Trao đổi với tôi

http://www.buidao.com

12/19/16

[System Info], [MASM] Processless Applications - Remotethreads on Microsoft* Windows 2000, XP and 2003

Processless Applications - Remotethreads on
Microsoft* Windows 2000, XP and 2003
Thomas Kruse
Universitas Virtualis
Tranz by Benina (7/2008)


Tôi (benina) có coding một chương trình theo dõi việc gắm đĩa USB vào máy tính dựa trên bài viết này: Source usb_dog

Abstract

The shown technique is able to run on all Windows operation systems. In order to avoid virus creation on it’s best, this technique is shown for W2K/XP/2K3 only. NT4 systems doesnt know several of the used API’s, also it is possible to rewrite them. Non NT-based systems need other techniques to detect the correct process to inject the code.
This essay was created while searching for new software protections to make ”crackers life” even harder. Based on ”WatchDog theory” - another way to protect applications - the idea is to create threads outside the main application which are able to continue workflow also if the main application terminates. This essay will show up a way to display a messagebox from process ”Explorer.Exe”, which is available on all OS.
The created application is ”processless” in that way that the main application becomes terminated after creating the external thread.
The shown source code is in Microsoft Assembler style (MASM [3]).

Keywords: Microsoft Operating Systems, Software Protection, Assembly Programming, System Internals
The author Thomas Kruse has his main research focus on operating-system independent code optimising and software-protection development. He is Associate
Editor of Assembly-Programming-Journal and CodeBreakers-Journal.

I. Introduction

Trước khi chúng ta bắt đầu cài đặt ứng dụng của chúng ta , chúng ta cần vài kiến thức căn bản về processes và threads. Và như thế nào chúng tương tác với nhau. Bất cứ khi nào một ứng dụng thưc thi, nó cài đặt một process và thread chính của nó. Ứng dụng này có thể run cho đến khi:

• process được kết thúc (terminated)
• Số các threads of process của nó là NULL

Trong 2 điều kiện trên, điều kiện sau cùng chỉ ra cho ta một điểm mới: chúng ta có thể cài đặt hơn một thread cho một ứng dụng. Và kỹ thuật này được sử dụng cho các ứng dụng “giám sát bảo vệ” WatchDog.
Cho ví dụ, các system processes có nhiều threads đang chạy bên trong một process. Cũng vậy nếu một thread kết thúc terminates, thì process vẫn đang chạy. Và thêm điều này nữa, các threads mới có thể được cài đặt khi process đang running.
Nhưng có một điểm khác mà chúng ta cần kiểm tra: Khi debugging[2] một ứng dụng mà nó cài đặt hơn một thread (e.g. WatchDog protected app), nó có thể trace thread assembler code! Lý do ở đây thật là đơn giản:

• mỗi created thread được cấp phát memory bên trong vùng nhớ calling process memory area
• calling process memory area có khả năng reading

Memory mới có thể được cho bởi sử dụng hàm VirtualAlloc[1] API từ KERNEL32.DLL, mà hàm này có trên tất cả các OS. Nếu chúng ta muốn làm cho việc crack của các crackers khó hơn, thì chúng ta phải “rời” calling process memory area của chúng ta và cài đặt các threads bên ngòai ứng dụng của chúng ta.
Điều này có thể được thực hiện bởi sử dụng VirtualAllocEx[1].

II. General Idea

Ý tưởng cài đặt một ứng dụng được ”better protected” dựa trên “lỗi hỏng” đã chỉ ra trước đó. Chúng ta cần vài name conventions để hiểu tiến trình công việc:

carrier application: ứng dụng được thực thi. Nó cài đặt remote thread, inject code và terminates chính nó.
target application: ứng dụng mà nó nhận một new thread. Nó tồn tại sẳn khi carrier starts.

Chúng ta phải có một cái nhìn tổng quát về tiến trình công việc :

 carrier application                      target application
-------------------                       ------------------
- đang running
- bắt đầu thực thi
- tìm ứng dụng target
- ban cấp truy xuất đến ứng dụng target   - return access rights
- cấp phát memory trong target area       - return memory address
- inject code trong target memory area
- create remote thread trong target app   - gia tăng threads
- resume remote thread                    - new thread executes*
- terminate kết thúc chính nó**

Khi thread thực thi, nó chứa code, nhưng ko biết chính xác addr của các hàm API function calls. Có 2 cách để tránh điều này:

• giải quyết function calls qua carrier application
• giải quyết function calls qua target application

** Carrier application và các threads của nó bị khử. Nhưng created thread dựa trên ứng dụng target. Trong cas đó, thread này ko bị ảnh hưởng khi kết thúc carrier (mang,vác,chở) application!

III. Resolving API calls

Chủ đề này nghe qua tưởng khó, nhưng ko phải vậy. Một lần nữa chúng ta cần biết một kỹ thuật mà nó dựa trên nền hệ điều hành NT sử dụng để ”sort” dynamic link librarys trong memory.
Khi một remote thread thực thi, nó có thể truy xuất Process Environment Block (PEB) of process chính của nó. Trong trường hợp của chúng ta, nó có thể truy xuất PEB of our target application. Và mỗi PEB chứa thành phần của nó :

• InLoadOrderModuleList
• InMemoryOrderModuleList
• InInitializationOrderModuleList

Hai thành phần đầu tiên chứa tên ứng dụng của nó như first module, theo sau bởi DLL mà nó cần (and accessable ones). Cuối cùng điều quan tâm hơn. NT-based operation systems cần  NTDLL.DLL. DLL này là thành phần đầu tiên trong InInitializationOrderModuleList. Và module kế tiếp là: KERNEL32.DLL! Một cách chính xác chúng ta cần cái gì, bởi vì  library này chứa các hàm API functions có 2 hàm calls quan trọng: GetProcAddress[1] và LoadLibraryA[1]. Bằng cách giải quyết hai hàm gọi này chúng ta có thể import những gì mà chúng ta muốn. Nhưng ở đâu chứa các addr cần giải quyết?.

