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é
|