Armadillo v1.7 & IDentify! v2.0 - Tutorial
http://www.siliconrealms.com - Armadillo.
http://www.imptec.com - IDentify! v2.x.
Welcome dear readers. I'm going to be focusing a lot more now on the quality of my tutorials rather than quantity, as there is already enough here on my site for any basic reverser to be "up and reversing" in a fairly short time. Armadillo is a commercial protection for any Win32 program, yet its not a packer as such, as we'll soon see, no modification is actually made to the original PE structure. If you read their webpage you'll see that Armadillo was really written as a spin-off by the Silicon Realms programmers after one of their registered users found a crack for their other program MultiDesk (we can only speculate how said registered user stumbled across such an unfortunate discovery).
As well as adding a wrapper Armadillo also adds a fully featured registration system, this at first sounds pretty ingenious and the registration system doesn't look like much fun to try and break (based on 64-bit keys so they say), Armadillo also adds as you might expect some anti-debugging code and the simple fact that its mostly encrypted means you can forget disassembling. I recommend you take a look at the part on their webpage wrt "Has Armadillo been cracked", the most recent breach was by none other than Alexey Solodovnikov (ASPack's eminent author).
So time to sit back. Lets see what's under the hood of this protection. Running Armadillo we get a rather nice message box, it seems they don't like our SoftICE :-). I'll spare you the mindless tracing and tell you there are 3 methods used to thwart both Windows 95 & NT SoftICE users.
Method 1
0137:10002A17 PUSH EAX <-- //./SICE, //./NTICE, /.//SIWDEBUG.
0137:10002A18 CALL [KERNEL32!CreateFileA]
0137:10002A1E CMP EAX,-01
0137:10002A21 JZ 10002A2C <-- This obviously is a good jump.
This is of course the MeltICE detection, its pretty simple to beat, in fact the program also calls GetLastError() which returns the calling threads last error value, basically its checking that CreateFileA really did fail (so a soft patch editing EAX after the call to CreateFileA will actually be detected), so be sure to make an effective patch (say editing the strings in memory).
In the spirit of ameliorating our own tools lets see if we can remove this detection forever by patching our SoftICE against it. Using something like VxD Viewer (included with DriverStudio), we can see all of our device names :-), SICE, SIWDEBUG & SIWVID. Changing SICE to something like SICE4 is the easiest thing to do, take out your HEX editor and search for SICE inside Winice.exe, probably the first occurrence is the one you want, several bytes before you'll find the SDK version and SoftICE device ID (02 02), change one of the trailing 20h bytes to 34h and also update this new name inside nmtrans.dll (you'll find it as /.//SICE). You can fix the SIWVID (display driver name) by patching siwvid.386 similarly.
The //./SIWDEBUG device however poses a problem. I wasn't able to find even a DDB for it in any of the NuMega files, the problem seems actually to be not when we call CreateFileA, but after the call to GetLastError. The value of EAX on which GetLastError depends is set beneath CreateFileA. Here's the code from inside kernel32.dll :-
0137:BFF7D32A PUSH EDX <-- //./Device Name.
0137:BFF7D32B PUSH EAX <-- 1.
0137:BFF7D32C PUSH ESI <-- Device Name (without //./).
0137:BFF7D32D PUSH BFF99758 <-- Sleep() maybe.
0137:BFF7D332 PUSH 00
0137:BFF7D334 PUSH 002A001F <-- VWin32_Dispatch() VxD loading/unloading.
0137:BFF7D339 CALL KERNEL32!ORD_0001
As far as I know Service ID 002A001F is undocumented, yet VxD Viewer will come to our rescue. Unassembling at the device's Control Procedure reveals little more than a CLC (re-enable interrupts), scrolling the data window a little higher will find you the mangled device name SIWDxxxEBUGh (where x are 0xC7,0x40 & 0x10 on my system). Search this sequence inside Winice.exe and we can now change it :-).
Method 2
BOOL IsDebuggerPresent(VOID)
This function is exported from kernel32.dll, it returns non-zero in the presence of a debugger, its only implemented in Windows NT so 95/98 users need not apply (my guess is if you run NT you'll probably have to patch kernel32.dll itself against this).
Method 3
:? eax 4243484B 1111705675 "BCHK"
0137:0040212F MOV EBP,EAX <-- Place it in EBP.
0137:00402131 MOV EAX,00000004
0137:00402136 INT 3 <-- Are you here WINICE.EXE :-).
This is the BoundsChecker / SoftICE interface which was first documented about 3 years ago I think, its actually not a great idea to try and trace this INT 3 even if you modify the BCHK value before. You could settle for changing the 'BCHK' value in EAX to something else before its moved into EBP and then a bpmb someway past the INT 3 or you could HEX edit Armadillo so that the XOR which retrieves 'BCHK' is slightly different, alternatively as per Method 1 you could patch your SoftICE against this trick.
Search for the sequence 'KHCB' and you'll be looking at this very interesting routine.
.D105 CMP EBP, 04243484B <-- 'BCHK'.
.D10B JE 0007FAC1 <-- Far jump.
.D111 CMP SI, 04647 <-- 'FG'.
.D116 JNE .D142
.D118 CMP DI, 04A4D <-- 'JM'.
.D11D JNE .D142 <-- The INT 3 'FGJM interface is here too.
In this instance we'll patch just the 'BCHK' interface by modifying the 'BCHK' reference to something else, say BCHR (use your imagination). You should now be able to run Armadillo untroubled. Lets have a little read of the documentation, firstly we'll need our program to protect, say Start Menu Cleaner v1.2 - Note also that Armadillo doesn't seem to be a conventional packer, more of a wrapper. The registration system you can attach is 64-bit so won't be any fun to break, yet lets see how rigidly it is attached to our original program, it might well be possible to remove the wrapper (and thus the registration system) altogether.
Its probably a good idea to peruse the help file before creating your first Armadillo protected program, the text is actually very lucid and easy to follow (if a little patronising in places). When we are ready to protect our program we'll break in on SoftICE at the point at which our startcln.exe is accessed (see below for more about this). After protecting our program the file size has jumped from 31k to near 95k. Lets run it and see what we can observe.
i) When we run our program a temporary file is created, these are assigned names pseudo-randomly Armxxxx.tmp, if you disassemble this file you'll see that this is really a dll. A quick look at the StringRef's ought to give away its function. You can probably also see which jump you'd like to reverse to get the "Key Valid" message :-).
ii) Another file named original_name.TMP0 is created. Take a really good look at this one, its an absolute gift :-), this file is virtually an exact replica of the original unprotected file barring the .text section (which is filled with 58h) and some very minor PE changes. Even the entry point and resource/import tables are all present. When we quit, this file is deleted.
Our task would therefore seem simple. Find the original .text section in unencrypted form, dump its correct raw size using IceDump, then simply paste the dump on top of the .TMP file created by the protected program and eliminate Armadillo completely. Our reminder is created with a call to DialogBoxParamA, we'll follow from the clicking of the OK button what happens after.
As I set about following my own advice (tracing from the DialogBox) I realised this is a very tedious and pointless task indeed as you'll probably not learn anything substantial doing so. I'd probably divide the unwrapping process into 4 distinct phases, the first is a long trace through calls to SetEnvironmentVariable which as you might expect sets up all those Armadillo internal variables such as DAYSLEFT, USESLEFT etc. The second stage executes the time-trial check, and its pretty good too, lots of subtle arithmetic tricks and checking of dates of other Windows files using CompareFileTime.
The third phase is based heavily on CreateFileA & WriteFile and sets up the temporary .TMP0 file, filling the .text section with 58h. The fourth phase is what I was most interested in, a call to WriteProcessMemory takes 0x3912 as nSize (the size of the .text section) and its here we can simply dump the memory pointer lpBuffer from SoftICE to a file and effect a virginity restoration (you'll benefit if you look at this as a BoundsChecker log).
So with Identify! v2.0 it sounds very simple indeed, but it isn't so :-), Armadillo also has a checkbox named Disable Debug/CopyMem protection which in the example above I left checked, lets see what effect this has on our preliminary technique above by running this through BoundsChecker too. Everything looks identical up until CreateProcessA and then things really differ, the key things to note are the CreateThread, Sleep, WriteProcessMemory & ResumeThread calls, with this option enabled our earlier technique will not work.
So all Armadillo is really doing is executing our thread in a suspended state, you'll see that WriteProcessMemory is called 4 times (1000h per call) to write the real code. This of course is merely a minor inconvenience, below I present the code from Identify! v2.0 (0x3F800 or 260,098 bytes need to be dumped). Just use a good HEX editor, say isn't UltraEdit just such a thing :-), the amount of bytes can be found several lines before this snippet.
0137:00401C9F PUSH EAX <-- Write from here (lpBuffer).
0137:00401CA0 PUSH EBX <-- Write here (lpBaseAddress).
0137:00401CA1 PUSH DWORD PTR [EBP+08]
0137:00401CA4 CALL EDI <-- WriteProcessMemory().
0137:00401CA6 TEST EAX,EAX <-- Check API status.
0137:00401CA8 JZ 00401C5C <-- Jump API failed.
Running our newly unwrapped Identify.exe we see from the about that our trial has so say expired, yet we know it hasn't really (or is it :-) ), otherwise you wouldn't be able to run the program. Perfectionists might like to null the legacy 58h bytes left at the end of the .text section, you could also disassemble the program if the mood takes you. If you feel so inclined, you might like also to unpack Armadillo itself.
--------------------------------------------------------------------------------
Packers Return to Main Index
--------------------------------------------------------------------------------
13th December 1999.