IV. Preparing memory areas

Chúng ta ở đâu có thể giải quyết các API calls cần thiết, nhưng chúng ta cần tìm một cách để chứa chúng. Có một cách là chứa chúng trên stack, cách khác để cài đặt một vùng nhớ riêng biệt cho data of thread chúng ta. Nhưng bởi sử dụng kỹ thuật thứ hai, chúng ta cần nói cho remote thread của chúng ta ở đâu để tìm data. Trước tiên chúng ta hảy nhìn vào hàm API function CreateRemoteThread[1]:

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

lpStartAddress: Trỏ đến app - defined function of type
LPTHREAD START ROUTINE được thực thi bởi thread và miêu tả starting address of thread trong remote process. Hàm phải hiện có trong remote process.
lpParameter: Chỉ định một giá trị 32-bit đơn truyền cho hàm thread function.

Nếu thread chúng ta thực thi, lpParameter là vẫn trên stack. Có thể qua phần code sau của injected threadcode của chúng ta:

; -- access the given parameter from Create(Remote)Thread
mov eax,dword ptr [ebp+0Ch]   ; get parameter
push eax                      ; store this value on stack

Chúng ta có thể cắt phần data và code ra trong hai vùng nhớ. Chúng ta đơn giản sử dụng cập API VirtualAllocEx :

• cài đặt vùng data trong target application
• lưu chứa memory address, chúng ta cần nó như lpParameter cho CreateRemoteThread
• inject data vào trong data area
• cài đặt code area trong target application
• lưu chứa memory address, chúng ta cần nó như là lpStartAddress
• inject code vào trong code area
• cài đặt remote thread trong trạng thái suspended mode

Bởi sử dụng hai vùng nhớ - cho code và data – chúng ta ở đâu cài đặt address independent code trong suốt quá trịnh phát triển!
Chúng ta rời carrier application process memory range và có – trong suốt quá trình khởi động của thread trong target – không có ý nghĩa về address ranges trong process app target của chúng ta. Và bằng cách sử dụng kỹ thuật này, code là target independent (độc lập), regardless (bất chấp) which application is affected.

V. Thread Data Part

Phần data được cài đặt qua VirtualAllocEx và WriteProcessMemory[1] trong process memory range of our target application. Bởi sử dụng VirtualAllocEx, chúng ta có thể ở đâu đặt tên cho target process trong first parameter of this API call.
Khi debugging carrier application, vùng nhớ này ko dễ trace, bởi vì nó dựa trên process khác và debugger ko biết vùng nhớ này. Cho ví dụ, chúng ta sử dụng phần data sau đây:

.data
_sizeofInject dd offset _inject_end - offset _inject
_sizeofData dd offset _ENDOFDATA - offset _dwThreadID
_dwThreadID dd 0
_dwThreadHandle dd 0
_lpCode dd 0
_lpData dd 0
_lpRsrc dd 0
_dwExitCode dd 0
_Kernel32 dd 0
_User32 dd 0
_GetProcAddress dd 0
_GetExitCodeThread dd 0
_ExitThread dd 0
_szMessageBoxA db "MessageBoxA",0
_szExitThread db "ExitThread",0
_szExitCodeThread db "GetExitCodeThread",0
_szText db "Hello from Thread!",0
_szCaption db "Thread:",0
_ENDOFDATA dd 0

Hai cái đầu tiên và thành phần sau cùng KHÔNG thực hiện. Chúng chỉ mô tả size để inject. Phần data để inject bắt đầu với dwThreadID và kết thúc với szCaption như là thành phần sau cùng. Phần data chứa nhiều placeholders (chổ chứa) cho các hàm API function calls cần giải quyết và strings (for MessageBox call and additional API functions to resolve). Có nhiều placeholders mà nó ko được sử dụng trong ví dụ của chúng ta, nhưng phần data này cũng được prepared cho các further projects.

VI. Thread Code Part

Chúng ta có thể tưởng tượng tiến trình làm việc của thread code bằng cách phân tích phần data đã cho:

• resolve GetProcAddress API from KERNEL32.DLL
• resolve GetExitCodeThread API from KERNEL32.DLL
• resolve ExitThread API from KERNEL32.DLL
• resolve MessageBoxA API from User32.DLL
• prepare MessageBoxA parameters on stack
• show the messagebox
• prepare GetExitCodeThread parameter on stack ( dwThreadHandle)
• get the exitcode
• exit the thread (thread terminates, process continue!)

VII. Carrier Application

Thread code được sửa chửa là vô dụng với carrier application. Chúng ta hảy xem nó làm gì:

• check the operating system (do not execute on 9X)
• resolve USER32.DLL base address for our thread (*)
• create a snapshot of running processes
• find the target application (explorer.exe)
• if not found, terminate with error message
• store target processID
• free snapshot
• grant access to target application via OpenProcess[1]
• store the target process handle
• create two memory areas via VirtualAllocEx
• create a remote thread in suspended mode
• inject the parts via WriteProcessMemory
• resume the remote thread to become executed via ResumeThread[1]
• close the target process handle
• terminate the carrier via ExitProcess[1]

