Static code injection

From Merlin

(Difference between revisions)
 
Line 253: Line 253:
fac nimic altceva decât să mănânce cicluri CPU degeaba.
fac nimic altceva decât să mănânce cicluri CPU degeaba.
 +
Pentru discuţii ne vedem pe [[Talk:Static_code_injection|talk]]
[[Category:ASM]]
[[Category:ASM]]

Current revision as of 18:39, 14 January 2007

În acest articol voi încerca să descriu cât mai pe înţelesul tuturor tehnica numită injectarea codului binar în mod static - tehnică aplicată în software patching.

În primul rând vom crea o aplicaţie al cărei fişier binar urmează apoi să îl modificăm. Pentru aceasta voi folosi masm32 şi ollydbg. Ca fapt divers folosesc WinAsm ca editor.

Lectură plăcută!

O mică aplicaţie inofensivă, clasică, ce nu ne deranjează cu nimic:

-[BEGIN CODE 0x01]----------------------------------------------------------------
01	.386                                                                          
02	.model flat,stdcall                                                           
03	option casemap:none                                                           
04	                                                                              
05	include windows.inc                                                           
06	include kernel32.inc                                                          
07	includelib kernel32.lib                                                       
08	include user32.inc                                                            
09	includelib user32.lib                                                         
10	                                                                              
11	WinMain proto :DWORD,:DWORD,:DWORD,:DWORD                                     
12	                                                                              
13	.DATA                                                                         
14		ClassName		db "an annoying class",NULL                               
15		AppName			db "window title",NULL                                    
16	.DATA?                                                                        
17		hInstance		HINSTANCE ?                                               
18		CommandLine	LPSTR ?                                                       
19	.CODE                                                                         
20	start:                                                                        
21	invoke GetModuleHandle,NULL                                                   
22	mov hInstance,eax                                                             
23	invoke GetCommandLine                                                         
24	mov CommandLine,eax                                                           
25	; procedura main a programelor windows                                        
26	invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT                      
27	invoke ExitProcess,eax                                                        
28	; --------------------------------------------------------------------------- 
29	WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,cmdLine:LPSTR,CmdShow:DWORD  
30		LOCAL wc:WNDCLASSEX                                                         
31		LOCAL msg:MSG                                                               
32		LOCAL hwnd:HWND                                                             
33		                                                                            
34		mov wc.cbSize, SIZEOF WNDCLASSEX                                            
35		mov wc.style, CS_HREDRAW OR CS_VREDRAW                                      
36		;functia ce proceseaza mesajele GUI, apelata in bucla while de mai jos      
37		mov wc.lpfnWndProc, OFFSET WndProc                                          
38		mov wc.cbWndExtra,  NULL                                                    
39		mov wc.cbClsExtra,  NULL                                                    
40		push hInstance                                                              
41		pop wc.hInstance                                                            
42		mov wc.hbrBackground,COLOR_APPWORKSPACE + 1                                 
43		mov wc.lpszMenuName,NULL                                                    
44		mov wc.lpszClassName,OFFSET ClassName                                       
45		invoke LoadIcon,NULL,IDI_WINLOGO                                            
46		mov wc.hIcon,eax                                                            
47		mov wc.hIconSm,eax                                                          
48		invoke LoadCursor,NULL,IDC_ARROW                                            
49		mov wc.hCursor,eax                                                          
50		invoke RegisterClassEx,addr wc                                              
51		invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,WS_OVERLAPPEDWINDOW,\\
52					CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,\\  
53					NULL,hInst,NULL                                                 
54		mov hwnd,eax                                                                
55		invoke ShowWindow,hwnd,CmdShow                                              
56		invoke UpdateWindow,hwnd                                                    
57		.WHILE TRUE                                                                 
58			invoke GetMessage,ADDR msg,NULL,0,0                                     
59			.break .if(!eax)                                                        
60			invoke TranslateMessage,ADDR msg                                        
61			; apeleaza WndProc                                                        
62			invoke DispatchMessage,ADDR msg                                         
63		.ENDW                                                                       
64		mov eax,msg.wParam                                                          
65		ret                                                                         
66	WinMain endp                                                                  
67	; --------------------------------------------------------------------------- 
68	WndProc proc hWnd:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM                  
69		.IF uMsg == WM_DESTROY                                                    
70			invoke PostQuitMessage, NULL                                          
71		.ELSE                                                                     
72			invoke DefWindowProc,hWnd,uMsg,wParam,lParam                          
73			ret                                                                   
74		.ENDIF                                                                    
75		xor eax,eax                                                               
76		ret                                                                       
77	WndProc endp                                                                  
78	; --------------------------------------------------------------------------- 
79	end start                                                                     
-[ END CODE ]---------------------------------------------------------------------

