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 https://forum.tuts4you.com/topic/39017-autoit-crackme/?do=findComment&comment=187457 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

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:

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:

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:

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

If 1 Then
$LOADERHARDWAREIDNULLA = 336.785775121767
EndIf

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:

If 1 Then <-- we start here.
For $IBINARYDECLAREA = 0 To 826.403731859988
If 1 Then
; do something useful
EndIf <-- this is first endif we see, but it's not the right one.
ExitLoop
Next
EndIf <-- this is endif we're looking for

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

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 <i>BinaryToString</i> + call to <i>Execute</i>.

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. The code I wrote is really ugly, so I'm not going to show it. Go, figure it out yourself!

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. Here's my code which seems to work:

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.

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..

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:

If 1 Then
$A = "If at first you don't succeed, try, try again." <-- Cleaner will find string "If " and mess up counts.
EndIf <-- So this EndIf will not be removed.
... <-- Cleaner will find another "EndIf" in code below, or just crash

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 https://en.wikipedia.org/wiki/Abstract_syntax_tree abstract syntax trees or disassembled P-Code are much more efficient but more time-consuming to create.

Share this post

Link to post

SmilingWolf 263

SmilingWolf
263

While waiting for @kao's paper here is my solution. Full loosely commented Python 2 sources, readme file, even almost all of the garbage files left in place (minus the ones we don't need created by mATE).
EDIT: check my last post for all the half assed crap sexy goodness.