* Người đọc nào mà hiểu được thread code được chỉ ra có thể thấy trong phần Section IX, mà base address of USER32.DLL ko được giải quyết bởi thread code của chúng ta! Bạn cần thay đổi nó để run được. Hiểu tiến trình công việc này chúng ta có thể create carrier application phần tổng quát. Chứa những dữ liệu cần thiết và các hàm được included và được chỉ ra trong phần Section X. Phần code được chỉ ra trong phần Section XI thực hiện chính xác những gì chúng ta muốn làm. Và code này có thể được sử dụng như một template tổng quát để cài đặt các ứng dụng như vậy. Nếu bạn muốn sử dụng resources later on, đơn giản là cài đặt memory region khác bằng cách sử dụng VirtualAllocEx và chứa memory address bên trong thread data ( lpRsrc).

VIII. Conclusions

Kỷ thuật chỉ ra sử dụng một target mà nó thực thi injected code. Nhưng nó cũng có thể :

• inject code trong KHÁC target app và truyền thông giữa các targets này sử dụng kỷ thuật từ carrier application để truy xuất một process
• DUYỆT qua từ một target application đến target khác, destroying itself sau khi inject các phần cần thiê1t cho new target bởi sử dụng polymorphic code
• WALK from one target application to another, leaving itself intact (nguyên vẹn) and create ”splitted” applications which communicate together
• INTERACT (tương tác) with still running carrier application as kind of ”external” WatchDog from target application(s)
• KẾT HỢP tất cả các ví dụ chỉ ra ở trên.

Với sự kết hợp các kỹ thuật bảo vệ software đang được sử dụng thì có thể tránh ”readable” thread data. Bằng cách sử dụng polymorphic code và crypto algorythms thì kỹ thuật reversing approach trở nên gần như ko thể.

References

[1] Microsoft Corporation, Microsoft Developer Network, http://msdm.microsoft.com
[2] Yuschuk, O., Olly Debugger, http://home.t-online.de/home/ollydbg
[3] Hutchenson, S., MASM V8, http://www.masmforum.com

IX. SourceCode Thread.inc

Chúng ta sử dụng nhìêu cấu trúc cho việc giải quyết export address table từ KERNEL32.DLL. Các cấu trúc này được thay đổi vào trong correct code trong suốt quá trình biên dịch và trong cas đó, chúng ta ko cần làm life harder than it is.

;-- thread.inc (data and code)
.data
_sizeofInject dd offset _inject_end - offset _inject
_sizeofData dd offset _ENDOFDATA - offset _dwThreadID
_dwThreadID dd 0
_dwThreadHandle dd 0
_lpCode dd 0
_lpData dd 0
_lpRsrc dd 0
_dwExitCode dd 0
_Kernel32 dd 0
_User32 dd 0
_GetProcAddress dd 0
_GetExitCodeThread dd 0
_ExitThread dd 0
_szMessageBoxA db "MessageBoxA",0
_szExitThread db "ExitThread",0
_szExitCodeThread db "GetExitCodeThread",0
_szText db "Hello from Thread!",0
_szCaption db "Thread:",0
_ENDOFDATA dd 0

.code
_inject: ; start offet of code to inject
; -- access the given parameter from Create(Remote)Thread
mov eax,dword ptr [ebp+0Ch] ; get parameter
push eax ; store this value on stack
; resolve the kernel32 base via PEB (NT-based!)
mov eax,7FFDF00Ch ; pointer to PEB_LDR
mov eax,[eax]
add eax,1Ch ; pointer to InInitOrder_First
mov eax,[eax] ; NTDLL.DLL InitOrder_Next
mov eax,[eax] ; K32.dll InitOrder_Next
add eax,8h ; pointer to base address
mov eax,[eax] ; get the base addess
mov ebx,[esp] ; get base data
add ebx,18h ; kernel32 address place
mov [ebx],eax ; store kernel32 base
mov ebx,eax ; to ebx
; -- GetProcAddress in another style
push edi ; store edi
push esi ; store esi
push ebp ; store ebp
push edx ; store edx
mov eax,ebx ; kernel32
mov ebp,eax ; ebp becomes base address
assume eax: ptr IMAGE_DOS_HEADER
add eax,[eax].e_lfanew
assume eax:ptr IMAGE_NT_HEADERS
mov edi,[eax].OptionalHeader.DataDirectory[0].VirtualAddress
add edi,ebp ; add the base value
assume edi:ptr IMAGE_EXPORT_DIRECTORY
mov esi,[edi].AddressOfNames
add esi,ebp ; add the base value
xor edx,edx ; clear edx
assume eax:nothing ; avoid errors
_loopstart:
mov eax,[esi]
add eax,ebp ; add the base value
cmp word ptr [eax+0Ch],"ss"
jne _nextloop
cmp dword ptr [eax+04h], "Acor"
jne _nextloop
cmp dword ptr [eax+00h], "PteG"
jne _nextloop
cmp dword ptr [eax+08h], "erdd"
jne _nextloop
mov eax,[edi].AddressOfNameOrdinals
add eax,ebp ; add the base value
movzx ebx,word ptr [edx*2+eax]; get ordinal
mov eax,[edi].AddressOfFunctions
add eax,ebp ; add the base value
mov ebx,[ebx*4+eax]
add ebx,ebp ; ebx holds value of function
jmp _found ; quit searching
_nextloop:
add esi,4
inc edx
cmp edx,[edi].NumberOfNames
jne _loopstart
_found:
assume edi:nothing ; avoiding errors
pop edx ; restore edx
pop ebp ; restore ebp
pop esi ; restore esi
pop edi ; restore edi
mov eax,[esp] ; get base data
add eax,20h ; GetProcAddress placeholder
mov [eax],ebx ; store API address
mov eax,[esp] ; init eax with base data
; -- resolve all needed API’s for this thread
mov ebx,[eax+18h] ; base address kernel32
mov edx,[eax+20h] ; function GetProcAddess
push eax ; store base data on stack
add dword ptr [esp],38h ; pointer to ExitThread String
push ebx ; the kernel32 base
call edx ; call GetProcAddress
xchg eax,ebx
pop eax ; restore data base
add eax,28h ; placeholder of ExitThread
mov dword ptr [eax],ebx ; store the resolved address
sub eax,28h ; restore the original data base
push eax ; store it on stack
mov ebx,[eax+18h] ; base address kernel32
mov edx,[eax+20h] ; function GetProcAddess
push eax ; store base data on stack
add dword ptr [esp],43h ; GetExitCodeThread String
push ebx ; the kernel32 base
call edx ; call GetProcAddress
xchg eax,ebx
pop eax ; restore data base
add eax,24h ; placeholder of ExitThread
mov dword ptr [eax],ebx ; store the resolved address
sub eax,24h ; restore the original data base
push eax ; store it on stack
mov ebx,[eax+1ch] ; base address user32
mov edx,[eax+20h] ; function GetProcAddess
push eax ; store base data on stack
add dword ptr [esp],2ch ; pointer to MessageBoxA String
push ebx ; the user32 base
call edx ; call GetProcAddress
xchg eax,ebx
pop eax ; restore data base
add eax,1ch ; placeholder MessageBoxA (User32)
mov dword ptr [eax],ebx ; store the resolved address
sub eax,1ch ; restore the original data base
; -- start visible "action" inside the created thread
push eax ; store data base
mov ebx,eax ; ebx becomes data base
mov edx,[eax+1Ch] ; function MessageBoxA
add ebx,68h ; pointer to caption
push 0 ; MB_OK
push ebx ; caption
sub ebx,13h ; pointer to text
push ebx ; text
push 0 ; handle
call edx ; MessageBoxA
pop eax ; restore data base/correct stack!
; -- final cleanup and destroying the created thread
push eax ; store data base
mov ebx,eax
add ebx,14h ; pointer to ExitCode
push ebx ; address to recieve the ExitCode
sub ebx,10h ; pointer to ThreadHandle
push dword ptr[ebx]
add ebx,20h ; pointer to GetExitCodeThread
call dword ptr[ebx] ; GetExitCodeThread
xchg eax,ebx
pop eax ; restore data base
mov ebx,eax
add ebx,14h ; pointer to ExitCode
push dword ptr[ebx] ; ExitCode for this thread
add ebx,14h ; pointer to ExitThread
call dword ptr[ebx] ; ExitThread

