Many people were asking for an aobscan tutorial so here I make a short description for using aobscan.
Why are we using aobscan frequently?
In many cases, the address of the code that we want to change is not static. This may happen in many games
where the codes are loaded only when they are used and it also happens if you are not using the same
game version. If the code itself did not change, only the address of the code, you can use aobscan to find the code's address.
First I will start with an example where I show how can you change a simple script into an aobscan script,
then I will explain it in more details for those who want to know how is it working. At the end I will write
some more advanced hints for specific problems that you may encounter when you are using aobscan.
The target process is the Tutorial program for CE 6.2. Here is a normal script that will solve Step 2
of the tutorial (at this point I assume that you can make cheats with static code addresses).
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
mov [ebx+00000464],(int)1000
originalcode:
mov eax,[ebx+00000464]
exit:
jmp returnhere
"Tutorial-i386.exe"+22988:
jmp newmem
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-i386.exe"+22988:
mov eax,[ebx+00000464]
//Alt: db 8B 83 64 04 00 00
To change this into a script with aobscan, you have to make a few modifications only (look at the comments):
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
label(whatever) //make a label that you can use for your aobscan
registersymbol(whatever) //also register it as a symbol
aobscan(aob1,8B 83 64 04 00 00 3D) //use aobscan to search for the code, more explanation later
newmem: //this is allocated memory, you have read,write,execute access
mov [ebx+00000464],(int)1000
originalcode:
mov eax,[ebx+00000464]
exit:
jmp returnhere
aob1: //replace the static address with your aobscan, which is called aob1 in my case
whatever: //store aob1 on the whatever label
jmp newmem
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
whatever: //replace the static address with the whatever label
db 8B 83 64 04 00 00 //restore the original byte pattern
unregistersymbol(whatever) //we don't need this symbol anymore so unregister it
If you copy-paste this script into CE, it will scan for the code that I have changed in the original script
and do the same code injection. Now I should explain a few things in more details, though it is not really
neccessary to understand everything in order to use aobscan.
The aobscan instruction:
This instruction is used to find a byte pattern in the memory and store the address of the first result. Important
to remember that if there are more results for the scan, the first result will be used. Make sure to have only one
result for your scan or at least the first one should be the correct address that you want to find.
The code that we have changed in the original script was this:
- Code:
Tutorial-i386.exe+22988 - 8B 83 64040000 - mov eax,[ebx+00000464]
Tutorial-i386.exe+2298E - 3D E8030000 - cmp eax,000003E8
If you look at it in the disassembler, you can see how is this instruction stored in the memory.
- Code:
8B 83 64 04 00 00
3D E8 03 00 00
You have to come up with a pattern that will identify this code. Let's do this:
Switch value type to "Array of byte", make sure that the "Writable" box is just "optional" and it doesn't have
a checkmark in it, as the code we are looking for is NOT writable, only executable. Now scan for this byte pattern
- Code:
8B 83 64 04 00 00
You will probably have 8 results, which is not a good start and if you check the first result, it is not the
correct code we are looking for. Thus we can see that we can't use this byte pattern. Now we have to come up with a new pattern that will filter out the 7 wrong results. As we can see, the first byte of the next instruction
starts with 3D. Try to scan for this byte pattern:
- Code:
8B 83 64 04 00 00 3D
Now you will have one result, or at least the first one will be the code that you are looking for. This byte
pattern can be used to find our code's address. The syntax is very simple
- Code:
aobscan(name,byte pattern)
aobscan(aob1,8B 83 64 04 00 00 3D)
The name of the aobscans and symbols can be anything, it's up to you. After this instruction is executed,
aob1 is equal to "Tutorial-i386.exe"+22988 address. You can use aob1 to refer to this address.
NOTE: The * character is a joker character. If you put * in your byte pattern, it can be anything.
Eg
"* 83 64 04 00 00 3D" could be anything from "00 83 64 04 00 00 3D" to "FF 83 64 04 00 00 3D"
When you are not sure that a byte will be the same in other game versions, use *.
Making the script:
As you see it was neccessary to make a few modifications in the original script to use aobscan. You can just
copy-paste these changes into your script without ever asking why are they needed, but if you are curious, here
are the reasons.
- Code:
label(whatever)
registersymbol(whatever)
You are not able to use your aobscan result in the disable section and you can't scan for the code either, because you have changed it with your code injection. This is why the address has to be saved somewhere for later use.
- Code:
aob1: //replace the static address with your aobscan, which is called aob1 in my case
whatever: //store aob1 on the whatever label
This is the part where we store aob1 on the "whatever" symbol so we can use it in the disable section too.
Disable section
- Code:
whatever: //replace the static address with the whatever label
This is why we have saved the address on "whatever", we need to restore the original code in the disable section.
- Code:
db 8B 83 64 04 00 00 //restore the original byte pattern
Yes. I have replaced
- Code:
mov eax,[ebx+00000464]
//Alt: db 8B 83 64 04 00 00
with the original byte pattern. The reason is that the instruction may be compiled differently by CE and we have to be 100% sure to restore the instruction exactly as it was.
Example:
- Code:
mov eax,[ebx+ecx++00000464]
and
mov eax,[ecx+ebx++00000464]
unregistersymbol(whatever) //we don't need this symbol anymore so unregister it
Simple clean-up. Remove the symbol that we are not using anymore.
Advanced tips and solutions to various problems that you may encounter when you are using aobscan:
The most common mistakes with aobscan are using the same symbol names when you have more than one script and not restoring the original code correctly. You should always use separate symbol/label/aobscan names in different scripts and as I have said above, be 100% sure that you have restored the original byte pattern. If your scripts are working correctly, they can be turned on/off anytime independently, without interfering with each other. It is easy to make mistakes and the users often blame CE for some bug, but if you have followed the instructions properly, it should work. The error is in your script, somewhere something is screwed up. Double-check everything, look at the changes in the disassembler when you turn the cheats on/off, check the original code and the restored code that they are really matching etc.
Make sure that your byte pattern is not containing static addresses or other codes that will most likely change
in other versions of the game. Eg a code like
- Code:
call 00908070
will be different in another version of the game, because 00908070 is a static address. Replace static addresses with * character. Every * is a byte. So replace the address with * * * * in the pattern.
How to make a code injection at an address if you do not find a byte pattern for that particular address?
Let's assume that we want to change this code
- Code:
Tutorial-i386.exe+22988 - 8B 83 64040000 - mov eax,[ebx+00000464]
Tutorial-i386.exe+2298E - 3D E8030000 - cmp eax,000003E8
Tutorial-i386.exe+22993 - 75 2C - jne Tutorial-i386.exe+229C1
and we have a byte pattern that gives us "Tutorial-i386.exe+22988" but we want to make the code injection at this address:
- Code:
Tutorial-i386.exe+2298E - 3D E8030000 - cmp eax,000003E8
If we do not find any working byte pattern that will find Tutorial-i386.exe+2298E, we can use the previously discovered byte pattern that gives us Tutorial-i386.exe+22988 as the result. The original code would look like this:
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
mov [ebx+00000464],(int)1000
originalcode:
cmp eax,000003E8
exit:
jmp returnhere
"Tutorial-i386.exe"+2298E:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-i386.exe"+2298E:
cmp eax,000003E8
//Alt: db 3D E8 03 00 00
The aobscan version will be this:
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
label(whatever)
registersymbol(whatever)
aobscan(aob1,8B 83 64 04 00 00 3D)
newmem: //this is allocated memory, you have read,write,execute access
mov [ebx+00000464],(int)1000
originalcode:
cmp eax,000003E8
exit:
jmp returnhere
aob1+6: //22988+6 = 2298E so we are making our code injection on 2298E address instead of 22988
whatever:
jmp newmem
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
whatever:
db 3D E8 03 00 00
unregistersymbol(whatever)
As you can see, we have used the byte pattern that gives us Tutorial-i386.exe+22988 as a result but since we want to make the code injection at 2298E, we have simply used aob1+6 instead of aob1. 22988 + 6 = 2298E and since aob1 = 22988, aob1+6 is also equal to 2298E.
Jumps in your script:
Jump instructions in the original code will cause problems for you if you don't handle them properly. The source of the problem is this:
- Code:
Tutorial-i386.exe+22988 - 8B 83 64040000 - mov eax,[ebx+00000464]
Tutorial-i386.exe+2298E - 3D E8030000 - cmp eax,000003E8
Tutorial-i386.exe+22993 - 75 2C - jne Tutorial-i386.exe+229C1
Tutorial-i386.exe+22995 - 8B 83 4C040000 - mov eax,[ebx+0000044C]
CE will show you that conditional jump as jne Tutorial-i386.exe+229C1 to make things easier, but in reality, that instruction is jne +2C. It will jump forward 2C bytes.
What happens if you use this code?
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
originalcode:
jne Tutorial-i386.exe+229C1
mov eax,[ebx+0000044C]
exit:
jmp returnhere
"Tutorial-i386.exe"+22993:
jmp newmem
nop
nop
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-i386.exe"+22993:
jne Tutorial-i386.exe+229C1
mov eax,[ebx+0000044C]
//Alt: db 75 2C 8B 83 4C 04 00 00
Will it work? Absolutely. The problem is, Tutorial-i386.exe+229C1 is a static address and you can't jump to a static address, because it will be different in other game versions. This jump will work when you are making a normal script, but you can't use it with aobscan.
So let's replace it with the original instruction, jne +2C.
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
originalcode:
jne +2C
mov eax,[ebx+0000044C]
exit:
jmp returnhere
"Tutorial-i386.exe"+22993:
jmp newmem
nop
nop
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
"Tutorial-i386.exe"+22993:
jne +2C
mov eax,[ebx+0000044C]
//Alt: db 75 2C 8B 83 4C 04 00 00
What will happen if you enable this? If you are lucky, your program will not crash. But it will not work either. Because if you jump forward 2C bytes in the allocated memory that you have created, you end up at the middle of nowhere. Don't forget that this jump is calculating the destination from the current address so when you make a code injection, it is calculating the jump from the allocated memory.
Ok how to fix this? First we need a byte pattern for this code.
- Code:
75 2C 8B 83 4C 04 00 00
Now calculate the difference between "Tutorial-i386.exe"+22993 and the destination of the jump, Tutorial-i386.exe+229C1. The difference is 2E bytes, which means that aob1+2E = Tutorial-i386.exe+229C1.
Why not 2C? We have jne +2C and now we have 2E. The answer is that the relative jump will calculate the destination from the end of the instruction and we are calculating it from the first byte of the instruction. Our instruction takes up 2 bytes, this is why the difference is 2 bytes higher.
Now that we know how should we change the jump, we can make a script that will jump to aob1+2E, wherever it is found by the aobscan.
- Code:
[ENABLE]
//code from here to '[DISABLE]' will be used to enable the cheat
alloc(newmem,2048) //2kb should be enough
label(returnhere)
label(originalcode)
label(exit)
label(whatever)
registersymbol(whatever)
aobscan(aob1,75 2C 8B 83 4C 04 00 00)
newmem: //this is allocated memory, you have read,write,execute access
//place your code here
originalcode:
jne aob1+2E
mov eax,[ebx+0000044C]
exit:
jmp returnhere
aob1:
whatever:
jmp newmem
nop
nop
nop
returnhere:
[DISABLE]
//code from here till the end of the code will be used to disable the cheat
dealloc(newmem)
whatever:
db 75 2C 8B 83 4C 04 00 00
unregistersymbol(whatever)
Tada, we have made a magnificent script that is absolutely not making anything useful, but at least it is jumping at the right location, despite the fact that the jump instruction itself is far away from the destination. Now let's do something useful and replace jne with je. This will solve Step 2 of the tutorial and you can proceed to step 3.
In this moment, nothing more comes to my mind about this topic. This should be enough to deal with most of the problems that you will face when you are using aobscan.