21 Nov

Running WinDbgX on Windows 7

Motivation

Main reason for writing this blog-post is the extremely crappy article by Vallejo named "Installation and First Contact With the New WinDbg". I read it, cried for a few minutes and decided to fix it.

Mandatory XKCD reference:
Someone is wrong on the Internet

Having said that, let's go through some of the most "brilliant" Vallejo's statements!

Installation

We execute WinDbg from installation shortcut and we search the main process.

Dude, when your article is called "Installation and first steps..." shouldn't you start at the beginning and tell us where to get this app and how to install it?

You need to get the app from the Windows Store: https://www.microsoft.com/store/apps/9pgjgd53tn86.

No, there is no real technical reason for that, just another attempt of Microsoft to convert you to Windows 10 and make you use their Windows Store.

To make matters worse, you need to have installed the latest and greatest update of Windows 10 to do that. There is no technical reason for that, either.

After you've jumped through all those hoops, you get this nice and shiny Windows Store app. Windows Store apps get installed under "C:\Program Files\WindowsApps\" and this one is no different. At the moment of writing the application version was 1.0.16, so it got installed into "C:\Program Files\WindowsApps\Microsoft.WinDbg_1.0.16.0_x86__8wekyb3d8bbwe".

Reparse point

The installation creates another exe here: C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\WinDbgX.exe. It is zero bytes, and if you try, for example, to copy it, you can’t.

Because it's a reparse point, not an EXE file.

Windows 10 processes bundled Windows Store application's AppxManifest.xml and creates appropriate appExecutionAlias'es:

Same thing goes for all other applications Vallejo "found" under C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\, like dbgsrv64.exe:

You can read more about aliases on MSDN: Start your app by using an alias.

Fsutil

I have not found a tool or way to manage or get information about these files.

Ever tried fsutil? It's been part of Windows since Windows7.. bigsmile

Here's output of fsutil reparsepoint query WinDbgX.exe:

As you can see, it's a reparse point with a tag 0x8000001b (IO_REPARSE_TAG_APPEXECLINK).

Sure, you can Google for that - but it won't tell you much, except that it's not really documented and should be left alone.

Command-line

Old windbg.exe accepted parameters with “-“, for example -k. New Windbg needs /k parameter to pass the connection configuration

Bullshit! In fact, WinDbgX accepts any of 4 different delimiters: "-", "–", "—", "/" and combinations of those..

Ok, that was enough criticism for one day. Let's do something more constructive!

Running WinDbgX on Windows 7

Remember how I said that there is no technical reason why WinDbgX should be available only on Windows 10 and only as a store app? There really isn't. smile

Here's WinDbgX running on my Windows 7 and debugging one of the FLARE2017 crackmes:

It's actually a really simple fix.

  1. You need to copy all the files from your Windows 10 machine to your other machine (Windows 7 in my case). It's as simple as selecting all files in "C:\Program Files\WindowsApps\Microsoft.WinDbg_1.0.16.0_x86__8wekyb3d8bbwe" and copy-pasting them. Don't worry about the reparse points in C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\, we'll fix that later.
  2. If you try to run DbgX.Shell.exe on Windows 7, it will fail with Exception:
  3. Let's look at that code in DbgX.dll using dnSpy:

    It's crashing on the Package.Current.Id.FamilyName, as this function is available only for Windows Store apps.

    As a simple hack, we can replace this call with an empty string. Better hack would be to use the proper folder based on the actual WinDbgX path. But the simple way will do for our demo..

  4. Using "Edit IL instructions" function in dnSpy, replace first 4 instructions with ldstr and nops:
  5. Save the module. If saving fails, remove read-only attribute from DbgX.dll and try again.
  6. Since we chose a simple hack, create folder C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\
  7. Depending on your OS (32- or 64-bit), copy files from WinDbgX\X86 or WinDbgX\amd64 folder to C:\Users\<user>\AppData\Local\Microsoft\WindowsApps\. Rename dbgsrv.exe to dbgsrv32.exe or dbgsrv64.exe accordingly.
  8. Run the DbgX.Shell.exe shell now. It will work just fine!

There are 3 more places in DbgXUI.dll, DbgX.Util.dll and Extensions\DbgX.External.dll that might need similar fixes. But that's behind the scope of this article.

Conclusion