_inject_end: ; end offset of code to inject

X. SourceCode inject.inc

; -- inject.inc
.386 ; minimum required CPU
.model flat,stdcall ; win 32 app
option casemap:none ; case sensitive
include windows.inc ; stuff we need
incboth macro incl ; macro for less typing
include incl.inc
includelib incl.lib
endm
incboth kernel32 ; for kernel32 API’s
incboth user32 ; for user32 API’s
include thread.inc ; thread related data
.data
szErrorText db "Process EXPLORER.EXE not found",0
szErrorOS db "You need an NT-based OS!",0
szErrorCaption db "Error occured:",0
szExplorer db "explorer.EXE",0
szUser32 db "user32.dll",0
.data?
hInstance dd ? ; application instance handle
_myProcessID dd ? ; resolved process ID
_myProcessHandle dd ? ; resolved process handle
_BytesWritten dd ? ; for WriteProcessMemory
hSnapShot dd ? ; snapshot handle
myProcess PROCESSENTRY32 <>

XI. SourceCode inject.asm

; -- inject.asm
include inject.inc ; additional data and includes
.code
start: ; application entry point
; -- carrier application
invoke GetModuleHandle,NULL ; get application module handle
mov hInstance,eax ; and store it
assume fs:nothing ; needed for MASM
mov eax,fs:[18h] ; get TEB self pointer
mov ebx,fs:[30h] ; get PEB pointer
.if eax!=7FFDE000h || ebx!=7FFDF000h ; NT check
invoke MessageBox,NULL,addr szErrorOS,addr szErrorCaption,MB_OK
jmp _exitApp
.endif
invoke LoadLibrary,addr szUser32
mov _User32,eax
invoke FreeLibrary,_User32
invoke CreateToolhelp32Snapshot, TH32CS_SNAPALL, 0
mov hSnapShot, eax ; store the snapshot handle
mov myProcess.dwSize, sizeof myProcess
invoke Process32First,hSnapShot, ADDR myProcess
.while eax
lea eax,myProcess.szExeFile
lea esi,szExplorer ; the name to search
invoke lstrcmpi,eax,esi ; compare both strings
.if eax==0 ; if the same...
push myProcess.th32ProcessID
pop _myProcessID ; get the process ID and
jmp @F ; leave search routine
.else
invoke Process32Next,hSnapShot, ADDR myProcess
.endif
@@:
.endw
invoke CloseHandle,hSnapShot ; close snapshot handle
mov eax,_myProcessID ; get the process ID
test eax,eax ; is there an ID
jne _goOn ; if so, continue
invoke MessageBox,NULL,addr szErrorText,\
addr szErrorCaption, MB_OK
jmp _exitApp ; leave application
_goOn:
invoke OpenProcess,PROCESS_ALL_ACCESS,NULL,_myProcessID
mov _myProcessHandle,eax ; store the process handle
; -- create two mem parts: one for code, one for data
invoke VirtualAllocEx,_myProcessHandle,NULL,1000h,MEM_COMMIT,\
PAGE_EXECUTE_READWRITE
mov _lpCode,eax ; store code base in thread data
invoke VirtualAllocEx,_myProcessHandle,NULL,100h,MEM_COMMIT,\
PAGE_EXECUTE_READWRITE
mov _lpData,eax ; store data base in thread data
; -- create the thread in suspended mode!
; -> the 4th (here: _lpData) parameter is important; it
; guarantees an memory independent inject code...
invoke CreateRemoteThread,_myProcessHandle,NULL,NULL,_lpCode,\
_lpData, CREATE_SUSPENDED,\
addr _dwThreadID
mov _dwThreadHandle,eax ; store handle in thread data
; -- write code section
invoke WriteProcessMemory,_myProcessHandle,_lpCode,\
addr _inject, _sizeofInject,\
addr _BytesWritten
; -- write data section
invoke WriteProcessMemory,_myProcessHandle,_lpData,\
addr _dwThreadID, _sizeofData,\
addr _BytesWritten
invoke ResumeThread,_dwThreadHandle ; start the thread
invoke CloseHandle,_myProcessHandle ; close process handle
_exitApp:
invoke ExitProcess,NULL ; exit application

