Armadillo5 x64 Unpacking
From UIC
Armadillo v5 x64 basic unpacking
Contents |
| Infos | |
|---|---|
| Author: | Pnluck |
| Email: | Pnluck@virgilio.it |
| Website: | http://pnluck.netsons.org |
| Date: | 03/03/2008 (dd/mm/yyyy) |
| Level: |
|
| Language: | English |
| Comments: |
Della serie alla tv c'è il grande fratello :(, ma per fortuna che c'è il Rev e la Radio trasmette il rock!! |
Introduction
Armadillo is one of the most popular executable protectors, and it's one of the few that support PE+ executables.
Tools
Ida: I used this debugger
Explorer Suite: Task Explorer and CFF Explorer
Attached file: Unpackme, Security.dll and ItRebuilder demo
Tutorial infos
We're going to unpack the windows calc.exe protected by Armadillo with basic protection options.
Before reading this tutorial you should know how x64 architecture works: I advise you to read these articles:
Everything You Need To Know To Start Programming 64-Bit Windows Systems
Moving to Windows x64
I'm so impatient to write the first (I believe) unpacking tutorial on x64 executable in the RCE scene =)
Essay
A look inside the Armadillo Loader
Let's disasm the file with IDA: the EP of the loader is a normal Visual C++ EP, IDA actually recognises the WinMain at: 0x000000010001F000. We are looking for the Armadillo MainProc:
.text1:000000010001F3EE mov [rsp+68h+var_48], eax
.text1:000000010001F3F2 cmp [rsp+68h+var_48], 0
.text1:000000010001F3F7 jnz loc_10001F48E
This call has another call inside it, that leads us to the real Armadillo_MainProc RVA_Section_EntryPoint + 000283A0 (00000001000283A0).
We notice a first check that uses a Mutex: this checks if the process is already running
.text1:00000001000284A5 mov ecx, cs:dword_10005F618 ; dd 0DEAF1A85h
.text1:00000001000284AB xor ecx, eax
.text1:00000001000284AD mov eax, ecx
.text1:00000001000284AF xor eax, cs:dword_10005F66C ; dd 47D5D8D9h
.text1:00000001000284B5 mov r8d, eax ; eax = 6EB050EDh
.text1:00000001000284B8 lea rdx, aRn08x ; "RN%08X"
.text1:00000001000284BF lea rcx, [rsp+798h+WindowName] ; char *
.text1:00000001000284C4 call sprintf
.text1:00000001000284C9 lea r8, [rsp+798h+WindowName] ; lpName
.text1:00000001000284CE xor edx, edx ; bInitialOwner
.text1:00000001000284D0 xor ecx, ecx ; lpMutexAttributes
.text1:00000001000284D2 call cs:CreateMutexA
.text1:00000001000284D8 mov [rsp+798h+hMutex], rax
.text1:00000001000284E0 cmp [rsp+798h+hMutex], 0
.text1:00000001000284E9 jz loc_100028645
If this check is passed the execution proceeds, and we reach this interesting code:
.text1:0000000100028890 mov [rsp+798h+var_618], eax
.text1:0000000100028897 xor ecx, ecx
.text1:0000000100028899 call LoadRegisterServiceProcessApi
.text1:000000010002889E xor ecx, ecx ; int (__stdcall *)(unsigned __int64)
.text1:00000001000288A0 call ?_set_new_handler@@YAP6AH_K@ZP6AH0@Z@Z ; _set_new_handler(int (*)(unsigned __int64))
.text1:00000001000288A5 cmp [rsp+798h+var_618], 1
.text1:00000001000288AD jnz short loc_1000288C3
What is the Work_with_Dll thing? Well it's a loader function that loads in memory a packed dll (Security.dll), that is embedded in the executable: it is the core of the Armadillo protection.
.text1:0000000100026F97 mov eax, cs:dword_10005F674
.text1:0000000100026F9D mov ecx, cs:dword_10005F690
.text1:0000000100026FA3 xor ecx, eax
.text1:0000000100026FA5 mov eax, ecx
.text1:0000000100026FA7 xor eax, cs:dword_10005F63C
.text1:0000000100026FAD and eax, 3
.text1:0000000100026FB0 movzx ecx, ax
.text1:0000000100026FB3 call sub_10002B110
.text1:0000000100026FB8 call FindSignatureOfPackedDll
.text1:0000000100026FBD mov [rsp+148h+SectionWithSignature_VA], rax
.text1:0000000100026FC5 cmp [rsp+148h+SectionWithSignature_VA], 0
.text1:0000000100026FCE jnz short loc_100026FE1
FindSignatureOfPackedDll finds the first section of the executable image that begins with "PRDATA00".
Let's continue the analysis...
.text1:0000000100027080 movzx r8d, [rsp+148h+var_B8] ; char
.text1:0000000100027089 lea rdx, [rsp+148h+hWnd] ; __int64
.text1:0000000100027091 mov rcx, [rsp+148h+MemAddrOfDll] ; void *
.text1:0000000100027099 call MemExtractDll
.text1:000000010002709E mov [rsp+148h+MemAddrOfDll], rax
.text1:00000001000270A6 cmp [rsp+148h+MemAddrOfDll], 0
.text1:00000001000270AF jnz short loc_1000270B8
.text1:00000001000270B1 xor eax, eax
.text1:00000001000270B3 jmp loc_1000273D3
.text1:00000001000270B8 ;
.text1:00000001000270B8
.text1:00000001000270B8 loc_1000270B8: ; CODE XREF: Work_with_Dll+11F�j
.text1:00000001000270B8 mov rcx, [rsp+148h+MemAddrOfDll] ; void *
.text1:00000001000270C0 call DllLoader
.text1:00000001000270C5 mov [rsp+148h+MemAddrOfDll], rax
.text1:00000001000270CD cmp [rsp+148h+MemAddrOfDll], 0
.text1:00000001000270D6 jnz short loc_1000270DF
MemExtractDll extracts in memory the packed dll. DllLoad is a LoadLibrary-like function: it tries to load the dll at the correct ImageBase, then loads the ImportTable, calculates Relocations, and saves the entrypoint address in a variable.
mov eax, [rax+10h] ;Address of Entry Point
mov rcx, [rsp+0D8h+MemorySpaceVirtualAlloc]
add rcx, rax
mov rax, rcx
mov cs:ptrDllEntryPoint, rax
Actually after call DllLoader there's this:
.text1:00000001000270DF loc_1000270DF: ; CODE XREF: Work_with_Dll+146�j
.text1:00000001000270DF xor r8d, r8d
.text1:00000001000270E2 mov edx, 1
.text1:00000001000270E7 mov rcx, cs:ImageBaseDll
.text1:00000001000270EE call cs:ptrDllEntryPoint ; call some function
.text1:00000001000270F4 test eax, eax
.text1:00000001000270F6 jnz short loc_100027109
So I dumped the dll with Task Explorer to analyse it.
Now, on with the Work_with_Dll function:
.text1:0000000100027109 lea rdx, aSetfunctionadd ; "SetFunctionAddresses"
.text1:0000000100027110 mov rcx, cs:ImageBaseDll ; __int64
.text1:0000000100027117 call LoadFunctionFromDll ; an LoadLibrary-like function
.text1:000000010002711C mov [rsp+148h+SetFunctionAddress_VA], rax
.text1:0000000100027124 mov eax, cs:dword_10005F620
.text1:000000010002712A mov ecx, cs:dword_10005F660
.text1:0000000100027130 xor ecx, eax
.text1:0000000100027132 mov eax, ecx
.text1:0000000100027134 xor eax, cs:dword_10005F674
.text1:000000010002713A xor eax, cs:dword_10005F640
.text1:0000000100027140 lea rcx, [rsp+148h+dll_anProc3]
.text1:0000000100027148 mov [rsp+148h+var_C8], rcx
.text1:0000000100027150 lea rcx, [rsp+148h+dll_OEProc_]
.text1:0000000100027158 mov [rsp+148h+var_D0], rcx
.text1:000000010002715D lea rcx, [rsp+148h+Dll_UnpackExeProc]
.text1:0000000100027165 mov [rsp+148h+var_D8], rcx
.text1:000000010002716A mov [rsp+148h+var_E0], eax
.text1:000000010002716E lea rax, loc_10002B960
.text1:0000000100027175 mov [rsp+148h+Proc_10002b690], rax
.text1:000000010002717A lea rax, loc_10002B950
.text1:0000000100027181 mov [rsp+148h+var_F0], rax
.text1:0000000100027186 lea rax, loc_10002B890
.text1:000000010002718D mov [rsp+148h+var_F8], rax
.text1:0000000100027192 lea rax, Proc9
.text1:0000000100027199 mov [rsp+148h+var_100], rax
.text1:000000010002719E lea rax, Proc8
.text1:00000001000271A5 mov [rsp+148h+var_108], rax
.text1:00000001000271AA lea rax, Proc7
.text1:00000001000271B1 mov [rsp+148h+var_110], rax
.text1:00000001000271B6 lea rax, Proc6
.text1:00000001000271BD mov [rsp+148h+var_118], rax
.text1:00000001000271C2 lea rax, Proc5
.text1:00000001000271C9 mov [rsp+148h+var_120], rax
.text1:00000001000271CE lea rax, Proc4
.text1:00000001000271D5 mov [rsp+148h+var_128], rax
.text1:00000001000271DA lea r9, Proc3
.text1:00000001000271E1 lea r8, Proc2
.text1:00000001000271E8 lea rdx, loc_10002A790
.text1:00000001000271EF mov rcx, cs:hInstance
.text1:00000001000271F6 call [rsp+148h+SetFunctionAddress_VA] ; interesting!!
.text1:00000001000271FD mov rax, [rsp+148h+Dll_UnpackExeProc]
.text1:0000000100027205 mov [rsp+148h+Dll_UnpackExeProc_], rax
.text1:000000010002720D mov rax, [rsp+148h+dll_OEProc_]
.text1:0000000100027215 mov cs:dll_OEProc, rax
.text1:000000010002721C mov rax, [rsp+148h+dll_anProc3]
.text1:0000000100027224 mov cs:Dll_AnProc3_, rax
.text1:000000010002722B mov [rsp+148h+var_B4], 0
.text1:0000000100027236 movzx eax, [rsp+148h+var_B8]
.text1:000000010002723E test eax, eax
.text1:0000000100027240 jz short loc_100027253
What does the Security.SetFunctionAddress routine do?
.text:000000018002F660 mov [rsp+arg_18], r9
.text:000000018002F665 mov [rsp+arg_10], r8
.text:000000018002F66A mov [rsp+arg_8], rdx
.text:000000018002F66F mov [rsp+arg_0], rcx
.text:000000018002F674 sub rsp, 28h
.text:000000018002F678 movzx eax, cs:ThisFunctionWasUsed
.text:000000018002F67F test eax, eax
.text:000000018002F681 jnz loc_18002F79F
...
; un-important code for the unpacking
...
.text:000000018002F755
.text:000000018002F755 loc_18002F755: ; CODE XREF: SetFunctionAddresses+C9�j
.text:000000018002F755 mov eax, [rsp+28h+arg_68]
.text:000000018002F75C mov cs:Val_7FD3BD7F, eax
.text:000000018002F762 mov rax, [rsp+28h+arg_70]
.text:000000018002F76A lea rcx, UnpackExeProc
.text:000000018002F771 mov [rax], rcx
.text:000000018002F774 mov rax, [rsp+28h+arg_78]
.text:000000018002F77C lea rcx, OEProc
.text:000000018002F783 mov [rax], rcx
.text:000000018002F786 mov rax, [rsp+28h+arg_80]
.text:000000018002F78E lea rcx, An_Proc_3
.text:000000018002F795 mov [rax], rcx
.text:000000018002F798 mov cs:ThisFunctionWasUsed, 1
.text:000000018002F79F
.text:000000018002F79F loc_18002F79F: ; CODE XREF: SetFunctionAddresses+21�j
.text:000000018002F79F add rsp, 28h
.text:000000018002F7A3 retn
.text:000000018002F7A3 SetFunctionAddresses endp
This function saves in some variables the addresses of the functions that unpack the executable and are used to call the OEP of the executable.
Let's return to Work_with_Dll: after some useless code (useless to us), there's an interesting call:
.text1:0000000100027350 mov [rsp+148h+ExeImgBase], rax
.text1:0000000100027358 mov eax, cs:dword_10005F5F0
.text1:000000010002735E mov [rsp+148h+var_30], eax
.text1:0000000100027365 mov [rsp+148h+var_2C], 0FFFFFFFFh
.text1:0000000100027370 lea rcx, [rsp+148h+var_78]
.text1:0000000100027378 call [rsp+148h+Dll_UnpackExeProc_]; another interesting function!!
.text1:000000010002737F mov [rsp+148h+var_A0], eax
.text1:0000000100027386 cmp [rsp+148h+var_2C], 0FFFFFFFFh
.text1:000000010002738E jz short loc_10002739F
Security.UnpackExecutable calls a function that executes some antidebug controls, and then calls other functions that implement these protections: Debug-Blocker, Copymem II, Nanomites, Import table elimination, Import redirection, Code Splicing etc...
After that we exit from Word_With_Dll and come back to Armadillo_MainProc. In the end there's the call to OEProc
Now we can see what Security.OEProc (at 0000000180060610) does:
.text:000000018006080F call rax ; call executable OEP
.text:0000000180060811 mov [rsp+58h+var_38], eax
.text:0000000180060815 jmp exit
..... un-useful to us ;)
.text:0000000180060853 mov rcx, [rcx+10h]
.text:0000000180060857 call rax ; call executable OEP
That two "call rax" are used to call the real OEP of the executable, that (in this case) is:
.text:0000000100018CD3 sub rsp, 0C8h
.text:0000000100018CDA mov [rax+18h], rbx
.text:0000000100018CDE mov [rax+20h], rdi
Now we must talk briefly about IT: the IAT of the dumped file is not correct at all as you can see from this log:
1) RVA: 1008 value: 7ff7ff0b100 Import: ADVAPI32.dll.RegCloseKey
2) RVA: 1010 value: 7ff7ff03ef0 Import: ADVAPI32.dll.RegQueryValueExA
3) RVA: 1018 value: 1eda720
4) RVA: 1020 value: 7ff7fc92a90 Import: GDI32.dll.SetBkColor
5) RVA: 1028 value: 7ff7fc92b70 Import: GDI32.dll.SetTextColor
6) RVA: 1030 value: 7ff7fc92d40 Import: GDI32.dll.SetBkMode
7) RVA: 1038 value: 1eda560
8) RVA: 1040 value: 1eda850
9) RVA: 1048 value: 1ed8f80
....
Many API addresses were replaced by Security.dll functions: some useful, some not.
Useless functions were inserted in place of null qwords: an example is 1eda720
.text:000000018003A720 ; DATA XREF: FuckingProc+22AF�o ...
.text:000000018003A720 sub rsp, 28h
.text:000000018003A724 call MyFakeNull4 ;Recursive function
.text:000000018003A729 add rsp, 28h
.text:000000018003A72D retn
.text:000000018003A72D MyFakeNull2 endp
Others, the useful ones, replace -as I said- the real APIs: look at 1eda850:
.text:000000018003A850 mov [rsp+lpLibFileName], rcx
.text:000000018003A855 sub rsp, 38h
.text:000000018003A859 mov rcx, [rsp+38h+lpLibFileName]
.text:000000018003A85E call sub_180039D70
.text:000000018003A863 mov [rsp+38h+var_18], rax
.text:000000018003A868 cmp [rsp+38h+var_18], 0
.text:000000018003A86E jnz short loc_18003A8D0
.text:000000018003A870 xor edx, edx
.text:000000018003A872 lea rcx, MyImgBase
.text:000000018003A879 call sub_18003E9B0
.text:000000018003A87E xor edx, edx
.text:000000018003A880 lea rcx, MyImgBase
.text:000000018003A887 call sub_18003E990
.text:000000018003A88C test rax, rax
.text:000000018003A88F jz short Call_LoadLibrabry
.text:000000018003A891 xor edx, edx
.text:000000018003A893 lea rcx, MyImgBase
.text:000000018003A89A call sub_18003E990
.text:000000018003A89F mov [rsp+38h+var_10], rax
.text:000000018003A8A4 mov rcx, [rsp+38h+lpLibFileName]
.text:000000018003A8A9 call [rsp+38h+var_10]
.text:000000018003A8AD mov [rsp+38h+var_18], rax
.text:000000018003A8B2 jmp short loc_18003A8C4
.text:000000018003A8B4
.text:000000018003A8B4 Call_LoadLibrabry:
.text:000000018003A8B4 mov rcx, [rsp+38h+lpLibFileName] ; lpLibFileName
.text:000000018003A8B9 call cs:LoadLibraryA
.text:000000018003A8BF mov [rsp+38h+var_18], rax
.text:000000018003A8C4
.text:000000018003A8C4 loc_18003A8C4:
.text:000000018003A8C4 mov dl, 1
.text:000000018003A8C6 mov rcx, [rsp+38h+var_18]
.text:000000018003A8CB call sub_180039210
.text:000000018003A8D0
.text:000000018003A8D0 loc_18003A8D0:
.text:000000018003A8D0 mov rax, [rsp+38h+var_18]
.text:000000018003A8D5 add rsp, 38h
.text:000000018003A8D9 retn
.text:000000018003A8D9 MyLoadLibraryA endp
Many Security.dll functions are inserted into the IAT, and act as wrappers of real APIs. They are used to replace null qword as many functions that call CreateProcess or CreateFile: in this case, you should decide if it is an api wrapper or a null qword.
That's all you need to know in order to be able to unpack ;)
Armadillo Unpacking
After that short introduction to the Armadillo loader, we can make a running dump.
Set a bp at 00000001000288B6, and step until a "call rax", and then press step into: we are at the OEP, who is at 00018CD0(RVA).
Now we can dump the process with Task Explorer, don't close the debugger or the debuggee!.
Open the dump file with CFF Explorer and delete the following from the Section Table: text1, pdata2 and rsrc.
After that we can null the IAT Directory, and we must change the .text section flags: select "Is executable", "is writable", "is readable".
Extract from the attached file ItRebuilder.exe (it's an IT Rebuilder built ad-hoc for this target) copy it into the same directory of the dumped file and run it. Enter the name of the dump, the entrypoint RVA and wait for the message "The file was successfully rebuilt".
Run the dumped file, does it work? Yeahh it works!! ;)
Pn[L]uck
Note Finali
Thx to Ntoskrnl, Que, Evilcry, ZaiRoN, Quake2, Ermes, 0x87k, Locu, Evo, StarzBoy (indian friend of mine), and everyone who I met ops also Pincopall, Active85k e DrWatson (me li stavo a scordà, nun sia mai :P)
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.