From Merlin
Î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 ferestră 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]
-[BEGIN CODE 0x0A]----------------------------------------------------------------
-[ END CODE ]---------------------------------------------------------------------