end start ; end of application code section




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;          The End                ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

XII. Phần của người dịch:

Phần code sau đây mà benina chỉnh sửa để app running

1.File inject.Asm

; -- inject.asm

include inject.inc                              ; additional data and includes

.code
start:                                          ; application entry point

; ---------------------------------- carrier application --------------------------------
invoke GetModuleHandle,NULL                     ; get application module handle
mov hInstance,eax                               ; and store it

assume fs:nothing                               ; needed for MASM
mov eax,fs:[18h]                          ; get TEB self pointer
mov ebx,fs:[30h]                          ; get PEB pointer

comment /* phan nay nen bo
.if eax!=7FFDE000h || ebx!=7FFDF000h                  ; NT check
invoke MessageBox,NULL,addr szErrorOS,addr szErrorCaption,MB_OK
jmp _exitApp
.endif
*/

invoke LoadLibrary,addr szUser32
mov _User32,eax
invoke FreeLibrary,_User32

invoke CreateToolhelp32Snapshot, TH32CS_SNAPALL, 0
mov hSnapShot, eax                              ; store the snapshot handle

mov myProcess.dwSize, sizeof myProcess
invoke Process32First,hSnapShot, ADDR myProcess

.while eax
      lea eax,myProcess.szExeFile
      lea esi,szExplorer                        ; the name to search
      invoke lstrcmpi,eax,esi                   ; compare both strings
      .if eax==0                          ; if the same...
      push myProcess.th32ProcessID
      pop _myProcessID                    ; get the process ID and
      jmp @F                                    ; leave search routine
      .else
      invoke Process32Next,hSnapShot, ADDR myProcess
      .endif
      @@:
.endw

invoke CloseHandle,hSnapShot                    ; close snapshot handle
mov eax,_myProcessID                            ; get the process ID

test eax,eax                                    ; is there an ID
jne _goOn                                 ; if so, continue

invoke MessageBox,NULL,addr szErrorText,\
              addr szErrorCaption, MB_OK
jmp _exitApp                                    ; leave application

_goOn:

invoke OpenProcess,PROCESS_ALL_ACCESS,NULL,_myProcessID
mov _myProcessHandle,eax                        ; store the process handle

; -------------------- create two mem parts: one for code, one for data ---------------

invoke VirtualAllocEx,_myProcessHandle,NULL,1000h,MEM_COMMIT,\
                  PAGE_EXECUTE_READWRITE
mov _lpCode,eax                           ; store code base in thread data
invoke VirtualAllocEx,_myProcessHandle,NULL,100h,MEM_COMMIT,\
                  PAGE_EXECUTE_READWRITE
mov _lpData,eax                           ; store data base in thread data

; ---------------------------- create the thread in suspended mode! -------------------
; -> the 4th (here: _lpData) parameter is important; it
; guarantees an memory independent inject code...
;--------------------------------------------------------------------------------------

invoke CreateRemoteThread,_myProcessHandle,NULL,NULL,_lpCode,\
                                    _lpData, CREATE_SUSPENDED,\
                                    addr _dwThreadID
mov _dwThreadHandle,eax ; store handle in thread data

; ------------------------- write code section ----------------------------------------
invoke WriteProcessMemory,_myProcessHandle,_lpCode,\
                                    addr _inject, _sizeofInject,\
                                    addr _BytesWritten
; -------------------------- write data section ---------------------------------------
invoke WriteProcessMemory,_myProcessHandle,_lpData,\
                                    addr _dwThreadID, _sizeofData,\
                                    addr _BytesWritten
invoke ResumeThread,_dwThreadHandle                   ; start the thread
invoke CloseHandle,_myProcessHandle                   ; close process handle

_exitApp:
invoke ExitProcess,NULL                         ; exit application

end start                                 ; end of application code section



2.File inject.inc

; -- inject.inc
.386                                ; minimum required CPU
.model flat,stdcall                       ; win 32 app
option casemap:none                       ; case sensitive
include \masm32\include\windows.inc             ; stuff we need

incboth macro incl                        ; macro for less typing
include \masm32\include\incl.inc
includelib \masm32\lib\incl.lib
endm

incboth kernel32                    ; for kernel32 API’s
incboth user32                            ; for user32 API’s
include thread.inc                        ; thread related data

.data
szErrorText db "Process EXPLORER.EXE not found",0
szErrorOS db "You need an NT-based OS!",0
szErrorCaption db "Error occured:",0
szExplorer db "explorer.EXE",0
szUser32 db "user32.dll",0

