Hello all. Welcome back to part 7 of the Autist’s Guide to Malware Analysis.
Last paid post went over some patchwork, but I would like to expand upon that subject a bit more. I’ll once again walk through some programs that warrant patchwork that only the paid subscribers can see.
When/Why Do You Patch Programs?
Rewriting instructions in already-compiled programs is known as “patching”. Patching is used to alter program behavior to benefit the a reverse engineer (you!). Patching is quite difficult, as you must know the inner machinations of assembly to even be able to understand what you’re doing. I often reference this manual when I need to patch a program.
If you remember the very first post about malware analysis, I mentioned that computers can only ready 1’s and 0’s. However this is a bit of a misnomer. Those 1’s and 0’s are interpreted as bytes, which are then interpreted as instructions. In the manual that I linked above, you can see how the bytes are mapped to instructions. There’s also a bunch of other mappings like program sections and strings but we won’t worry about that for now.
You will want to patch a program when you need to force a certain behavior. This can include logical conditions, function removal, and even dynamically injecting code!
There are 2 types of patching. There is static patching, which we will walkthrough in this post, and there is dynamic patching, which overwrites memory while the program is running. You’ll often see static patching in torrents, cracked games/programs, etc. You’ll see dynamic patching in game cheats and wickedly good malware. Dynamic patching is fun to do, but that’s a post for another time.
Say, for example, a program has anti-debugger checks (like in the previous post). You don’t want that code running, so you would patch out the logic that executes those checks. You could do this through a few methods, either by NOPing (erasing the logic), reversing the logic, or by JMPing over the code. You can pick any of the three depending on the complexity of the logic that you are trying to patch.
I’ve written 2 programs that we will toss into a disassembler and patch over some logic with. Let’s begin.
On my Tor site, as per usual, there are 2 programs for you to download. These are under the “programs” folder since these aren’t specifically malware executables. They are both in the “patching” .zip file.
Let’s look at the first one, countdown.exe.
If you run the program, you are prompted with a countdown that will give you a timer that will give you a password in 1000 seconds. Obviously, nobody has time for that. We can patch it and make the timer shorter.
We are looking for an assignment of 1000 (or 999) to a variable which would then be decremented or checked against as the timer continues.
0x3E8 is equal to 1000 in decimal. You can change the hex interpretation by clicking on it and pressing ‘H’.
Odds are, this is the 1000 second timer. So we can patch it to 0.
Remember endianness. E8 03 00 00 equates to 0x000003E8 or 0x3E8. Changing this to 00 00 00 00 will give us a 0 second timer.
Edit → Patch Program → Patch bytes
Change the E8 and 03. Click OK.
Edit → Patch Program → Apply patches to input file.
Now, you can run the program and get the password. (Also, run it from a terminal as it will exit upon completion).
Also, you definitely could’ve gotten the password in plaintext if you scrolled down in the assembly. I did not feel like writing an obfuscation program since this is for educational purposes.
The next program is password.exe.
If you run it, you will be prompted with a password. Let’s try the one we just got.
That didn’t work, so we have to reverse engineer this one as well (duh).
For example’s sake, just assume those “You have entered…” strings are different logical functions.
Perhaps, that hex value is the password. Let’s try it.
That didn’t work. It must be some sort of hash. (This is std::hash which can be reversed relatively easily, but ignore that fact for now.)
The easiest thing that we can do is to reverse the logic that executes the functions. Therefore we can enter any password (that isn’t the actual password) and get the functionality that we want.
There is a JZ statement stands for “jump if zero”. So, we can just change this to JNZ or “jump if not zero”.
You should also note that the instruction for JZ, which is 74 in this case, stands for jump short. There is also jump near, but that is a 2-byte instruction. This is just a one-byte instruction. Using the manual, the one-byte JNZ instruction is 75. So we patch and change it to 75. Same method as before.
Now, let’s say, for example, this program checks this hash value over, and over, and over, and over. It would be a pain in the ass to have to change all of those JZ instructions each time. A better solution would be to change the expected value to something that we want instead.
I wrote test.exe which takes an input and produces the std::hash of the passed argument. Let’s use “asd” as our input.
Now, we already know that the password.exe program uses std::hash to check against the input. So, what if we changed the hash to our hash?
We can patch over more than just instructions. We can change string bytes.
In order to do so, we need to understand how C programs store strings. UTF-8 encoded strings are sent to what is called the “data” section. This is explained in Part 2 of my handbook.
Since we have our new hash, we can change the old hash. To reach where it is, double-click it from the assembly.
We need the ASCII of our new password. Just Google a text-to-ASCII converter. Then convert it to hexadecimal.
This gives us 31 64 37 32 63 65 63 62. Now, we patch over the string.
Note that you are limited in the amount of bytes you have to work with. Hashes have a fixed length, which assures that we have the space for our new password. Essentially, you cannot use more bytes that what you have.
Now, if we run the program and input “asd”, it will execute normally. (Obviously, undo your previous patch if doing this).
Thus concludes this post, hope you enjoyed.
Go!
-BowTiedCrawfish