It is true that classic WinDbg looks really dated. So, I can totally understand why Microsoft would want to create a replacement with a better UI. However, WinDbgX falls short on everything - its installation via Windows Store is brain-dead stupid, its user interface is confusing (who would look at Model->Change Query to change debugger settings?!) and severely limited (no multiple Memory windows, seriously?). If it was a school project, it wouldn't get even a B-. But for some reason, Microsoft insists that this is the only way forward. Oh, well.. sad

At least, the DLLs are not obfuscated, so someone can take them and make a much better UI.. wink

Have fun!
kao.

04 Oct

October update to Molebox unpacker

Thanks to my reader Max I have fixed another bug in the Molebox unpacker.

  • Removed memory leak that caused "out of memory" error when unpacking very large files (1.5GB+)

The usual request

I hope you find this unpacker useful. But if it doesn't work for you, please send me an error report with all the details you can and I'll try to fix it. Have fun!

Download link: https://www.mediafire.com/?pd555sk18yj3wl3
Alternative link: https://mega.nz/#!thYFDCCB!EO5-BQRr_idpJKXm6c_82YrcIEadpXCB7M_tC4cZ7nA

22 Aug

Yet another update to Molebox unpacker

Thanks to my readers I have fixed few more bugs in the Molebox unpacker.

  • Better support for non-ASCII symbols in filenames
  • Very large (4GB+) files will now unpack correctly
  • Unpacker will extract embedded files packed with old versions of Molebox (like version 2.0570)

The usual request

I hope you find this unpacker useful. But if it doesn't work for you, please send me an error report with all the details you can and I'll try to fix it. Have fun!

Download link: removed. See October update to Molebox unpacker for an updated version

19 Aug

Use of syscall and sysenter in VMProtect 3.1

Few days ago Xjun briefly mentioned a new feature of VMProtect 3.1 - it uses direct syscalls to check if software is running under debugger. I decided to take a quick look myself and figure out how exactly it works.

