DK hack address is dynamic. This means it will always change. Unless you have a method of attaching a debugger without the game crashing, you will have a very hard time finding the memory pointer for this and defeating this dynamic memory allocation.
One way of finding DK hack is to find dynamic wind address. This can be scanned for easily. Then DK address is a fixed offset from there. In fact, it is the dynamic wind address minus 0x50. At that address, you define 16 bytes there as 00.
So what about you guys who have no UCE ? How do you DK hack ?
I'm gonna tell you 2 things and teach you 1 method and if you're smart enough, you will be able to get DK hack working:
- The array of bytes for DK hack stays the same each patch
- GB's memory only properly initialises after you're in-game
The method I am gonna teach you guys is a way for you to use your close method and be able to enable/disable hacks in-game. What does your close method consist of ? A detected executable and undetected driver most of the time. The driver stays in memory while you quickly close your engine before GG properly initialises.
Aim : Make an AutoAssembler script that you can tick before GB loads. Requirements of this AA script is that once you're in-game you can press certain hotkeys and can scan for the DK array of bytes and define those 16 bytes as 00.
First, let's overview on the stdcall convention. This is the way we will be calling win32 API that we need. Important things to note about using stdcall is that the return value is always EAX; also ECX and EDX are not always preserved. That is, after the call, ECX and EDX may have changed and if there is a return value, EAX will hold it.
Okay, I'm gonna demonstrate the method on Pinball and if you understand it, you can maybe translate it to GB.
To execute our own routine, we will have to redirect part of the thread to carry out our own procedure. To do this, I found an address in Pinball that is constantly accessed. From there, I can overwrite some of the instructions to redirect to a codecave. At that codecave, I can write my own little routine. Short and sweet
An address that is constantly accessed is 1013A0B. This is the instructions writing to the ball when it changes y coordinates. A few of the instructions are:
Code:
mov dword ptr ds:[esi+4],ecx
mov ecx,dword ptr ds:[eax+c]
mov dword ptr ds:[esi+8],ecx
mov ecx,dword ptr ds:[eax+c]
mov dword ptr ds:[esi+8],ecx
If I overwrite the first instruction with a long jump to a codecave, the second instruction will also be partially overwritten. This is easily amended however. Just re-write those instructions that were overwritten at the codecave.
If you understand the portable executable format, you can also add another section header with another section. This is not possible in GB as such though.
I found a codecave at 1022330. So we would patch a jump there from 1013A0B:
Code:
1013A0B:
jmp 1022330
nop
jmp 1022330
nop
Notice the NOP at the end to make sure the number of bytes is equal. Notice the following instructions are overwritten:
Code:
mov dword ptr ds:[esi+4],ecx
mov ecx,dword ptr ds:[eax+c]
mov ecx,dword ptr ds:[eax+c]
Therefore replace them at your codecave. The cheat I want to do is to make the score not change anymore when I enable the cheat and when I disable the cheat, I want the score change routine to be back to normal. I deliberately chose a simple example.
The instructions writing to score are at 1013c98. The bytes/instructions are:
Code:
89 08 --> mov dword ptr ds:[eax],ecx
To disable this, we merely change 89 08 to 90 90, that is change "mov dword ptr ds:[eax],ecx" to "NOP NOP".
We are now going to call an API that can determine whether a key was pressed. However we know EAX, ECX and EDX may change. Therefore, push them to the stack and we can restore their original values after we're done.
Code:
push ecx
push edx
push eax
push edx
push eax
Now we check for the hotkey:
Code:
push 41
call GetAsyncKeyState
call GetAsyncKeyState
GetAsyncKeyState has 1 argument. The virtual key that you want to test for. 0x41 is the A button.
About the return value, MSDN says:
Quote:
If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState. However, you should not rely on this last behavior; for more information, see the Remarks.
So if A was pressed down, the function returns true and EAX will be at a non-zero value. So let's test for that:
Code:
test eax,eax
jnz alternative_routine
jnz alternative_routine
So if EAX == 0, then zero flag will be set. Else, we will jump to alternative_routine, a currently undefined label. Therefore, if the function returns true (A is pressed), then we jump to the alternative routine. First let's deal with what happens when the function returns false, ie. A was not pressed. We want to just jump back to original memory. But first, remember to return the 3 original values to EAX, ECX and EDX:
Code:
pop eax
pop edx
pop ecx
jmp 1013a11
pop edx
pop ecx
jmp 1013a11
1013a11 is the address that we return to just after the instructions we overwrote originally.
We now need to do 1 more check. Whether the cheat is already enabled or disabled. There are many ways of doing this. What I did was to set the value of a byte to 01 when the cheat is enabled and 00 when the cheat is disabled. I can then test for this byte and it will tell me the current situation
102238b is such a possible free byte. By the way this is now where alternative_routine begins:
Code:
alternative_routine:
cmp byte ptr ds:[102238b],1
je disable
cmp byte ptr ds:[102238b],1
je disable
So if the byte at 102238b == 01, then jump to disable routine, else (routine to enable cheat):
Code:
mov ax,9090
mov word ptr ds:[1013c98],ax
mov word ptr ds:[1013c98],ax
Simple enough, moved the bytes 9090 to AX and then moved it to the address changing score, effectively NOP'ing it.
Set our free byte to 01, pop register and return to original memory:
Code:
mov byte ptr ds:[102238b],1
pop eax
pop edx
pop ecx
jmp 1013a11
pop eax
pop edx
pop ecx
jmp 1013a11
Now we are at the previously undefined disable label:
Code:
disable:
mov ax,889
mov word ptr ds:[1013c98],ax
pop eax
pop edx
pop ecx
jmp 1013a11
mov ax,889
mov word ptr ds:[1013c98],ax
pop eax
pop edx
pop ecx
jmp 1013a11
Not much to explain there, it's pretty obvious. However you may ask why did I move 0889 to AX when the original bytes were 8908. Well, the reason is the 80x86's way of storing and reading memory. This is known as endianness. I write more about this here in one of my reversing series:
http://thedarkalliance.org/viewtopic.php?f=61&t=2465
Anyway here are images showing the places I patched (I did this in Olly but it's easy to convert what I've done to an AA script):
You may prefer GetKeyState to GetAsyncKeyState for various reasons btw. But it is essentially up to you.
1 more thing, I'm not sure about GB's memory but if the section you try to write to does not have write characteristics, then you have to do something about that. In my case, Pinball did not have write characteristics so when I tried to write to the memory, it would crash. I fixed this by getting going to the section header of the section I wanted to change and manually editing the characteristics/section flags. More about editing portable executables and the format in general here:
http://thedarkalliance.org/viewtopic.php?f=61&t=2569
I believe VirtualAlloc is another function that is available to you if you want to do this:
http://msdn2.microsoft.com/en-us/library/aa366887(VS.85).aspx
I haven't given you a method for scanning for the DK array of bytes so here is a push in the right direction. Possible macro instructions to use are:
- scasb/cmpsb
This is quicker than using lots of CMPs because of the instruction cache.
Anyway, enjoy, now to see how many people understood this. I'll be watching out for DKs around and about
Current DK AOB for GBNA is:
4C 9E 5B 00 EC F2 00 00 4C 9E 5B 00 ED F2 00 00
No hay comentarios:
Publicar un comentario