.data?
hInstance dd ? ; application instance handle
_myProcessID dd ? ; resolved process ID
_myProcessHandle dd ? ; resolved process handle
_BytesWritten dd ? ; for WriteProcessMemory
hSnapShot dd ? ; snapshot handle
myProcess PROCESSENTRY32 <>


3.File thread.inc

;-- thread.inc (data and code)
.data

_sizeofInject dd offset _inject_end - offset _inject
_sizeofData dd offset _ENDOFDATA - offset _dwThreadID
_dwThreadID dd 0                    ;00h
_dwThreadHandle dd 0                      ;04h
_lpCode dd 0                              ;08h
_lpData dd 0                              ;0ch
_lpRsrc dd 0                              ;10h
_dwExitCode dd 0                          ;14h
_Kernel32 dd 0                            ;18h
_User32 dd 0                              ;1ch
_GetProcAddress dd 0                      ;20h
_GetExitCodeThread dd 0                   ;24h
_ExitThread dd 0                          ;28h
_szMessageBoxA db "MessageBoxA",0         ;2ch
_szExitThread db "ExitThread",0                 ;38h
_szExitCodeThread db "GetExitCodeThread",0      ;43h
_szText db "Hello from Thread!",0               ;55h
_szCaption db "Thread:",0                       ;68h
_ENDOFDATA dd 0


.code

_inject:                            ; start offet of code to inject

; -- access the given parameter from Create(Remote)Thread
mov eax,dword ptr [ebp+0Ch]         ; get parameter
push eax                            ; store this value on stack

assume fs:nothing

      @@find_kernel32:
            push esi
            xor eax, eax
            mov eax, fs:[eax+030h]
            test eax, eax
            js @@find_kernel32_9x
           
      @@find_kernel32_nt:
            mov eax, [eax + 0Ch]
            mov esi, [eax + 01ch]
            lodsd
            mov eax, [eax + 08h]
            jmp @@find_kernel32_finished
           
      @@find_kernel32_9x:
            mov eax, [eax + 034h]
            lea eax, [eax + 7ch]
            mov eax, [eax + 03ch]
           
      @@find_kernel32_finished:
            pop esi
;--------return eax is base kernel32---------------------------        

mov ebx,[esp]                       ; get base data
add ebx,18h                         ; kernel32 address place
mov [ebx],eax                       ; store kernel32 base

mov ebx,eax                         ; to ebx

; -- GetProcAddress in another style
push edi                            ; store edi
push esi                            ; store esi
push ebp                            ; store ebp
push edx                            ; store edx

mov eax,ebx                         ; kernel32
mov ebp,eax                         ; ebp becomes base address

assume eax: ptr IMAGE_DOS_HEADER
add eax,[eax].e_lfanew
assume eax:ptr IMAGE_NT_HEADERS
mov edi,[eax].OptionalHeader.DataDirectory[0].VirtualAddress
add edi,ebp                         ; add the base value
assume edi:ptr IMAGE_EXPORT_DIRECTORY
mov esi,[edi].AddressOfNames
add esi,ebp                         ; add the base value
xor edx,edx                         ; clear edx
assume eax:nothing                  ; avoid errors

_loopstart:
mov eax,[esi]
add eax,ebp                         ; add the base value
cmp word ptr [eax+0Ch],"ss"
jne _nextloop
cmp dword ptr [eax+04h], "Acor"
jne _nextloop
cmp dword ptr [eax+00h], "PteG"
jne _nextloop
cmp dword ptr [eax+08h], "erdd"
jne _nextloop
mov eax,[edi].AddressOfNameOrdinals
add eax,ebp                         ; add the base value
movzx ebx,word ptr [edx*2+eax]      ; get ordinal
mov eax,[edi].AddressOfFunctions
add eax,ebp                         ; add the base value
mov ebx,[ebx*4+eax]
add ebx,ebp                         ; ebx holds value of function
jmp _found                          ; quit searching

_nextloop:
add esi,4
inc edx
cmp edx,[edi].NumberOfNames
jne _loopstart

_found:
assume edi:nothing                  ; avoiding errors
pop edx                             ; restore edx
pop ebp                             ; restore ebp
pop esi                             ; restore esi
pop edi                             ; restore edi
mov eax,[esp]                       ; get base data
add eax,20h                         ; GetProcAddress placeholder
mov [eax],ebx                       ; store API address
mov eax,[esp]                       ; init eax with base data

; -- resolve all needed API’s for this thread
mov ebx,[eax+18h]                   ; base address kernel32
mov edx,[eax+20h]                   ; function GetProcAddess
push eax                            ; store base data on stack
add dword ptr [esp],38h             ; pointer to ExitThread String
push ebx                            ; the kernel32 base
call edx                            ; call GetProcAddress
xchg eax,ebx
pop eax                             ; restore data base
add eax,28h                         ; placeholder of ExitThread
mov dword ptr [eax],ebx             ; store the resolved address
sub eax,28h                         ; restore the original data base
push eax                            ; store it on stack
mov ebx,[eax+18h]                   ; base address kernel32
mov edx,[eax+20h]                   ; function GetProcAddess
push eax                            ; store base data on stack
add dword ptr [esp],43h             ; GetExitCodeThread String
push ebx                            ; the kernel32 base
call edx                            ; call GetProcAddress
xchg eax,ebx
pop eax                             ; restore data base
add eax,24h                         ; placeholder of ExitThread
mov dword ptr [eax],ebx             ; store the resolved address
sub eax,24h                         ; restore the original data base
push eax                            ; store it on stack
mov ebx,[eax+1ch]                   ; base address user32
mov edx,[eax+20h]                   ; function GetProcAddess
push eax                            ; store base data on stack
add dword ptr [esp],2ch             ; pointer to MessageBoxA String
push ebx                            ; the user32 base
call edx                            ; call GetProcAddress
xchg eax,ebx
pop eax                             ; restore data base
add eax,1ch                         ; placeholder MessageBoxA (User32)
mov dword ptr [eax],ebx             ; store the resolved address
sub eax,1ch                         ; restore the original data base