I'll give you 2 targets for the research:

  • oppo_flash_tool.exe (https://mega.nz/#!ZgJzjQxR!cNEHMwM-jKnLVgPXf4OUupyk1DNt69FYB2rEfY-5AlA) - it was given as an example by Xjun;
  • asshurt.dll (https://mediafire.com/?3xyc0ugc2hxervn) - some sort of a cheat for Roblox. I don't care about the cheat itself, it just happened to have the syscall feature enabled. And it uses different syscalls than Oppo.

In addition to that, I'll provide a very simple demo executable which replicates part of the VMProtect protection, so that you don't waste time looking at obfuscated code.

As a debugger in 32-bit OS you can use anything you like. On 64-bit OS you will really need to use WinDbg - as far as I know, it's the only debugger that can handle those tricks..

32bit OS

Let's start by debugging oppo_flash_tool.exe. First, we need to get past the usual tricks like IsDebuggerPresent and CheckRemoteDebuggerPresent. If you're reading this, I'm sure you know how to do that.

Few moments later we'll arrive here:

Remember this conditional jump. It's taken on 32bit OSes and not taken on 64-bit OS.

Let's look at 32bit OS version first. Now VMProtect prepares to call sysenter.

Since the code is obfuscated, here comes a cleaned-up version. Please note that in other applications, different registers can be used.

64-bit OS

Here it is getting interesting! smile You cannot use sysenter instruction from 32-bit code in 64-bit Windows. But, as ReWolf described few years ago, one can mix x86 code with x64 code in the same process. And that's exactly what VMProtect 3.1 is doing.

Let's go back to that conditional jump and see what happens in 64-bit OS. The jump will not be taken:

Far call?! Last time I saw that was in 16-bit Windows era..

As explained in ReWolf's article:

Summing things up, for every process (x86 & x64) running on 64-bits Windows there are allocated two code segments:

  • cs = 0x23 -> x86 mode
  • cs = 0x33 -> x64 mode

So, as soon as you execute that call, you'll switch to a 64-bit world. WinDbg happily recognizes that, all other debuggers just go astray..

x64 code does pretty much the same thing as x86 code - sets up a stack frame, sets up registers and then executes syscall instruction. Cleaned-up and shortened version follows:

You'll notice that x64 version is slightly more complex due to the way parameters are passed (registers vs. stack). It also includes a special treatment for 8 special edge cases - it will modify syscall parameters to adjust buffers and pointer sizes to satisfy requirements for 64-bit code.

NOTE - to keep code simple, I only showed the part which deals with NtQueryInformationProcess but other cases are similar.

As you can see, return back from x64 to the x86 world is a simple retf instruction. x86 code continues right where it left off:

Instruction at address 0x00ddaeba is the same for both x86 and x64 OS-es and VM continues as usual.

Different protection modes and syscalls

I provided you with 2 real-world test executables. Oppo seems to be simpler and use just 3 syscalls:

  • NtQueryInformationProcess with ProcessDebugObjectHandle class
  • NtSetInformationThread with ThreadHideFromDebugger class
  • NtProtectVirtualMemory to set protection attributes for each section in original executable

Asshurt doesn't have antidebug trick with NtQueryInformationProcess but it uses additional syscalls for some purposes:

  • NtOpenFile
  • NtCreateSection
  • NtMapViewOfSection
  • NtQueryVirtualMemory
  • NtUnmapViewOfSection
  • NtClose

Suggested workaround

Since VMProtect is using undocumented Windows features, it somehow needs to ensure that the protection will work on each and every Windows version. That's VMProtect's biggest strength and also the biggest weakness.

Windows' syscall numbers change in each version and also between major builds. Use the wrong syscall number and you're guaranteed to receive unexpected results. So, VMProtect developers had to hardcode a table with Windows build numbers and corresponding syscall id's in the executable.

You can see the syscall numbers in the j00ru's page (slightly out of date) or in tinysec's windows kernel syscall table

To obtain Windows build number, VMProtect uses information from PEB (Process Environment Block). The method is already described in The MASM Forum, so I'll just reproduce the (ugly) code from their page:

VMProtect checks only the build number and picks the corresponding syscall number. However, if the build number is not in the internal database, it will not use direct syscall and fall back to standard protection. Bingo, problem solved - no need for ugly hacks like Xjun's SharpOD plugin!

Hint: VMProtect 3.1 doesn't support Windows 10 Creators Update (build number 15063).

Demo time

As promised, here is a download link for the test application: https://mediafire.com/?niqqbs0fqcq8n23
Note: it should support most common builds of Windows XP/7/8.1/10. Windows 2003/Vista and other rare systems are not supported!

If it shows "OK" message, you've hidden your debugger well. If it shows "Debugger detected", you have a problem. smile

Have fun!
kao.

EDIT: Updated download link for Oppo. Mediafire's antivirus tends to have plenty of False Positives..

02 Jun

Enigma’s EP_CryptDecryptBuffer internals

For one of my private projects, I needed to decrypt some data. Original executable uses Enigma's EP_CryptDecryptBuffer function but I needed to implement the same thing using .NET. Much to my surprise, there was no information about what encryption algorithm is used by Enigma or how it works internally. So, I started by compiling Enigma's sample project CryptBuffer and debugging the executable.

TL;DR - IDEA cipher in CBC mode, password = MD5(user_password), IV for CBC comes from IDEA.Encrypt(8-zeroes, password).

Research

After protecting my executable with Enigma, few assembly lines

got changed into something horrible that looked like an Enigma's VM:

I didn't feel like spending my time on analyzing that. Much easier solution was to put hardware breakpoints on my data and password and wait for them to trigger. In a few seconds I landed into code that looked like MD5 (notice the constants!):

and few hits later I landed into code that I didn't recognize at first:

Delphi compiler had helpfully included RTTI information telling me that this second piece of code belongs to a class called TIdeaCipher. Now I had all the information I needed, I just had to put it all together. smile

Implementing decryption in C#

There aren't many IDEA implementations in C#. In fact, there are 2: by BouncyCastle and by LexBritvin. Since I'm not a big fan of huge and complex libraries like BouncyCastle, so I took the simplest possible code: from Github and modified it a bit.

First, we need change password generation algorithm to use MD5 to generate real password:

Second, we need to add support for CBC mode. It requires both calculating correct IV value and pre/post-processing each block we're encrypting or decrypting.

Calculating IV is quite simple:

Processing each block is slightly harder but not a rocket science either. As explained in image borrowed from Wikipedia:
.

Before decrypting the data block, we need to store the encrypted values as IV for the next block. After decrypting the block, we need to XOR each byte of data with a corresponding byte of IV. Unfortunately, during encryption the data flow is different, so the simple crypt function has to be replaced with 2 functions: encrypt and decrypt.

Few more cosmetic changes and that's it! We got clean and nice implementation of EP_CryptDecryptBuffer in pure C#. smile
Download link: https://bitbucket.org/kao/ep_cryptdecryptbuffer/

Have fun!

P.S. My implementation only supports cases where data length is divisible by 8, adding support for other lengths is left as an exercise for the reader.

12 May

VMProtect and dbghelp.dll bug in export processing

If your Olly is crashing when loading executable protected by VMProtect, you most likely have outdated dbghelp.dll somewhere on your path. Grab the latest version from Microsoft and put it in the Olly folder.

Well, that might be enough to work around the issue that I had - but I still wanted to know what's causing the crash.

Cause of the problem

If you try to debug Olly with another Olly, you'll see the Access Violation happening somewhere in dbghelp.dll:

Check register values in Olly:

For some reason, value in EDX is garbage and therefore access violation happens.

Call stack doesn't tell us much:

And same piece of code in IDA doesn't help much either:

So, it's debugging time! Set breakpoint to start of LoadExportSymbols, then set hardware breakpoint on write to address [ebp+ptrAllocatedMemory].

First hit is initialization of variable with 0:

Second hit stores the address of allocated memory:

And third time is a charm:

Good folks at Microsoft have left us with a nice buffer overflow. exportFunctionName is defined as byte array of size 2048 bytes. Any exported function name longer than that will cause stack overflow and (possibly) subsequent crash.

010Editor with PETemplate confirms that the export name is indeed very long (3100 chars):

From what I can tell, it's a similar (but not the same) bug to what was described by j00ru at http://j00ru.vexillium.org/?p=405 (see "PE Image Fuzzing (environment + process)")

Stay safe!

P.S Here's an example file, if you want to test your Olly: https://forum.tuts4you.com/topic/38963-vmprotect-professional-v-309-custom-protection/
P.P.S. CFF Explorer, HIEW and IDA do not show us any exports in this example file - but that's a matter of another story..

21 Apr

Updated Enigma VirtualBox unpacker again

This update has been long overdue. Finally it supports files larger than 2GB! smile

Full changelog:

  • Supports files larger than 2GB. Yeah!
  • Correctly recognizes EnigmaVB 7.50-7.70;
  • You can use command-line EnigmaVBUnpacker.exe /nogui [pathToFile] to unpack file, save results to !unpacker.log and close automatically.
  • NEW: fixed "Error creating temporary file"

Hopefully I didn't break anything during the rewrite. But if I did, send me an email and I'll fix it! smile

Download link: https://www.mediafire.com/?phu1lxocl3r1b2x

EDIT 2x: Very stupid error fixed. /me embarrassed. Sorry.

09 Mar

Recovering data from faulty HDD

I'm extremely lucky. In my 15+ years of messing with computers, I've never lost data due to HDD developing bad blocks and dying. Never! smile

Other people are not that fortunate. So, last weekend I was asked to look at an Acer laptop that just won't start. Windows startup screen shows up, stays for 5-10 minutes and computer reboots. Safe mode doesn't start, Alt-F10 Acer Recovery Console won't show up, nothing. At least I got Windows Memory Diagnostics to show up - and it didn't find anything wrong with RAM.

After I disabled Automatic Restart on System Failure (and waited 10+ minutes for Windows to crash), I got this nice error UNMOUNTABLE_BOOT_VOLUME (STOP: 0x000000ED):

Considering how much time it takes to get to the error, it's probably a bad hard disk.

Disclaimer: data recovery is a very delicate science. If you value your data, I suggest that you use a specialized data-recovery service. But if you are short on cash or just want to have some fun with dying HDD, please read on! Just remember that each HDD issue is different and what worked for me might not work for you.

Disassembly time!

I removed 2 screws to get access to HDD. First thing I saw was this huge scratch all over HDD bracket and cover plastic.

Apparently Mr.Awesome Neighborhood PC Repair Dude has tried to remove HDD with a screwdriver and failed. He had also broken few plastic clips on HDD cover - but who cares about those, right? At least, he did no visible damage to the electronic parts of HDD. smile

Let's try to attach disk to another PC and see if it's really bad.

Windows hates bad disks

Let me tell you, attaching it to my Windows computer was a bad idea. When disk was plugged in, Windows took 5 minutes to start. Any program took 1-2 minutes to start. To be honest, I have no idea why Windows were acting so weirdly, but hey, kids, don't try this at home! smile

At least I got an output from Crystal Disk Info which confirmed my suspicions - bad HDD:

On the side note, Internet is full of really stupid advices. If you suspect that your disk might be physically damaged and dying, never ever use "chkdsk" or similar tools on it! They will likely fail and/or corrupt your data even more. Make a full disk copy and try to fix data there.

Lesson learned - don't use Windows if your HDD is dying. Linux is much safer and data-recovery friendly!

Clonezilla

After some Googling, I found Clonezilla. It's a free Linux-based software that helps with disk imaging/cloning. Reviews were nice, so I made a bootable USB with Clonezilla and tried it out.

It failed.

After enabling "Expert options" and enabling ––rescue flag, it started to do something. However, estimated completion time of 40+ hours wasn't exactly exciting. Apparently, Clonezilla/partclone is slow! I'd love to have a solution that actually works, preferrably today.

Ddrescue and open-source stupidities

Few more Google searches later I learned about ddrescue. It's yet-another-Linux-software that can do almost anything - iff you can master its arcane command-line arguments. As their "manual" tells it succinctly:

This tutorial is for those already able to use the dd command. If you don't know what dd is, better search the net for some introductory material about dd and GNU ddrescue first.

Dude, I AM reading the ddrescue manual. What other introductory material about ddrescue should I search for? sad

Since ddrescue is included in clonezilla USB image, I launched bash and tried the simplest possible version:

It failed with error "Can't open input file: Permission denied". Apparently, you need to use sudo. My next attempt was actually successful!

So, here we are, after 5 hours of running.. Estimated remaining run time is 25 minutes and it has recovered everything but 100MB of data from the HDD... Fingers crossed!

18 hours later my fingers were still crossed.. WTF?

Well... Hidden in the ddrescue manual is this great note:

The 'remaining time' is calculated using the average rate of the last 30 seconds and does not take into account ... Therefore it may be very imprecise, may vary widely during the rescue, and may show a non-zero value at the end of the rescue. In particular it may go down to a few seconds at the end of the first pass, just to grow to hours or days in the following passes.

Holy fuck, why on earth would you show "remaining time" if you very well know that it's "very imprecise"? Does it make your program go any faster? No. Does it help your user in any way? No. It just pisses everyone off.

All in all, ddrescue ran for around 48 hours - recovering 99.98% of data. There were still 45MB of non-scraped data left but I decided that it's not worth to wait 40-50 more hours to rescue mere 20-30 megabytes.

Lesson learned - reading data from unreadable sectors is really slow. Prepare to wait for days!

Analyze the rescued image

Recovering data is great. But what to do with the 0.02% of data that were unreadable? ddrescue log can tell you that sector 0x12345000 was unreadable - but you will have no idea which file occupied that sector. Since I'm a Windows guy, I decided to modify ddrescue's suggested approach a bit and used Windows tools when possible.

First, run ddrescue with ––fill-mode argument:

It will take the image file and mark all unreadable sectors with "BABEC0DE" and relevant sector/position information based on the log file. The affected part of file will look like this:

You can pick whatever text you want - I didn't want to use suggested "DEADBEEF" constant, as it is much more commonly used and might actually appear in some valid files.

Second, reboot into Windows and use OSFMount to mount the created hdimage.img:

Finally you can see files and folders from the damaged disk. Now use whichever Windows tool you like to search for "BABEC0DE". In my case, there were 16 files affected - 12 videos and 4 log files. So, nothing of value was lost! smile

Write the rescued image to the new hard drive

If you have Acronis or other Windows cloning software, you could use that to write HDD image to new disk. Since I didn't have any, I use Clonezilla's bootable USB and Linux standard dd command:

After an hour and a half all the data were transferred to the new disk. Now I just needed to put HDD back into the laptop, boot up the system and run chkdsk to make sure that everything is fixed.

After 3 evenings and plenty of swear words, it's a great success! smile

Final words

There are two kinds of people, those who back up their stuff and those who have never lost all their data. Be smart and make sure you have proper backups! Otherwise, be prepared to spend few evenings learning Linux disk management tools and cursing their command-lines.

Till next time!

06 Feb

Updated Molebox unpacker

During last year, the most common complaint on this blog was "your Molebox unpacker cannot unpack this crazy big EXE of MMORPG game X, Y or Z."

Sounds like an easy problem to fix, right? Well, that's not true - but I finally did it!

TL;DR - new version of unpacker is here: https://www.mediafire.com/?t3xw46s554it5fp

In the rest of the post I'll describe the obstacles I had to overcome while solving this seemingly simple problem.

Delphi TMemoryStream limitation

First, unpacker is written using classic (non-.NET) Delphi and compiled as x86 executable. And standard Delphi streams are retarded. TMemoryStream uses GetMem - which ends up somewhere in Delphi memory manager and VirtualAlloc. That doesn't work well with 800+MB files.

I ended up with implementing custom stream backed by temporary file (CreateFile with FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE).

Pointer magic

Since the original unpacker was using TMemoryStream, the whole file was loaded in memory as a continuous memory block and I could easily read/write each byte of file using pointers. Something like this:

Guess what? Files don't work that way.. sad I had to do a full and complete rewrite of all those methods.

Zlib conflicts

Original unpacker was compiled with Delphi 3. Delphi 3 didn't have Zlib library, so you had to supply your own Zlib implementation. But it won't compile with new versions of Delphi, giving error message

Unit Graphutil was compiled with a different version of zlib.TZDecompressionStream.

The solution was to get rid of my Zlib implementation and rewrite all methods that deal with decompression.

ANSI strings

In Delphi 3 all strings were ANSI. Starting from Delphi 20072009, strings are Unicode. Since Molebox internally uses ANSI, it required changing quite a few structure definitions and rewriting several string manipulation routines. It's a lot of fun (and source of the most obscure bugs), trust me!

Conclusion

I hope you find this unpacker useful. But if it doesn't work for you, please send me an error report with all the details you can and I'll try to fix it. Have fun!

Download link: see October update to Molebox unpacker for an updated version.

02 Dec

Deobfuscating AutoIt scripts

Every once in a while, someone posts an interesting challenge concerning protected or obfuscated AutoIt scripts. Today I'd like to show some basic approaches to AutoIt deobfuscation. As a target I'll use a very simple protection called AutoGuardIt and the crackme from Tuts4You thread. If you don't have access to Tuts4You, here is the alternative download link: https://www.mediafire.com/?qs52emp7tkk472g

In general, there is nothing hard in decompiling AutoIt scripts. The Autoit script interpreter is designed in such a way that it's really easy to convert P-Code back to the script form. There's also a tidy.exe utility which takes ugly hand-written script and reformats it to make it really pretty. All of this makes writing deobfuscators much easier because you can start with well-formatted AutoIt script and your deobfuscator can consist of simple regexps and string replaces. It will not be very pretty code but it will work.

While I was preparing this blog post, SmilingWolf came up with a full-featured solution written in Python. It's a nice solution but it doesn't explain how or why it works. So, in this article I will explain how the protection works, show the basic techniques and sample source code to defeat each of the protection steps. Making a full-featured deobfuscator is left as an exercise for the reader.

Required tools

  • C# compiler. All my examples were tested under Visual Studio 2010 but any recent version should do
  • MyAutToExe. I'm using my personal modification of myAutToExe. You can download it from Bitbucket: https://bitbucket.org/kao/myauttoexe
  • Tool for testing regexps. I'm using http://regexr.com/
  • Some brains. You can't become a reverser if you can't think for yourself.

Decompiling the script

There are 2 public tools for extracting compiled AutoIt script: MyAutToExe and Exe2Aut.

Exe2Aut uses dynamic approach for obtaining script - it runs the file and gets decrypted and decompressed script from process memory. That's usually the easiest way but you really don't want to run the malware on your computer.

MyAutToExe uses static approach - it analyzes file and tries to locate, decrypt and decompress the script on its own. That's more safe approach but it's easier to defeat using different packers, modified script markers and so on. To extract script from this crackme, I used my own MyAutToExe (see "Required tools" section above).

Analyzing the obfuscation

Once the script is extracted and decompiled, it looks quite strange and unreadable:

Let's look at each of the obfuscation techniques and see how it works and how it can be defeated.

Integer decomposition

AutoGuardIt takes constants and converts them to series of math operations. Example:

Deobfuscator should be able to take the expression, evaluate it and replace the expression with the correct value.

The biggest problem here is the precedence of operations (multiply and divide should be processed before addition and subtraction), so you can't start from the beginning of line and do it one step at a time. This would be wrong:

After some thinking and few Google searches, I found a LoreSoft.MathExpressions library that does all the heavy lifting for me. smile

The following C# code snippet will find all math expressions, extract them, evaluate them and replace expression with the actual value:

Pseudo-random integers

This is quite strange protection that relies on a fact that AutoIt's Random function is actually pseudo-random number generator. If you seed it with the same seed, you get the same results. Example:

In general, it's a very bad idea because there's no guarantee that random number generator will not change in the next version of AutoIt. But for now it works..

Since I was already using myAutToExe, I decided to use RanRot_MT.dll from the package.

a = StringLen("xyz")

Small integers can be obfuscated by using function StringLen:

To clean them up, a simple regex can be used:

End result:

Chr(x)

Some strings in the executable are split into bytes, and each byte is then encoded as call to Chr function:

Another simple regex will kill all of those:

The result will be valid but still hardly readable string:

And one more simple search-replace will fix that:

End result:

If 1 Then

Once you remove the integer obfuscations, you'll see a lot of useless statements like this:

This condition is always true, so we can remove both If and EndIf lines and improve readability.

The problem here is that If's can be nested and you can't just remove first EndIf that you encounter. Consider this example:

Taking all that into account, I came up with this ugly but working* code:

* - see below for some scenarios where this code might fail.

If a = a Then

Variation of previous protection. In such cases, using regexp is much more efficient than simple string comparison

Do Until 1

It's pretty much the same protection as If 1 Then and it can be defeated the exact same way.

While 1 / ExitLoop / WEnd

Another protection of the same kind, just uses 3 lines of code instead of 2. Same approach, just make sure you match the correct lines and remove all 3 of them.

For $random=0 to 123.456 / ExitLoop / Next

Another protection, very similar to previous ones.

Here one must be very careful not to remove the real for cycles from program, so it's better to use regexps. Apart from that, it's pretty much the same code again.

Assign/Execute

This type of protection relies on AutoIt's Assign function. First, an alias to a function is defined:

Later, alias is used to call the function:

Deobfuscation is a simple operation: find all calls to Assign, extract the variable name and the function name, then replace all references to the variable with the function name:

BinaryToString

As you can see in the example above, some strings in the script are replaced with calls to BinaryToString. Here's a another example of the same protection where part of code is replaced with BinaryToString + call to Execute.

Merging all 3 lines into one and converting hex strings to bytes gives us the following code:

which using the methods described earlier can be deobfuscated as:

Functions returning constants

Some strings are not only encoded using BinaryToString but also moved to a separate function.

Deobfuscated code will look like this:

It's quite tricky to find the correct return value for each function and replace function calls with correct values. In addition to that, regexes aren't really suitable for such tasks. smile The code I wrote is really ugly, so I'm not going to show it. Go, figure it out yourself! smile

Switch control-flow obfuscation

This is actually the hardest one of them all. The example code looks like this:

You must find the initial value of the variable. Then you must find the correct start/end of the Switch, all the switch cases and all other assignments to the control variable. Then you'll be able to reorder all the code. It's a moderately hard problem for which I don't have a pretty solution. smile

Here's my code which seems to work:

After cleanup, the deobfuscated code looks like this:

Unused variable assignments

There are random variable assignments sprinkled all over the code.

They are only assigned once and never used. To clean them up, one can locate the assignments using regex, count how many times this variable appears in the code and remove it, if it's only assigned once. Something like this:

You can remove string and integer assignments using very similar regex.

Lather, rinse, repeat

If you run each of the mentioned deobfuscations only once, you'll end up with half-deobfuscated code. Also, there isn't one specific order in which the deobfuscations should be applied. Of course, you could just run the entire loop 100 times, but it's just ugly.

Therefore, I prefer to run the code in the loop like this:

It will run all the deobfuscations until there's nothing left to clean up. Then run tidy.exe on the output and you'll get a nicely readable script.. smile

Possible problems and gotchas

Deobfuscators based on string matching are very easy to implement. However, one must be very careful to write correct regular expressions and string comparisons. My example code works very well on this specific crackme. However, it will mess up with code like this:

You can work around such issues by using more specific regexes with anchors. During my research, I used http://regexr.com/ a lot, and I find it really helpful. Give it a try!

Conclusion

In this article I showed basic approaches that can be used to deobfuscate (not only) AutoIt scripts. They are extremely simple and work well for one-time tasks. For more complex tasks, deobfuscators based on abstract syntax trees or disassembled P-Code are much more efficient but more time-consuming to create.

Have fun!