Announcement

Collapse
No announcement yet.

The AOBscan Instruction

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • The AOBscan Instruction

    The AOBscan Instruction

    Many people were asking for an aobscan tutorial so here Geri makes 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).

    [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):

    [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 Geri has 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
    necessary 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:

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

    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:

    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

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

    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.

    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

    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.


    db 8B 83 64 04 00 00 //restore the original byte pattern

    Yes. I have replaced

    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:
    mov eax,[ebx+ecx++00000464]
    and
    mov eax,[ecx+ebx++00000464]
    has the same results, but they are not the same instruction. We have to make sure that we restore the same instruction that has exactly the same byte pattern as the original. If we fail to do that, the original byte pattern will be lost and your aobscan will not find it if you try to enable the cheat again. Always be 100% sure that you have restored the original byte pattern, or the users will not be able to turn the script on/off.

    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

    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

    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:

    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:

    [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:

    [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:

    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?

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

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

    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.

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

    Peace!
    Geri

    Source
    The Hackmaster

  • #2
    If only it could work on AMD64.

    Comment

    Working...
    X