; -- start visible "action" inside the created thread
push eax                            ; store data base
mov ebx,eax                         ; ebx becomes data base
mov edx,[eax+1Ch]                   ; function MessageBoxA
add ebx,68h                         ; pointer to caption
push 0                              ; MB_OK
push ebx                            ; caption
sub ebx,13h                         ; pointer to text
push ebx                            ; text
push 0                              ; handle
call edx                            ; MessageBoxA
pop eax                             ; restore data base/correct stack!

; -- final cleanup and destroying the created thread
push eax                            ; store data base
mov ebx,eax
add ebx,14h                         ; pointer to ExitCode
push ebx                            ; address to recieve the ExitCode
sub ebx,10h                         ; pointer to ThreadHandle
push dword ptr[ebx]
add ebx,20h                         ; pointer to GetExitCodeThread
call dword ptr[ebx]                 ; GetExitCodeThread
xchg eax,ebx
pop eax                             ; restore data base
mov ebx,eax
add ebx,14h                         ; pointer to ExitCode
push dword ptr[ebx]                 ; ExitCode for this thread
add ebx,14h                         ; pointer to ExitThread
call dword ptr[ebx]                 ; ExitThread

_inject_end:                        ; end offset of code to inject

XIII. Phần code tương tự:

1.File InjectProcess.Asm

.386
.model flat, stdcall
option casemap:none


include kernel32.inc
include user32.inc
include WINDOWS.INC

includelib kernel32.lib
includelib user32.lib

;############################################################################################

include InjectProcess.Inc

;############################################################################################

.data
hSnapShot         dd 0
PrEntry                 PROCESSENTRY32 <>
BufferExe         db "explorer.exe"
pMap              dd 0
nbwr              dd 0
ThID              dd 0
hProcess          dd 0

;############################################################################################

.code
start:
;scan tout les process lancés
      invoke CreateToolhelp32Snapshot,TH32CS_SNAPPROCESS,NULL
      mov hSnapShot,eax

      mov PrEntry.dwSize,SIZEOF PROCESSENTRY32
      invoke Process32First,eax,addr PrEntry

      .while eax!=FALSE
            invoke lstrcmpi,addr PrEntry.szExeFile,addr BufferExe ;regarde s'il s'agit d'explorer.exe
            test eax,eax
            je InjectIT
            invoke Process32Next,hSnapShot,addr PrEntry ;si ce n'est pas explorer.exe alors on continue de scanner
      .endw

      jmp ErrorCannotBeFounded

      dwSize                  equ (offset EndCode - offset CodeToBeInj) ;taille du code à injecter
     
      InjectIT:
            invoke OpenProcess,PROCESS_ALL_ACCESS,FALSE,PrEntry.th32ProcessID
            test eax,eax
            je ErrorPr
            mov hProcess,eax

            invoke VirtualAllocEx,hProcess,NULL,dwSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE ;alloue de la mémoire avec tous les accès dans explorer.exe
            test eax,eax
            je ErrorMem
            mov pMap,eax

            invoke WriteProcessMemory,hProcess,pMap,addr CodeToBeInj,dwSize,addr nbwr ;recopie le code dans explorer.exe
            test eax,eax
            je ErrorWr

            invoke CreateRemoteThread,hProcess,NULL,NULL,pMap,NULL,NULL,addr ThID ; lance notre code :D
            test eax,eax
            je ErrorThread

            invoke ExitProcess,0

;############################################################################################
;traitement des erreurs
ErrorThread:
      push MB_OK
      pushstr "Error"
      pushstr "Unable to launch remote thread"
      jmp WarnAndStop

ErrorCannotBeFounded:
      push MB_OK
      pushstr "Error"
      pushstr "Cannot opening process"
      jmp WarnAndStop

ErrorPr:
      push MB_OK
      pushstr "Error"
      pushstr "Cannot opening process"
      jmp WarnAndStop

ErrorMem:
      push MB_OK
      pushstr "Error"
      pushstr "Memory allocation failed"
      jmp WarnAndStop

ErrorWr:
      push MB_OK
      pushstr "Error"
      pushstr "Writing malicious code into process failed"
      jmp WarnAndStop

WarnAndStop:
      push NULL
      call MessageBox
      invoke ExitProcess,NULL

;############################################################################################

end start

2. File InjectProcess.Inc


;############################################################################################
GetKernelAddr                 PROTO
FindExport                    PROTO :DWORD,:DWORD
AdjustIfIsForwarded           PROTO :DWORD, :DWORD
GetKernelAddr                 PROTO

;############################################################################################

_LoadLibraryA     equ   0EC0E4E8Eh
_GetProcAddress   equ   07C0DFCAAh

;Macros
;#######################################################################
;récupère dans eax l'adresse de la fonction de la dll
GetProcAddr macro DllName,FuncName
      call GetKernelAddr
      invoke FindExport,eax,_LoadLibraryA
      pushstr DllName
      call eax    ; call LoadLibrary
           
      push eax
      call GetKernelAddr
      invoke FindExport,eax,_GetProcAddress
      pop ebx
      pushstr FuncName
      push ebx ;dll module
      call eax ;call GetProcAdress 
endm
pushstr macro a
      call @F
      db a,0
@@:
endm


.code
;####################################################################################### 
;####################################################################################### 
;code injecté dans explorer.exe

