Quellcode von Loadern/Memorypatchern:
N0iseTools - ein Beispiel für Codeinjection + EAT Hook von Funktionen.
Oder: wie man .NET Protections umgeht, ohne .NET auch nur anzufassen
make.batCode:;############################# ;N0ise Tools Loader ;autor: EBFE ;mail: ebfe@inbox.ru ;############################# .386 .model flat, stdcall option casemap :none include windows.inc include masm32.inc include user32.inc include kernel32.inc include psapi.inc includelib psapi.lib include advapi32.inc includelib advapi32.lib includelib masm32.lib includelib user32.lib includelib kernel32.lib .data kernel32 db "kernel32.dll",0 crypt_get_hash_param db "CryptGetHashParam",0 Error_starting db "Exe konnte nicht gefunden/gestartet werden!",0 Error_alloc db "Speicher konnte nicht reserviert werden!",0 Error_injection db "Konnte Code nicht injecten!",0 count dd 0 .data? process PROCESS_INFORMATION <> procstart STARTUPINFO <> injmem dd ? buffer db(256) ? .code ;#### eigentliche Injection #### Injection:: ;db 0ebh,0feh call inj_delta mydata: temp dd 0 advapi32 db "advapi32.dll",0 cryptGetHashParamEAT dd 0 loadlib_addr dd 0 virtual_protect dd 0 exit_thread dd 0 hook: db 90h, 090h ;call original func push [esp + 14h] push [esp + 14h] push [esp + 14h] push [esp + 14h] push [esp + 14h] db 0e8h ;CALL Opcode original_func: ; dd 00000000 ;Distanz muss vor der Ausführung reingepatcht werden test eax, eax ; return on error jnz @f ret 14h @@: pushad call delta hook_data: original_hwid db 48h,0d3h,0cbh,0c8h,0cah,0b8h,2ah,0b6h db 0dah,8ah,00,0a6h,69h,20h,34h,73h ;db(32) dup (0) user_hwid db 0f9h,00h,30h,58h,0a8h,0cch db 0b1h,3ah,34h,66h,1ch,4fh,1bh,45h,82h,48h;(16) dup (0) delta: pop edi ;param 3 holen, pushad beachten mov edx,[esp + 20h + 0Ch] test edx, edx jz ende lea ebx, [edi + (user_hwid - hook_data)] ; vergleiche den Buffer mit HWID mov eax, dword ptr[ebx] cmp [edx],eax jne ende mov eax, dword ptr[ebx + 4] cmp [edx + 4], eax jne ende mov eax, dword ptr[ebx + 8] cmp [edx + 8], eax jne ende mov eax, dword ptr[ebx + 12] cmp [edx + 12], eax jne ende ;ok, beide gleich, schreibe nun original ID in den Buffer rein lea ebx, [edi + (original_hwid - hook_data)] mov eax, [ebx] mov [edx], eax mov eax, [ebx + 4] mov [edx+4], eax mov eax, [ebx + 8] mov [edx+8], eax mov eax, [ebx + 12] mov [edx+12], eax ende: popad ret 14h db 0ebh,0feh db 5eh,05h,73h,21h inj_delta: pop edi ; mydata Address ;call LoadLibraryA(Advapi32) lea eax, [edi + (advapi32 - mydata)] push eax call dword ptr [edi + (loadlib_addr - mydata)] ; in eax ImageBase of ADVAPI32 DLL mov esi, [edi + (cryptGetHashParamEAT - mydata)] mov ebx, eax ; ebx = ImageBase of ADVAPI32 DLL add esi, eax ; in eax = EAT Address of cryptGetHashParam ; set EAT writeable: ;invoke VirtualProtectEx,process.hProcess,lstrcmpEAT,4,PAGE_EXECUTE_READWRITE,offset procstart ;procstart==temp Variable push edi ; temp push PAGE_EXECUTE_READWRITE push 4 push esi call dword ptr[edi + (virtual_protect - mydata)] ;backup original address: mov eax, [esi] add eax, ebx ;berechne JMP distanz von inj code zur API Function lea edx, [edi + (original_func - mydata + 4)] sub eax, edx mov [edi + (original_func - mydata)], eax ;patch EAT lea eax, [edi + (hook - mydata)]; sub eax, ebx ; EAT Hookaddr = hook addr - ImageBase mov [esi], eax ; done !:) call dword ptr[edi + (exit_thread - mydata)] db 0, 0, 0, 0 Injection_end:: ;#### main start: invoke GetCL,1,offset buffer cmp eax,1 jne Exit ;##### Starte Exe #### invoke CreateProcess,offset buffer,0,0,0,FALSE,CREATE_SUSPENDED,0,0,offset procstart,offset process test eax,eax jnz @f mov eax,offset Error_starting jmp Error @@: ; LoadLibrary und VirtualProtect Adressen über eigene IAT bestimmen mov eax, LoadLibrary mov eax, [eax+2] mov eax, [eax] mov loadlib_addr, eax mov eax, VirtualProtect mov eax, [eax+2] mov eax, [eax] mov virtual_protect, eax mov eax, ExitThread mov eax, [eax + 2] mov eax, [eax] mov exit_thread, eax ;##### hole cryptGetHashParam Adresse in EAT invoke LoadLibrary,offset advapi32 mov ebx, eax mov esi,offset crypt_get_hash_param mov ecx,sizeof crypt_get_hash_param call getproc ; ; get RVA only! sub eax, ebx mov cryptGetHashParamEAT,eax invoke LoadLibrary, offset kernel32 mov ebx, eax ;#### warte, bis kernel32 verfügbar ist #### wait_loop: add count,1 cmp count,10000 ;max. 10 Sek warten jb @f mov eax, offset Error_injection jmp Error @@: invoke ResumeThread,process.hThread invoke Sleep,1 invoke SuspendThread,process.hThread invoke GetModuleInformation,process.hProcess,ebx,offset procstart,sizeof procstart;procstart=TEMP Var test eax,eax jz wait_loop ;##### Alloziere Speicher in ZielExe #### invoke VirtualAllocEx,process.hProcess,0,offset Injection_end-offset Injection,MEM_COMMIT,PAGE_EXECUTE_READWRITE test eax,eax jnz @f mov eax,offset Error_alloc jmp Error @@: mov injmem,eax ;##### Schreibe Injection invoke WriteProcessMemory,process.hProcess,injmem,offset Injection,Injection_end-Injection,offset procstart test eax,eax jnz @f inj_error: mov eax,offset Error_injection jmp Error @@: invoke CreateRemoteThread,process.hProcess,0,0,injmem,0,0,0 test eax,eax jz inj_error ;##### Setze Zielexe fort: invoke ResumeThread,process.hThread Exit:: invoke ExitProcess,0 ret ;########################### Error: invoke MessageBox,0,eax,0,MB_ICONERROR ;eigentlich müsste noch ein VirtualFreeEx rein, aber da der Prozess ;eher terminiert wird, verzichte ich auf den Aufwand invoke TerminateProcess,process.hProcess,0 jmp Exit ;########################## ;##### Parst die Export Table und gibt Adresse der Funktion in EAT zurück getproc: ;erwartet: DLL Base in EAX,in ECX Stringlänge, in ESI string adresse ;rückgabe: Export Adresse in EAX push ebp push edi mov edx,[eax+3ch] lea edx,dword ptr [eax+edx+78h] ;nun EDX=Exportdirektory VA mov edx,[edx] ;RVA holen add edx,eax ;VA zeiger auf ExportDirectory Eintrag push edx mov ebp,[edx+18h] ;Anzahl der Namen mov edx,[edx+20h] ;in EDX: zeiger auf *AdressOfNames add edx,eax ;Erster Eintrag des AdressOfNames Arrays cld search: mov edi,dword ptr [edx+ebp*4-4] add edi,eax push ecx push esi repe cmpsb pop esi pop ecx jz found dec ebp jnz search ;nicht gefunden: mov eax,-1 pop ebp pop ebp ;ebp wiederherstellen+Stackframe ret found: ;nun in EBP Funktionsnummer pop edx ;zeiger auf ExportDirectory Eintrag push edx mov edx,dword ptr [edx+24h] ;*AdressOfOrdinals RVA add edx,eax ;AdressOfOrdinals VA movzx ecx,word ptr [ebp*2-2+edx]; lese index der Funktion aus der *AdressOfOrdinals pop edx mov edx,dword ptr [edx+1ch] add edx,eax lea eax,dword ptr [ecx*4+edx] ;VA Adresse in exporttable ;add eax,edx ;+ImageBase=VA pop edi pop ebp ret end start
der Code des Builders wird nicht gepostet, da dieser nur 3 (durchaus wichtige) Aufgaben hatte: NFO anzeigen, Musik abspielen und die eingegebene HWID in den "hwid_user" Byte-Array (Zeile 74) zu schreiben (vorher natürlich Str2Hex Umwandlung durchzuführen). Das kann man eigentlich auch gut per Hand machen. D.h nichts wirklich interessantesCode:SET INCLUDE=C:\masm32\include SET LIB=C:\masm32\lib SET PATH=C:\masm32\bin SET NAME=loader if exist %NAME%.obj del %NAME%.obj if exist %NAME%.exe del %NAME%1.exe ml /c /coff /nologo %NAME%.asm Link /SUBSYSTEM:WINDOWS /FILEALIGN:512 /MERGE:.data=.text /MERGE:.rdata=.text /SECTION:.text,RWE %NAME%.obj pause
PíxelStealer - im wesentlichen interessant, weil PixelStealer 1.5.5 mit Themida geschützt ist:
make.batCode:;############################# ;Pixelloader ;autor: EBFE ;mail: ebfe@inbox.ru ;############################# .386 .model flat, stdcall option casemap :none include windows.inc include masm32.inc include user32.inc include kernel32.inc include psapi.inc include advapi32.inc includelib psapi.lib includelib advapi32.lib includelib masm32.lib includelib user32.lib includelib kernel32.lib .data filename db "pixel Stealer.exe",0 Error_starting db "'Pixel Stealer.exe' konnte nicht" db " gefunden/gestartet werden!",0 Error_patch db "Konnte den Code nicht patchen!",0 count dd 0 BUFFER dd 0 ;# Format: DWORD: Adresse ;4Bytes: Patch ; HWID "build" Funktion patch dd 4BA570h db 0b8h,0a0h,92h,40h dd 4BA574h db 00, 0c3h,90h,90h ;protector antis dd 6B1E9Fh db 0EBh,53h, 90h, 90h ;erster vbaCmp dd 4b9ef3h db 33h, 0ffh, 0f7h,0dfh ;überspringe den HTTP check dd 4b9519h db 0e9h,88h,00,00 dd 4b951ch db 00,00,90h,90h ;überspringe sicherheitshalber den ersten "error" Check dd 4b95c8h db 0ebh,06h,90h,90h ;überspringe den zweiten "error" Check dd 4b9689h db 0ebh,08h,90h,90h ;dritter Check, muss genoppt werden dd 4b96edh db 90h,90h,68h,20h ;weiterer "error" Check: dd 4b9732h db 0ebh, 06h,90h,90h ;noch ein "error" Check, langsam wird es lästig dd 4b9762h db 74h,04h, 90h,90h ;und den Namen nicht zu vergessen: dd 42ed6eh db "Olly" dd 42ed72h db "Dbg",0 PATCH_SIZE EQU ($ -patch)/8 %echo @CatStr(<PATCH_SIZE = >,%(PATCH_SIZE)) .data? process PROCESS_INFORMATION <> procstart STARTUPINFO <> TIME equ 1 TEMP dd ? .code ;#### main start: ;##### Starte Exe #### invoke CreateProcess,offset filename,0,0,0,FALSE, CREATE_SUSPENDED,0,0,offset procstart,offset process test eax,eax jnz @f mov eax,offset Error_starting jmp Error @@: ;#### warte, bis Exe entpackt wurde #### wait_loop: add count,TIME cmp count,10000 ;max. 10 Sek warten jb @f mov eax, offset Error_patch jmp Error @@: invoke ResumeThread, process.hThread invoke Sleep,TIME invoke SuspendThread, process.hThread ; lese Code an der ersten Patchstelle invoke ReadProcessMemory, process.hProcess, [patch], offset BUFFER, 4, offset TEMP ; wenn eingelesene Bytes gleich 0 sind, dann wurde der Code ; von Themida noch nicht entpack cmp DWORD PTR[BUFFER], 0 je wait_loop ;#### setze Codebereich auf Beschreibbar+Ausführbar ;#### sonst schlägt der Patchversuch fehl invoke VirtualProtectEx,process.hProcess, 0401000h, 01E5000h, PAGE_EXECUTE_READWRITE, offset TEMP test eax,eax jnz @f mov eax, offset Error_patch jmp Error @@: ;##### Patchen #### mov edi, offset patch mov esi, PATCH_SIZE patch_loop: lea eax,[edi+4] invoke WriteProcessMemory,process.hProcess, [edi], eax ,4, offset TEMP add edi,8 dec esi jnz patch_loop test eax,eax jnz @f mov eax,offset Error_patch jmp Error @@: ;##### Setze Zielexe fort: invoke ResumeThread,process.hThread Exit:: invoke ExitProcess,0 ret ;########################### Error: invoke MessageBox,0,eax,0,MB_ICONERROR invoke TerminateProcess,process.hProcess,0 jmp Exit ;########################## end start
die gleiche, wie oben gepostet
CodeSoft PW Stealer 0.5 - ein generischer Loader, einfach mal vollständigkeitshalber mitgeliefert (zudem verwendete CodeSoft ASProtect zum Schutz)
make.bat:Code:;Autor: EBFE ;mail: ebfe@inbox.ru .386 .model flat, stdcall option casemap :none include windows.inc include masm32.inc include user32.inc include kernel32.inc include psapi.inc includelib psapi.lib include advapi32.inc includelib advapi32.lib includelib masm32.lib includelib user32.lib includelib kernel32.lib .data kernel32 db "kernel32.dll",0 filename db "codesoft.exe",0 lstrcmp_ db "lstrcmpA",0 Error_starting db "Codesoft.exe konnte nicht gefunden/gestartet werden!",0 Error_alloc db "Speicher konnte nicht reserviert werden!",0 Error_protect db "Konnte Kernel32.dll nicht patchen!",0 Error_injection db "Konnte Code nicht injecten!",0 count dd 0 .data? process PROCESS_INFORMATION <> procstart STARTUPINFO <> lstrcmpEAT dd ? injmem dd ? newEAT dd ? .code ;#### eigentliche Injection #### Injection:: ;db 0ebh,0feh call @f original_lstrcmp dd 0 @@: ;esp+4 = 2 Parameter auf ESP+12 ;heuristik: prüfe ob 2 Parameter aus Exebereich: mov eax,[esp+12] cmp eax,400000h jl @f cmp eax,500000h jg @f ;ok, prüfe ob in beiden Parameter mindestens 4 dezimale Zahlen stehen: mov edx,[eax] and edx, 0c0c0c0c0h jnz @f mov ecx,[esp+8] mov edx,[ecx] and edx,0c0c0c0c0h jnz @f ;suche noch nach einem - searchloop: movzx ecx,byte ptr[eax] inc eax cmp cl,'-' je serial test ecx,ecx jz @f jmp searchloop serial: ;ok, es wird die Serial geprüft: 0 zurückgeben pop eax xor eax,eax ret 8 ;sonst: irgendwas anderes - rufe original auf @@: pop eax jmp dword ptr[eax] Injection_end:: ;#### main start: ;##### Starte Exe #### invoke CreateProcess,offset filename,0,0,0,FALSE,CREATE_SUSPENDED,0,0,offset procstart,offset process test eax,eax jnz @f mov eax,offset Error_starting jmp Error @@: ;##### hole lstrcmpA Adresse in EAT invoke LoadLibrary,offset kernel32 mov ebx,eax mov esi,offset lstrcmp_ mov ecx,sizeof lstrcmp_ call getproc ; mov lstrcmpEAT,eax mov eax,[eax] ;eigentliche RVA der Funktion add eax,ebx ;+ImageBase=VA (äquivalent zum GetProcAddr Aufruf) mov original_lstrcmp,eax ;#### warte, bis kernel32 verfügbar ist #### wait_loop: add count,1 cmp count,10000 ;max. 10 Sek warten jb @f mov eax, offset Error_injection jmp Error @@: invoke ResumeThread,process.hThread invoke Sleep,1 invoke SuspendThread,process.hThread invoke GetModuleInformation,process.hProcess,ebx,offset procstart,sizeof procstart;procstart=TEMP Var test eax,eax jz wait_loop ;#### setze EAT auf "beschreibbar" invoke VirtualProtectEx,process.hProcess,lstrcmpEAT,4,PAGE_EXECUTE_READWRITE,offset procstart ;procstart==temp Variable test eax,eax jnz @f mov eax, offset Error_injection jmp Error @@: ;##### Alloziere Speicher in ZielExe #### invoke VirtualAllocEx,process.hProcess,0,offset Injection_end-offset Injection,MEM_COMMIT,PAGE_EXECUTE_READWRITE test eax,eax jnz @f mov eax,offset Error_alloc jmp Error @@: mov injmem,eax ;##### Patche EAT #### sub eax,ebx ;Adresse der Injection - ImageBase der Kernel32 == Differenz für EAT Patch mov newEAT,eax invoke WriteProcessMemory,process.hProcess,lstrcmpEAT,offset newEAT,4,offset procstart test eax,eax jnz @f mov eax,offset Error_protect jmp Error @@: ;##### Schreibe Injection invoke WriteProcessMemory,process.hProcess,injmem,offset Injection,Injection_end-Injection-4,offset procstart test eax,eax jnz @f mov eax,offset Error_injection jmp Error @@: ;##### Setze Zielexe fort: invoke ResumeThread,process.hThread Exit:: invoke ExitProcess,0 ret ;########################### Error: invoke MessageBox,0,eax,0,MB_ICONERROR invoke TerminateProcess,process.hProcess,0 jmp Exit ;########################## ;##### Parst die Export Table und gibt Adresse der Funktion in EAT zurück getproc: ;erwartet: DLL Base in EAX,in ECX Stringlänge, in ESI string adresse ;rückgabe: Export Adresse in EAX push ebp push edi mov edx,[eax+3ch] lea edx,dword ptr [eax+edx+78h] ;nun EDX=Exportdirektory VA mov edx,[edx] ;RVA holen add edx,eax ;VA zeiger auf ExportDirectory Eintrag push edx mov ebp,[edx+18h] ;Anzahl der Namen mov edx,[edx+20h] ;in EDX: zeiger auf *AdressOfNames add edx,eax ;Erster Eintrag des AdressOfNames Arrays cld search: mov edi,dword ptr [edx+ebp*4-4] add edi,eax push ecx push esi repe cmpsb pop esi pop ecx jz found dec ebp jnz search ;nicht gefunden: mov eax,-1 pop ebp pop ebp ;ebp wiederherstellen+Stackframe ret found: ;nun in EBP Funktionsnummer pop edx ;zeiger auf ExportDirectory Eintrag push edx mov edx,dword ptr [edx+24h] ;*AdressOfOrdinals RVA add edx,eax ;AdressOfOrdinals VA movzx ecx,word ptr [ebp*2-2+edx]; lese index der Funktion aus der *AdressOfOrdinals pop edx mov edx,dword ptr [edx+1ch] add edx,eax lea eax,dword ptr [ecx*4+edx] ;VA Adresse in exporttable ;add eax,edx ;+ImageBase=VA pop edi pop ebp ret end start
die gleiche wie oben