Hello all. Today’s topic will discuss some more exploitation techniques. These techniques are usually found within video game cheats/mods but are also applicable to conventional software as well.
Before You Read
It is recommended to read these related posts before reading this one, so that you don’t get lost during the discussion of the concepts:
What
We’ve done function hooking and patching and other related exploitation techniques. However, there is a flaw with one of our methods. Maybe it isn’t quite a flaw, but it is something that could easily be defended against.
When finding the location of a function, we currently rely on the offset between the location of the function and the base address of the program. However, (this is the 3rd+ time I’m explaining this, hopefully the last) there is a concept called signature scanning AKA sigscanning.
A signature is a unique sequence of bytes within a program. You should know that there are bytes in a program that make up instructions, therefore a unique sequence of instructions constitutes a signature. Should the offset of a function, segment, location, etc. change when a program is recompiled, a signature, assuming it is robust, can be used to assure that the location of the function can be found. Some very complex packers can randomize code locations, rendering static offsets impractical.
Why
An important keyword is robust. Signatures are commonly created at the start of a subroutine (its prologue) as those bytes are less likely to change when a program is updated. Once you get the hang of signature creation, you can make signatures from practically any point in the program. I oftentimes make them near locations I want to patch/hook/etc. if the code sequence is unique and unlikely to change like below.
Making a signature by hand is relatively easy. All you need to do is copy several bytes (~5-10 depending on uniqueness) and ask your disassembler to search the entire binary for that byte sequence. If you only get 1 hit (the location you’re trying to scan for), then your signature is satisfactory. I have my own IDA Pro scripts that do all of that for me, though.
How
Let’s look at this subroutine:
If we wanted to make a signature, we would start at A1, then move along each byte and collect them into a signature like A1 68 CC AB 10 A8 01 0F 85 41 01 00 00.
But wait, the first instruction MOVs a value from the .data section into EAX. The MOV instruction takes an address from a static offset and moves its value into a register. Should the program be recompiled, a signature that contains the 68 CC AB 10 will undoubtedly be wrong.
To overcome this issue, the bytes in question will be designated as wildcards. This means that during the scanning process, the signature scanner will ignore these bytes and move on. Wildcards are usually represented by a ‘?’ within the mask, which is a set of characters used in conjunction with the signature to indicate the presence of wildcarded bytes. Bytes that should be included/checked within a signature are commonly represented by ‘x’ within a mask.
There’s another similar issue in the assumed signature at bytes 0F 85 41 01 00 00. Bytes 0F 85 stand for “jump near if not zero/not equal”. Same thing as before, take the last 4 bytes. But hang on, the last 4 bytes get us 00 00 01 41. This clearly isn’t an address, so what is it?
JMP instructions take a relative address. This means that, when executing the instruction, the program takes the current address (from EIP), and adds it to the relative address, which in this case 0x141. If we add the current address 0x1049545D (+6 because of the size of the instruction sequence) by 0x141, this gets us 0x1049559E, which is the start of another part of the subroutine.
Should the code within that this function change, the signature runs the risk of having this byte sequence change, thus breaking the signature. The last 4 bytes of 0F 85 41 01 00 00 should also be wildcarded. If we continue in this manner and generate a unique signature, it will be:
A1 ? ? ? ? A8 01 0F 85 ? ? ? ? 6A 00 6A 00 6A FF 6A 04 6A 00 83 C8 01 68 ? ? ? ? 68 ? ? ? ? A3 ? ? ? ? E8 ? ? ? ? 68 ? ? ? ? 6A 00 68 04 01 00 00
Or, split apart into a signature and mask:
A1 68 CC AB 10 A8 01 0F 85 41 01 00 00 6A 00 6A 00 6A FF 6A 04 6A 00 83 C8 01 68 EC C3 75 10 68 E8 C9 AB 10 A3 68 CC AB 10 E8 12 E8 D6 FF 68 30 40 20 10 6A 00 68 04 01 00 00
x????xxxx????xxxxxxxxxxxxxx????x????x????x????x????xxxxxxx
… which is massive! The signature reaches all the way to the PUSH 104h instruction meaning that there is another sequence of bytes with the exact same values + wildcards up until that point. This is commonly seen with constructors and/or initialization macros (which this function is). This isn’t too big of a deal, but it does slightly increase the risk of any changes early on in a function causing a signature breakage.
Also, no, I’m not leaking disassembled code from work. Those screenshots are from the disassembly of a video game I used to play.
Here are a few more tips for signature creation/scanning that didn’t really have a place in the post:
If your signature extends beyond the bounds of a subroutine without being unique, you’re out of luck! You either need to:
Scan for a function that calls the function/byte sequence you want to create a signature for and dereference the location that way
OR, assure that your signature is the closest to the base of the program. Since sigscanning takes place from top to bottom (left to right), if your signature is not unique, but is first to be found, you can squeak by with it.
You can be more liberal with wildcarding if you want a very robust signature.
These instructions load up huge offsets within a structure. If the structure gets any new members before those fields, the signature will break!A signature here that would be F3 0F 11 8E 28 44 00 00 F3 0F 11 9E 24 44 00 00 F3 0F 11 86 2C 44 00 00 can (and probably should be) wildcarded to F3 0F 11 8E ? ? ? ? F3 0F 11 9E ? ? ? ? F3 0F 11 86 ? ? ? ?.
If you can’t afford the wildcards, you can assume that the structure field offsets won’t exceed 0x10000 and can use this as a signature instead: F3 0F 11 8E ? ? 00 00 F3 0F 11 9E ? ? 00 00 F3 0F 11 86 ? ? 00 00
This can go both ways, too. If there’s a chance that xmm1, xmm3, and xmm0 will be different registers, you can wildcard that signatory byte as well, which prompts this signature: F3 0F 11 ? ? ? 00 00 F3 0F 11 ? ? ? 00 00 F3 0F 11 ? ? ? 00 00.
This might be a bit overkill, but if you really don’t want to have to analyze a program again, quality signatures can help.
That’s all for this post. Hope you all enjoyed. Make sure you paid extra attention to calculating relative offsets, as that will be the topic of the next paid post.
Have a great rest of your weekend.
Go!
-BowTiedCrawfish