Horgh
Membre actif
Messages : 197
Sujets : 4
Points: 91
Inscription : Mar 2012
|
[MASM] Extracteur de dll pour Backdoor.Win32.Papras
Blablabla post techniques etc.
Ma contribution, n'hésitez pas à m'indiquer des corrections si vous en trouvez, j'ai codé ça assez salement :')
Si vous voulez des infos sur comment ça marche, ce que ça fait, lisez le readme. Si vous voulez voir le code, c'est plus bas.
Le readme :
Code : README
1) Purpose of this program
This program intend to extract and uncompress the x86 and x86_64 dlls from the TrojanSpy:Win32/Ursnif.gen!M variant, also know as Backdoor.W32.Papras. More information about it can be found here :
http://www.microsoft.com/security/portal/threat/encyclopedia/entry.aspx?Name=TrojanSpy:Win32/Ursnif
2) Variants supported
Keep in mind I use the microsoft detections to name variants of this malware.
The previous Ursnif variants (I checked from TrojanSpy:Win32/Ursnif.FX to TrojanSpy:Win32/Ursnif.gen!L) won't work, for multiple reasons :
- There is no structures in the PE Header with dll informations
- Dlls are still packed with aplib, but it's handled differently. Look at the header in these variants :
[...]
Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F Ascii
0000BC80 00 00 00 00 00 00 00 00 08 00 43 00 4C 00 49 00 .........C.L.I.
0000BC90 45 00 4E 00 54 00 33 00 32 00 08 00 43 00 4C 00 E.N.T.3.2..C.L.
0000BCA0 49 00 45 00 4E 00 54 00 36 00 34 00 00 00 00 00 I.E.N.T.6.4.....
0000BCB0 41 50 33 32 18 00 00 00 46 CF 00 00 55 29 33 E3 AP32...FÏ..U)3ã
0000BCC0 00 DC 00 00 01 28 2D 2A 4D 38 5A 90 38 03 66 02 .Ü..(-*M8Z8f
[...]
You can see the aplib header, and the dll names. This header no more exists in the last variant. If I see this version still spreaded in the wild, maybe I'll add the possibility to extract old variants to this program, but it is not on the top of my todo list.
For details about how the M variant works, see the 4) of this document.
3) Requirements about the dump file
All the failures I had with this program where related to a bad dump. The dump has to have FIVE sections : .text, .rdata, .data, .rsrc, .reloc
The compressed dlls are included into the .reloc section. You MUST NOT alter in any way the PE Header, or the file alignement ; otherwise the program will fail to retrieve the correct offsets of the dlls.
One reliable way to dump the file is to use the "dump partial" option in lordpe. Indicate the Image Base of the unpacked program in memory, the size of it and you're done. Don't fix the imports or anything, just use the raw dump.
Last thing about the unpacking : Often you'll have a valid dump by dumping the memory allocated (with correct imports and all, as for many of these crap packers). These dumps are fine for analysis, but in most of the cases the program will fail to successfully extract the dlls from them. So I wait until the packer do its modification onto the original file (PE Header modifications & copying the new sections from the unpacked one in memory), and then I dump the original program at the OEP. This will work for the majority of the samples, but as multiple packers are used these methods are not foolproof and it's very likely you'll have to fix dumps your own way.
4) Quick explanation of the program's action
The TrojanSpy:Win32/Ursnif.gen!M variant includes two hardcoded structures in the PE Header of the unpacked program. These structures contain the informations about each dll. I'll detail the dll extraction process as it is performed in the malware :
First it retrieves the adress of the structures :
VA to dll structure : (ImageBase + (NumberOfSections * 28h)) + SizeOfOptionalHeader + 40
Then it will check the structure parameters. Here is its composition :
Structure (size : 0x14h) :
46 4A : mark
00 00 : unused
00 88 00 00 : dll offset
00 7C 01 00 : dll size
A7 CE 75 4F : constant
05 20 00 00 : ?
The first parameter is 'FJ', it indicates the beginning of the struct. The next word is unused, then you'll have a dword with the dll offset and another dword with the dll size. The two last dwords are more interesting. The first one is a constant included in the structure and in the code. Before the call to the extraction procedure, you'll have this dword pushed onto the stack. I don't know the use of the last one, and I skipped the checks on it in my code.
Once the program has retrieved the dll offset, its size, verified the constants, it simply use the depack procedure of the aplib (v1.01) to uncompress dlls.
Je conseille fortement de le lire si vous voulez utiliser le programme correctement, c'est assez chiant d'avoir un bon dump. J'ai aussi inclus dedans une explication rapide du fonctionnement de papras au niveau de l'extraction / décompression des dlls. Si vous voulez des samples pour tester, pingez moi sur irc ; si vous voulez faire du bug report / suggestions, Horgh[at]lavabit[dot]com.
Le code now :
Code : ; -------------------------------------------------------------------------------------------------------------------------
; I don't give a fuck about the licence, do what you want with the code, but crediting the original author is always nice
; This is a program to extract and uncompress dlls embedded in papras / ursnif executables. This will work only with the M
; variant, and it has to be unpacked first.
; Usage in command line : extract.exe <papras exe>
; For more information about the way it works, see the readme file
;
; To contact me for suggestions or bug reports : Horgh[at]lavabit[dot]com
; -------------------------------------------------------------------------------------------------------------------------
.386
.model flat,stdcall
option casemap:none
include aplib/depack.asm
include windows.inc
include kernel32.inc
include user32.inc
include shell32.inc
include masm32.inc
includelib kernel32.lib
includelib user32.lib
includelib shell32.lib
includelib masm32.lib
.data?
hFile HANDLE ? ; handle on dumped exe
hFile2 HANDLE ? ; handle on dll written on disk
lpBuffer db 1024 dup (?) ; buffer for the header
lpNumberOfBytesRead LPVOID ?
lpNbBytesWritten dd ?
lpCmdArg db 128 dup (?)
WhichDll db ? ; determine if x86 or x86_64 to be processed next
BufOut dd ? ; ptr to allocated memory with uncompressed dlls
.data
Checksum1 dd 4F75CEA7h ; constants
Checksum2 dd 90F8AAB4h
dll1 db "x86",0
dll2 db "x64",0
strpg db "This program intend to extract and uncompress dlls from the TrojanSpy.Win32Ursnif.gen!M variant. Be aware that previous variants won't work (different implementation).",13,10
db "Usage : extract.exe <unpacked papras executable>",13,10,0
str2 db "--------------------------------------------------------------------------------",13,10,0
failed db "The extraction process failed. Verify that your exe meet all the requirements",13,10,0
success db "Congrats ! The extraction process was successful :)",13,10,0
x86 db "x86 dll extraction... OK !",13,10,0
x64_ db "x64 dll extraction... OK !",13,10,0
.code
start:
invoke GetCL, 1, addr lpCmdArg ; get commandline argument
invoke StdOut, addr strpg
invoke StdOut, addr str2
call ExtractDlls
test eax, eax
jne failure
invoke StdOut, addr success
invoke ExitProcess,0
failure:
invoke StdOut, addr failed
invoke ExitProcess,0
ExtractDlls proc
push ebp
mov ebp, esp
sub esp, 0Ch
; Check if file passed in commandline exists, and open a handle on it if true
invoke CreateFile, addr lpCmdArg,GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
cmp eax, INVALID_HANDLE_VALUE
jz SomethingWentWrong
mov hFile, eax
; read the first 1024 octets of the file, check if the file is an executable
invoke ReadFile,hFile, addr lpBuffer, sizeof lpBuffer, addr lpNumberOfBytesRead, 0
mov ebx, offset lpBuffer
mov ax, "ZM"
cmp ax, word ptr [lpBuffer]
jnz SomethingWentWrong
; retieve ptr to dll structure
mov eax, [ebx+3Ch] ; e_lfanew
add eax, ebx ; PE Header RVA
movzx ecx, word ptr [eax+6] ; retrieve NumberOfSections
movzx edx, word ptr [eax+14h] ; retrieve SizeOfOptionalHeader
imul ecx, 28h
add ecx, eax ; add ImageBase
lea esi, [edx+ecx+40h] ; (ImageBase + (NumberOfSections * 28)) + SizeOfOptionalHeader + 40 = VA to dll structure
; test if 'FJ' mark is present on the first word of the structure
LoadStructMark:
movzx eax, word ptr [esi]
test ax, ax ; test if null, if not jump to cmp
jnz short TestIfMark
movzx eax, word ptr [esi+2] ; if null, try to get second structure
lea esi, [esi+eax*4+14h]
mov eax, 4A46h ; and cmp if mark is present
cmp [esi], ax
jz MarkFound
jmp SomethingWentWrong ; if not, no correct structure was found.
TestIfMark:
mov ecx, 4A46h
cmp ax, cx
jz short MarkFound
add esi, 14h ; if mark is not equal, try on the next structure
jmp LoadStructMark
; if a correct structure was found, check if its 5th field is equal to the constant
MarkFound:
movzx eax, byte ptr [WhichDll] ; to determine which checksum is to compare
test eax, eax
jne dll64
mov WhichDll,1
mov eax, Checksum1
jmp next
dll64:
mov eax, Checksum2
next:
cmp [esi+0Ch], eax ; now compare constant in the struct and constant in the code
jnz SomethingWentWrong
; Allocate memory for reading compressed dll
mov eax, [esi+8] ; eax = dll size
inc eax ; size + 1
push eax ; uBytes
push 40h ; uFlags
call LocalAlloc
mov edi, eax
test edi, edi ; test if memory successfully allocated
jz SomethingWentWrong
; Read compressed dll and allocate memory to uncompress it
mov eax, [esi+4] ; eax = dll VA
invoke SetFilePointer,hFile,eax,NULL,FILE_BEGIN ; put FilePointer onto dll VA
invoke ReadFile,hFile, edi, [esi+8], addr lpNumberOfBytesRead, 0
mov eax, [esi+8] ; eax = dll size
inc eax ; size + 1
push eax ; uBytes
push 40h ; uFlags
call LocalAlloc
; uncompress dll with aplib
mov BufOut, eax
push eax ; mem allocated
push edi ; compressed dll
call aP_depack_asm
cmp eax, [esi+8] ; check if everything decrypted
jnz SomethingWentWrong
movzx eax, byte ptr [WhichDll]
cmp eax, 2 ; has x86 dll already be uncompressed ?
je x64
; write x86 dll to disk
invoke CreateFile, addr dll1, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
cmp eax, INVALID_HANDLE_VALUE
jz SomethingWentWrong
mov hFile2, eax
invoke WriteFile,hFile2, BufOut, [esi+8], addr lpNbBytesWritten,NULL
invoke CloseHandle,hFile2
invoke StdOut, addr x86
mov WhichDll, 2
add esi, 14h
jmp LoadStructMark
; write x64 dll to disk
x64:
invoke CreateFile, addr dll2, GENERIC_WRITE, 0, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
cmp eax, INVALID_HANDLE_VALUE
jz SomethingWentWrong
mov hFile2, eax
invoke WriteFile,hFile2, BufOut, [esi+8], addr lpNbBytesWritten,NULL
invoke StdOut, addr x64_
invoke CloseHandle,hFile
invoke CloseHandle,hFile2
xor eax, eax
leave
ret
SomethingWentWrong:
mov eax, 1
leave
ret
ExtractDlls endp
end start
Et si vous voulez le package, c'est par ici
|