CodeToBeInj:
      GetProcAddr "user32.dll","MessageBoxA"
     
      push MB_OK
      pushstr "yuhu"
      pushstr "Thanh cong roi:)"
      push 0
      call eax

      ret
;####################################################################################### 
assume fs:nothing
GetKernelAddr proc
LOCAL KernelAddr:DWORD
      pushad
      @@find_kernel32:
            push esi
            xor eax, eax
            mov eax, fs:[eax+030h]
            test eax, eax
            js @@find_kernel32_9x
           
      @@find_kernel32_nt:
            mov eax, [eax + 0Ch]
            mov esi, [eax + 01ch]
            lodsd
            mov eax, [eax + 08h]
            jmp @@find_kernel32_finished
           
      @@find_kernel32_9x:
            mov eax, [eax + 034h]
            lea eax, [eax + 7ch]
            mov eax, [eax + 03ch]
           
      @@find_kernel32_finished:
            pop esi
            mov KernelAddr,eax
       popad
       mov eax,KernelAddr
    ret
GetKernelAddr endp
FindExport PROC KernelAddr:DWORD, HashFromAPI:DWORD

      @@find_function:
            pushad
            mov ebp, KernelAddr
            mov eax, [ebp + 03ch]         ; signature PE
            mov edx, [ebp + eax + 078h]   ; RVA export table
            add edx, ebp                        ; Export table address
            mov ecx, [edx + 018h]         ; ecx = nombre d'export de la DLL
            mov ebx, [edx + 020h]         ; ebx = RVA pointeur de nom
            add ebx, ebp                        ; ebx = pointeur de nom
           
      @@find_function_loop:
            jecxz @@find_function_finished
            dec ecx                                   ; dec nombre d'export
            mov esi, dword ptr [ebx + ecx * 4]  ; RVA string de la fonction
            add esi, ebp                        ; pointe sur la string de la fonction
           
      @@compute_hash:
            xor edi, edi
            xor eax, eax
            cld
           
      @@compute_hash_again:
            lodsb
            test al, al
            jz @@compute_hash_finished
            ror edi, 0dh
            add edi, eax
            jmp @@compute_hash_again
           
      @@compute_hash_finished:
     
      @@find_function_compare:
            mov eax, [esp+02ch];*
            cmp edi, eax ;HashFromAPI;
            jnz @@find_function_loop
           
            mov ebx, [edx + 024h]         ; ordinal table RVA
            add ebx, ebp                        ; pointeur vers ordinal table
            mov cx, word ptr[ebx + 2 * ecx]           ; cx = ordinal de la fonction
            mov ebx, [edx + 01ch]         ; ebx = export table address RVA
            add ebx, ebp                        ; pointeur vers table d'export
            mov eax, [ebx + 4 * ecx]      ; RVA de la fonction
           
            invoke AdjustIfIsForwarded,ebp,eax
           
            mov [esp+01Ch],eax                  ; retour sur eax (overwrite le EAX du pushad)
      @@find_function_finished:
            popad
     
      ret

FindExport endp
AdjustIfIsForwarded     proc  DllModule:DWORD,FuncRva:DWORD
LOCAL ForwarderRef[50]:BYTE

      mov esi, DllModule
      mov eax, [esi + 3Ch]          ;pe signature
      mov ebx, [esi + eax + 78h]    ;ebx == exportable rva
      mov ecx, [esi + eax + 7Ch]    ;ecx == sizeof EAT
;---------------------- teste si on se trouve dans l'export table
      cmp FuncRva,ebx
      jb NotForwarded
      add ebx,ecx
      cmp FuncRva,ebx
      ja NotForwarded
;----------------------
ForwardedRefPresent:
;si on se retrouve ici alors il s'agit d'un forwarder ref et FuncRva pointe vers une string sous la forme "NomDll"."NomFonction"

;---------------------- lstrlen maison    (ecx == taille)
      mov al,0
      mov ecx,-1
      mov edi,FuncRva
      add edi,esi
      push edi
      repne scasb
      not ecx
;----------------------
;---------------------- lstrcpy maison (edi == ptr buffer)
      pop esi                 ;esi==ptr ds l'export table (source)
      lea edi, ForwarderRef   ;edi == buffer (dest)
      push edi
      rep movsb
;----------------------
;---------------------- scan la pos du "." " (ecx == position relative au ptr buffer du ".")
      mov al,"."
      pop edi
      mov ecx,-1
      repne scasb
      not ecx
;----------------------
      mov byte ptr [edi - 1],0
     
      sub edi,ecx ;edi == ptr vers le nom de la dll
     
      push ecx;save ecx parce que loadlibrary le modifie
     
;charge la dll correspondante
      invoke GetKernelAddr
      invoke FindExport,eax,_LoadLibraryA
      push edi
      call eax
      mov ebx, eax ;ebx == module de la dll
     
      pop ecx;restaure ecx
     
      add edi,ecx ;edi == ptr vers le nom de la fonction
     
;---------------------- hash le nom de la fonction
      mov esi, edi
      @@compute_hash:
            xor edi, edi
            xor eax, eax
            cld
           
      @@compute_hash_again:
            lodsb
            test al, al
            jz @@compute_hash_finished
            ror edi, 0dh
            add edi, eax
            jmp @@compute_hash_again
      @@compute_hash_finished:
;----------------------
;---------------------- obtien le ptr de cette fonction    
      invoke FindExport,ebx,edi
      ret
;---------------------- si c pas une forwarded ref alors on ajoute a cette rva le module de la dll :)  
NotForwarded:
      mov eax,FuncRva
      add eax,DllModule
      ret

AdjustIfIsForwarded endp
EndCode:
;####################################################################################### 
;####################################################################################### 
;fin du code injecté