Acum vom insera lucrurile pe care vom dori să le ştergem apoi direct din fişierul binar:

După linia 70:

-[BEGIN CODE 0x02]----------------------------------------------------------------
		.ELSEIF uMsg == WM_LBUTTONUP                                                
			invoke MessageBox,hWnd,addr ClassName,addr AppName,MB_OK                
-[ END CODE ]---------------------------------------------------------------------

Acest apel la MessageBox va deschide un nou box enervant la fiecare eliberare a butonului stâng al mouse-ului. După linia 56:

-[BEGIN CODE 0x03]----------------------------------------------------------------
		invoke MessageBox,hwnd,addr ClassName,addr AppName,MB_OK                    
-[ END CODE ]---------------------------------------------------------------------

Imediat după ce arătăm fereastra pe desktop vrem să mai apară un MessageBox enervant pe care să avem apoi plăcerea de a-l şterge direct din fişierul binar

După linia 24:

-[BEGIN CODE 0x04]----------------------------------------------------------------
invoke MessageBox,NULL,addr ClassName,addr AppName,MB_OK                      
-[ END CODE ]---------------------------------------------------------------------

Din nou, acelaşi mesaj enervant înainte de toate :-)

Acum să ne uităm mai atent la codul nostru. Linia

-[BEGIN CODE 0x05]----------------------------------------------------------------
02	.model flat,stdcall                                                           
-[ END CODE ]---------------------------------------------------------------------

ne spune că memoria este tratată linear (flat) şi că parametrii funcţiilor apelate sunt puse pe stack în ordine inversă (stdcall). Aşadar un apel de genul

-[BEGIN CODE 0x06]----------------------------------------------------------------
		invoke MessageBox,hwnd,addr ClassName,addr AppName,MB_OK                    
-[ END CODE ]---------------------------------------------------------------------

rezultă în realitate în codul:

-[BEGIN CODE 0x07]----------------------------------------------------------------
		push MB_OK
		push addr AppName
		push addr ClassName
		push hwnd
		call MessageBox
-[ END CODE ]---------------------------------------------------------------------

aşa cum vom vedea în timp ce vom face debugging JIT (Just In Time) cu ollydbg. Compilează şi testează programul să te asiguri că funcţionează mai întâi. Poţi adăuga instrucţiunea

-[BEGIN CODE 0x08]----------------------------------------------------------------
int 3
-[ END CODE ]---------------------------------------------------------------------

înaintea apelurilor MessageBox inserate anterior pentru a instrucţiona ollydbg (cu setările standard) să facă o mică pauză.Dacă vrei să testezi programul direct fără ollydbg va trebui să comentezi liniile cu interrupt 3 si să recompilezi. Acum deschide programul cu ollydbg şi apasă F9 pentru a-l rula. Se va opri automat după instrucţiunea "int 3" lucru care ne va da ocazia să analizăm fişierul binar rezultat în urma compilării. Codul binar din ollydbg fără interrupt 3:

-[BEGIN CODE 0x09]----------------------------------------------------------------
00401000 >/$ 6A 00          PUSH    0                                ; /pModule = NULL
00401002  |. E8 91010000    CALL    <JMP.&kernel32.GetModuleHandleA> ; \\GetModuleHandleA
00401007  |. A3 20304000    MOV     DWORD PTR DS:[403020],EAX
0040100C  |. E8 81010000    CALL    <JMP.&kernel32.GetCommandLineA>  ; [GetCommandLineA
00401011  |. A3 24304000    MOV     DWORD PTR DS:[403024],EAX
00401016     6A 00          PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
00401018     68 12304000    PUSH    demo_app.00403012                ; |Title = "window title"
0040101D     68 00304000    PUSH    demo_app.00403000                ; |Text = "an annoying class"
00401022     6A 00          PUSH    0                                ; |hOwner = NULL
00401024     E8 99010000    CALL    <JMP.&user32.MessageBoxA>        ; \\MessageBoxA
00401029  |. 6A 0A          PUSH    0A                               ; /Arg4 = 0000000A
0040102B  |. FF35 24304000  PUSH    DWORD PTR DS:[403024]            ; |Arg3 = 00000000
00401031  |. 6A 00          PUSH    0                                ; |Arg2 = 00000000
00401033  |. FF35 20304000  PUSH    DWORD PTR DS:[403020]            ; |Arg1 = 00000000
00401039  |. E8 06000000    CALL    demo_app.00401044                ; \\demo_app.00401044
0040103E  |. 50             PUSH    EAX                              ; /ExitCode
0040103F  \\. E8 48010000    CALL    <JMP.&kernel32.ExitProcess>      ; \\ExitProcess
-[ END CODE ]---------------------------------------------------------------------

Corespunde codului nostru asm:

-[BEGIN CODE 0x0A]----------------------------------------------------------------
20 start:                                                                        
21 invoke GetModuleHandle,NULL                                                   
22 mov hInstance,eax                                                             
23 invoke GetCommandLine                                                         
24 mov CommandLine,eax                                                           
25 invoke MessageBox,NULL,addr ClassName,addr AppName,MB_OK                      
26 ; procedura main a programelor windows                                        
27 invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT                      
28 invoke ExitProcess,eax                                                        
-[ END CODE ]---------------------------------------------------------------------

Ceea ce ne interesează este să observăm că într-adevăr parametrii sunt puşi pe stack în ordine inversă - vezi offseturile 00401016-00401022, deci MB_OK e o constantă cu valoarea 0, aşa cum poţi vedea şi în masm32/include/windows.inc.

Să zicem că vrem să nu apară acest MessageBox. Nu trebuie teoretic decât să nu apelăm MessageBoxA, dar cum? Putem înlocui acest apel cu instrucţiunea NOP (no operation) care spune procesorulului să nu facă nimic. Motivul pentru care nu putem şterge pur şi simplu apelul MessageBoxA din program este că prin asta am decala adresele instrucţiunilor şi datelor ce urmează, facând astfel programul inutil - deşi am putea teoretic repara manual toate referinţele la adrese, acest lucru ar fi o muncă prea costisitoare.

Click dreapta pe offseturile 00401016 - 00401024 -> "Binary" -> "Fill with NOPs". Deschide din nou meniul contextual al ferestrei cu segmentul .code şi alege "Copy to executable" -> "All modifications". Se va deschide o fereastra; inchide-o, acceptând să salvezi modificările, apoi lansează în execuţie această nouă versiune modificată a programului. Vei observa că nu mai apare niciun MessageBox înainte de afişarea ferestrei.

Următorul apel MessageBox arată cam aşa (ollydbg îţi arată pe coloana din dreapta unde se face un apel MessageBox şi lista de parametri cu un fel de paranteză):

-[BEGIN CODE 0x0B]----------------------------------------------------------------
004010FE     6A 00          PUSH    0                                ; /Style = MB_OK|MB_APPLMODAL
00401100     68 12304000    PUSH    demo_app.00403012                ; |Title = "window title"
00401105     68 00304000    PUSH    demo_app.00403000                ; |Text = "an annoying class"
0040110A     FF75 B0        PUSH    DWORD PTR SS:[EBP-50]            ; |hOwner
0040110D     E8 B0000000    CALL    <JMP.&user32.MessageBoxA>        ; \\MessageBoxA
-[ END CODE ]---------------------------------------------------------------------

Sper că ştii că hOwner e hwnd - pentru că aşa am compilat noi fişierul asm. Asta înseamnă că MessageBox-ul va avea ca fereastră părinte fereastra noastră principală a aplicaţiei. Nu trebuie decât să punem valoarea NULL în stack în loc de cea de la adresa SS:[EBP-50] pentru a face MessageBox-ul să poată fi dat la o parte şi aplicaţia noastră să poată primi focus. Aşadar marchează linia

-[BEGIN CODE 0x0C]----------------------------------------------------------------
0040110A     FF75 B0        PUSH    DWORD PTR SS:[EBP-50]            ; |hOwner
-[ END CODE ]---------------------------------------------------------------------

şi apasă CTRL+E pentru a o edita. Acolo vom introduce instrucţiunile

-[BEGIN CODE 0x0D]----------------------------------------------------------------
PUSH 0
NOP
-[ END CODE ]---------------------------------------------------------------------

care se "traduc" în instrucţiuni binare 6A0090. Te vei întreba probabil de ce încă un NOP ? Pentru ca instrucţiunea "PUSH DWORD PTR SS:[EBP-50]" are 3 bytes, iar ceea ce suprascriem trebuie să aibă aceeaşi mărime pentru a nu decala nimic (chiar şi un singur byte ar fi un dezastru). Acum salvează imaginea procesului pe hard disk şi testează noua versiune. Vei vedea că vei putea activa fereastra principală fără nici un fel de probleme, deoarece părintele MessageBox-ului nu mai este acum hwnd ci 0, care este handle-ul desktopului.

Acum hai să radiem definitiv acest apel la MessageBox. Pentru aceasta vom face acelaşi lucu ca şi la prima modificare, înlocuim toate acele "PUSH" de argumente si acel "CALL" la MessageBox cu instrucţiunea "NOP". Vom face la fel şi pentru invocarea MessageBox din procedura WndProc. În final nu vor mai exista decât apeluri fără sens NOP care nu fac nimic altceva decât să mănânce cicluri CPU degeaba.

Pentru discuţii ne vedem pe talk

Personal tools