Chapter II - Making a "levelskipper"

What we want to do in Chapter II is a "levelskipper". Remember the fix Address for the game's score from Chapter I ? If not, read chapter I first, then come back. It was 412f74, right. And if you played the game a bit, you probably know that for each level to succeed you have to get a specific score. Your current score must be compared somewhere to the score you have to reach for each level. And what does the game have to do to get your current score? It must read the score (at 412F74). So what we have to do is to set a breakpoint on this address once again, but this time with the "R" parameter (Softice will then break if the game Reads the value stored inside 412F74). Press CTRL-D to get into Softice. You must be sure that "Loogie" is the active process. A rude way would be trying to "catch" the process by pressing CTRL-D until you finally got it. The better way is typing "addr loogie" and "map32 loogie" to get a list of the game's objects and its virtual addresses:

:map32 loogie

  LOOGIE .text  0001 017F:00401000 0000DB9F CODE  RO
  LOOGIE .rdata 0002 0187:0040F000 00000340 IDATA RO
  LOOGIE .data  0003 0187:00410000 00004714 IDATA RW
  LOOGIE .idata 0004 0187:00415000 00000EA4 IDATA RW
  LOOGIE .rsrc  0005 0187:00416000 00026BB4 IDATA RO
  LOOGIE .reloc 0006 0187:0043D000 000017EE IDATA RO

As our address 412F74 resides in .data section, we will use 0187 as selector. So you simply type:

bpmd 187:412f74 r

Softice will then break here:

Break due to BPMD #0187:00412F74 R DR3
  MSR LastBranchFromIp=00401C1B
    MSR LastBranchToIp=00401C3A

017F:00401C37  5E                  POP     ESI
017F:00401C38  5B                  POP     EBX
017F:00401C39  C3                  RET
017F:00401C3A  833D8C45410000      CMP     DWORD PTR [0041458C],00
017F:00401C41  7530                JNZ     00401C73
017F:00401C43  A1742F4100          MOV     EAX,[00412F74]			<- Copy scorepoints to eax
017F:00401C48  3905A82F4100        CMP     [00412FA8],EAX			<- and compare it with the value stored inside 412FA8
017F:00401C4E  7723                JA      00401C73
017F:00401C50  C7056045410002000000MOV     DWORD PTR [00414560],00000002
017F:00401C5A  FF05B02F4100        INC     DWORD PTR [00412FB0]
017F:00401C60  E8DBF4FFFF          CALL    00401140
017F:00401C65  E826F8FFFF          CALL    00401490
017F:00401C6A  B801000000          MOV     EAX,00000001
017F:00401C6F  5F                  POP     EDI
017F:00401C70  5E                  POP     ESI
017F:00401C71  5B                  POP     EBX
017F:00401C72  C3                  RET
017F:00401C73  8B4C2410            MOV     ECX,[ESP+10]
017F:00401C77  81F9007F0000        CMP     ECX,00007F00

Have a look at 401C43, where Softice broke in. Our score will be "copied" to eax and then -at 401C48- compared with the dword inside 412FA8. So let's see what is stored inside 412FA8. Type ? *412FA8 and Softice will show you the value 412FA8 is pointing to.

:? *412fa8
   00000014 0000000020 ""

14 hexadecimal equals 20 decimal. Twenty, XX, 20 hmmm.. sounds familiar. That is the score we have to reach for level one. Great! Now all we have to do is to extend our little trainer engine with a function, which copies some memory from one place to another (from 412FA8 to 412F74 - as 412FA8 contains the score to reach and 412F74 holds our current score) and we'll set up a new hotkey for that (don't forget to update the dialog-file). It could look like this:

And here is our new function, which will copy a dword from one virtual address to another.

Engine_Mov	Proc lpWndCap: DWORD, lpSrcAddress: DWORD, lpDstAddress: DWORD, lpBuffer: DWORD
lpWndCap is a pointer to a string containing the window's caption (e.g.: WndCap db  'Beavis & Butt-head Hock-a-Loogie',0).
lpSrcAddress is a pointer to the address holding a value that will be copied to lpDstAddress.
lpDstAddress is a pointer to the address where the value from lpSrcAddress will be copied to.
lpBuffer is a pointer to a variable that we will use for temporary storage of the value to be copied.

Here the updated routine to fetch the F11 key:


 invoke GetAsyncKeyState,VK_F11 										;Has F11 been pressed?
   .IF eax==TRUE														;Yes? Great! Then ...
   																		;copy value
   invoke Engine_Mov,addr WndCap, LevelAddr, ScoreAddr, addr ScoreBuf


chapter2 sources - sources and binaries