[TUTORIAL] How to debug CM0102 exe in OllyDbg ? Debugging and Recovering Save Games

Locked
User avatar
Xeno
Patch Team
Posts: 2291
Joined: Wed Nov 25, 2020 5:01 am
Has thanked: 197 times
Been thanked: 794 times

[TUTORIAL] How to debug CM0102 exe in OllyDbg ? Debugging and Recovering Save Games

Post by Xeno »

Knowledge about debugging CM0102 exe in OllyDbg and possible recovering save games. This tutorial is open for coders/patchers to edit when new info is added. (Last updated on 04.11.2023)

*** You need :

* Download OllyDbg debugger : http://www.ollydbg.de/download.htm >>> Download the Olly debugger from the link. It's a small program that doesn't even need to be installed.
Make sure you are running the OLLYDBG.exe as an administrator (right click on OLLYDBG.exe and select Properties --> Compatibility --> Tick the box beside 'Run this program as an administrator').

* Download freeware Hex Editor XVI32 : http://www.chmaas.handshake.de/delphi/f ... m#download >>> XVI32 for Windows is a HEX editor, which is a program that can edit hexadecimal code.

* Download wincmp3.exe as file compare tool : https://nic.hopto.org/open/cm0102/TapaniCompare.7z >>> Download wincmp3.exe , file compare and merge tool helps in file comparison.

* Reverse Engineering resource links : Some recommended knowledge
https://www.youtube.com/playlist?list=P ... 6PjamaY6JO >>> Reverse Engineering Tutorials (including OllyDbg) on youtube
https://beginners.re/ >>> Reverse Engineering for Beginners free book

* [DOCUMENTATION] Offset Index : Decoding OllyDbg :: viewtopic.php?f=35&t=2802 >>> In this documentation , you can find cm0102_3.9.68_text.rar and C Header.rar to understand what the crashed line/block offsets are doing or for which function.

*** Recommended links, you will also need at least below info's :

* [TUTORIAL] Patching the game > Applying an offset change/patch via OllyDbg . >>> viewtopic.php?p=26693#p26693

* [DOCUMENTATION] Offset Index : Decoding OllyDbg >>>>> In this documentation , you can find cm0102_3.9.68_text.rar and C Header.rar to understand what the crashed line/block offsets are doing or for which function. >>> viewtopic.php?f=35&t=2802

* [DOCUMENTATION] Offset Index : Decoding OllyDbg >>>> CPP headers to understand which section is related with crashed line. >>> viewtopic.php?p=22964#p22964

* [DOCUMENTATION] The [9CF***] Thread >>> to understand the related league, nation, club, etc. if necessary. >>> viewtopic.php?f=35&t=2801

* [TUTORIAL] CM0102 All known bugs , v3.9.68 cpp errors , solutions , fix and common game FAQs ? >>> viewtopic.php?f=85&t=3573

* In the first post of below threads, you can find some common errors ( reasons and solutions )
- Installing a SI Games Patch (v3.9.65 or v.3.9.68) FAQ >>> viewtopic.php?f=43&t=263
- Installing a database FAQ >>> viewtopic.php?f=43&t=274
- Installing a Nick+Co, saturn or Tapani Patch FAQ >>> viewtopic.php?f=43&t=292

*** How to debug in Olly ? :

* Some key actions in Ollydbg :
  • Download the Olly debugger from above link. It's a small program that doesn't even need to be installed.
  • Unzip it and open OLLYDBG. Make sure you are running the OLLYDBG.exe as an administrator (right click on OLLYDBG.exe and select Properties --> Compatibility --> Tick the box beside 'Run this program as an administrator').
  • Press F3 and navigate to your cm0102.exe and click Open.
  • First click on the assembly - Ctrl+Home, Ctrl+Shift+End, Ctrl+C and copy all the assembly to notepad and save it. Makes searching the assembly easy.
  • Learn to Ctrl+G - Press ctrl+g and type in an address, hit enter and you'll jump to that address
  • When you see a CALL or a JMP or similar. Select it and hit Enter to follow it to where its going.
  • Right click on the start of a function and select "Find References To -> Selected Command" will show all points that function is called.
  • In the registers window on the top right, right clicking and selecting Follow in Dump will often show you what a register is pointing to in the dump. Pressing Ctrl+G in the dump also works like it does in the assembly.
  • Double-clicking the EIP register will take you to the line of code that's currently running (when paused).
  • Learn the function key short cuts for debugging (F2 to add a breakpoint, F9 to run, etc).
  • Note that in Olly, when entering hexadecimal values that begin with a letter, a zero has to go before it.
  • During changing/inserting a line, 'Fill with NOP's' option shall be ticked on Assemble task screen.
* Finding crash line - Example : If you have a crash or an error, open the exe in Olly . When game crashes , it should show you the line of code (highlighted) that causes the crash on left hand side. It doesn't matter if you holiday or not, as long as it crashes.
1. Open OLLYDBG.
2. Press F3 and navigate to your cm0102.exe and click Open.
3. After the exe has loaded in Olly, hit F9.
4. CM will open as normal, but is now running within Olly. Create a new game or load problematic sav game as you would normally. It doesn't matter if you holiday or not, as long as it crashes.
5. The game will still crash, but a line will now be highlighted in Olly. Check what line it is (the value in the far left column, it should be something from 00400000 to 00960000).

When you find the crash line, ( if there is near this line ) mostly changing JE-JL-etc to JMP or NOP or something different based on the code after understanding the issue. But, here Nick sometimes write new code lines to correct or avoid crash.

* Error debug info - Example : Most of the errors say ' Out of Memory ' in the beginning , but when you read all error debug info , you will see an error such as v3.9.68 index..cpp 5809 .

* Finding error block - Example : If you ever get this error v3.9.68 index..cpp 5809 for example and want to find related block where error is displayed.
- Number 5809 is decimal , it is 16B1 in HEX .
- So, when you search PUSH 16B1 in exe ( in Olly or exe text version - sometimes you need add 0 at beginning of this number if it is 2 or 3 digits ) , you can find the error block ( such as below example ) so that you can have some idea about error and you can check related blocks for it. Record the offset line adress for PUSH 16B1 , here offset line is 0060D7C3 . Related example error block is below.

0060D780 /$ 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
0060D784 |. 81EC 00020000 SUB ESP,200
0060D78A |. 50 PUSH EAX
0060D78B |. 68 F4939D00 PUSH cm0102.009D93F4 ; ASCII "Unable to find the %s index"
0060D790 |. 68 641FDE00 PUSH cm0102.00DE1F64
0060D795 |. E8 05753300 CALL cm0102.00944C9F
0060D79A |. 8D4C24 0C LEA ECX,DWORD PTR SS:[ESP+C]
0060D79E |. 8D9424 0C01000>LEA EDX,DWORD PTR SS:[ESP+10C]
0060D7A5 |. 51 PUSH ECX
0060D7A6 |. 52 PUSH EDX
0060D7A7 |. 6A 00 PUSH 0
0060D7A9 |. 6A 00 PUSH 0
0060D7AB |. 68 CC939D00 PUSH cm0102.009D93CC ; ASCII "E:\dev\CM3\cm3 00-01\cm3\code\index.cpp"
0060D7B0 |. E8 4A753300 CALL cm0102.00944CFF
0060D7B5 |. 83C4 20 ADD ESP,20
0060D7B8 |. 8D4424 00 LEA EAX,DWORD PTR SS:[ESP]
0060D7BC |. 8D8C24 0001000>LEA ECX,DWORD PTR SS:[ESP+100]
0060D7C3 |. 68 B1160000 PUSH 16B1
0060D7C8 |. 50 PUSH EAX
0060D7C9 |. 51 PUSH ECX
0060D7CA |. E8 61F92F00 CALL cm0102.0090D130
0060D7CF |. 50 PUSH EAX
0060D7D0 |. 68 E8709800 PUSH cm0102.009870E8 ; ASCII "v%s %s.%s %d"
0060D7D5 |. 68 D024AE00 PUSH cm0102.00AE24D0
0060D7DA |. E8 C0743300 CALL cm0102.00944C9F
0060D7DF |. 6A 00 PUSH 0
0060D7E1 |. 68 D024AE00 PUSH cm0102.00AE24D0
0060D7E6 |. 68 E0709800 PUSH cm0102.009870E0 ; ASCII "Error"
0060D7EB |. E8 A0AAFDFF CALL cm0102.005E8290
0060D7F0 |. C705 347AB600 >MOV DWORD PTR DS:[B67A34],0
0060D7FA |. 81C4 24020000 ADD ESP,224
0060D800 \. C3 RETN

* Using breakpoint - Example : If you ever get this error v3.9.68 index..cpp 5809 for example and want to know what the game can't find, this is how to find out. You already find error block like above .Then :
1. Open your cm0102.exe using Olly application.
2. Ctrl+g - enter 0060D7C3 , then you should see this line: 0060D800 C3 RETN
3. Double click on the C3 , the line 0060D800 should then be highlighted red.
4. Click the blue forward arrow (4 buttons from the left at the top).
5. CM0102 will then load, start a new game, the error will appear as usual, click OK, the game will then pause, go back to Olly.

This line:
0060D78B 68 F4939D00 PUSH OFFSET 009D93F4 ; ASCII "Unable to find the %s index"

Will be replaced by (example):
0060D78B 68 F4939D00 PUSH OFFSET 009D93F4 ; ASCII "Unable to find the MANCHESTER_UNITED index"

Press F9 in Olly to continue, the game will then carry on as normal.

* Making game show you error details - Example-1 : Another easy way to find ( using above example ). Result - the game tells you what is missing! for error v3.9.68 index..cpp 5809 .
Using Olly make this single change:
0060D7E1 PUSH 00DE1F64

1. Open Notepad in computer and copy patch form into new Notepad file.
2. Save this file with .patch extension ( you can also change file extension as .patch manually after saving file )
3. Then apply it by using Nick+Co's CM0102Patcher (Tools > Apply Patchfile...). Nick+Co's CM0102Patcher >>> viewtopic.php?p=5214#p5214

Code: Select all

0020D7E2: D0 64
0020D7E3: 24 1F
0020D7E4: AE DE
* Making game show you error details - Example-2 : You can apply this patch into your exe (after you backup your own exe) to see the problematic issue on the screen.
This is not the solution, just to see the root cause, then you may have chance to solve the problem based on what you see.
Below patch will push the game (if lucky) to show errors like below when there is related error:
v3.9.68 virtual staff...cpp 195 >>> patch will protect crash for this error to see below root causes if any.
v3.9.68 match_eng...cpp 612 ASCII "Only %d home players ** %s v %s, %s on %s" >>> you will see club name which has a problem about its nation or available player qty
v3.9.68 match_eng...cpp 652 ASCII "Only %d away players %s v %s **, %s on %s" >>> you will see club name which has a problem about its nation or available player qty.

1. Open Notepad in computer and copy patch form into new Notepad file.
2. Save this file with .patch extension ( you can also change file extension as .patch manually after saving file )
3. Then apply it by using Nick+Co's CM0102Patcher (Tools > Apply Patchfile...). Nick+Co's CM0102Patcher >>> viewtopic.php?p=5214#p5214

Code: Select all

0x2b8965 0xd0 0x64
0x2b8966 0x24 0x1f
0x2b8967 0xae 0xde
0x2b8abc 0xd0 0x64
0x2b8abd 0x24 0x1f
0x2b8abe 0xae 0xde
0x45d891 0x90 0x31
0x45d892 0x90 0xc0
0x45d893 0x90 0x83
0x45d894 0x90 0xfa
0x45d895 0x90 0x0
0x45d896 0x90 0x74
0x45d897 0x90 0x24
0x45d898 0x90 0xf
0x45d899 0x90 0xbe
0x45d89a 0x90 0x42
0x45d89b 0x90 0x7e
0x45d89c 0x90 0xeb
0x45d89d 0x90 0x1e
0x45d8b8 0xf 0xeb
0x45d8b9 0xbe 0xd7
0x45d8ba 0x42 0x90
0x45d8bb 0x7e 0x90
User avatar
Xeno
Patch Team
Posts: 2291
Joined: Wed Nov 25, 2020 5:01 am
Has thanked: 197 times
Been thanked: 794 times

[TUTORIAL] Debugging and Recovering Save Games: Lesson-1 by Nick+Co

Post by Xeno »

*** Debugging and Recovering Save Games: Lesson-1 by Nick+Co :
(Nick+Co's original post is here >>> viewtopic.php?p=97478#p97478)

For this one (from the Retro Update / CM84 thread in the Data Updates forum - viewtopic.php?p=97441#p97441) the way I found it was to run up the exe in Ollydbg, load the save game, holiday until the crash. The crash happens here (this is from my notes I saved at the time):

Code: Select all

008E7BDF  |.  895C24 10                   MOV DWORD PTR SS:[LOCAL.8],EBX
008E7BE3  |.  75 48                       JNE SHORT 008E7C2D
008E7BE5  |.  8B46 39                     MOV EAX,DWORD PTR DS:[ESI+39]       <---- Does this Staff member have a club?
008E7BE8  |.  85C0                        TEST EAX,EAX
008E7BEA  |.  74 41                       JZ SHORT 008E7C2D          <---- if not do a jump. We can always do this jump safely
008E7BEC  |.  8B4E 61                     MOV ECX,DWORD PTR DS:[ESI+61]         <---- Getting NULL here from Player data at +61
008E7BEF  |.  66:8B51 0B                  MOV DX,WORD PTR DS:[ECX+0B]       <---- Crashing here
008E7BF3  |.  52                          PUSH EDX                                 ; /Arg5
008E7BF4  |.  6A 02                       PUSH 2                                   ; |Arg4 = 2
008E7BF6  |.  50                          PUSH EAX                                 ; |Arg3
008E7BF7  |.  8B80 CF000000               MOV EAX,DWORD PTR DS:[EAX+0CF]           ; |
I reference this file (compiled by others long ago) when I'm curious about what an offset does:
https://nic.hopto.org/open/cm0102/Struc ... memory.txt
I can also see from the structures I load for saved games in my code:
https://github.com/nckstwrt/CM0102Patch ... es.cs#L178
That +61 is referencing the Player data of a Staff object and +39 is referencing the club the staff is at.

One usefui tip is, if you have a breakpoint at this code and stop here - the ESI register is pointing to the Staff object. So under "Registers" on the right hand side in Ollydbg, you can find the number next to ESI and right click it and select "Follow in Dump". Then the hexadecimal below the assembly will now be pointing to ESI which is the staff object. The first 4 bytes shown are the ID of the Staff, the next 4 are their first name (and the next their surname). If you highlight the hex bytes at offset 4,5,6 + 7 then right-click "Follow in Dump" you will then see what that Staff Object's first name is. Another tip after following in dump the ESI register is to right click in the dump and select Addressing -> Relative to Selection. It then changes the address to a relative address on the left hand side making it easy to find the offsets in the structure.
^ I hope all that made sense - would be so much easier to show in a video :)

From the code above you can see the code checking if the staff member has a club and if so then also trying to get their player details. But for this particular player they did not have any player details so ECX becomes zero then when we try and reference it in "MOV DX,WORD PTR DS:[ECX+0B]" because ECX is zero it tries to access an invalid memory address and crashes.

My solution was to check if ECX becomes zero and if so, then jump as if we did not have a club (i.e. as that seemed a safe option to do). We need 5 bytes to do a big jump, so I replace the lines MOV DX,WORD PTR DS:[ECX+0B] and PUSH EDX with a JMP to spare space that my patcher creates with EXPANDEXE. Stupidly I keep a reference for where I've put patches in that space here:
https://github.com/nckstwrt/CM0102Patch ... er.cs#L409
^ I need to find a better place to log it.

So the code above ends up looking like:

Code: Select all

008E7BEC  |.  8B4E 61                     MOV ECX,DWORD PTR DS:[ESI+61]
008E7BEF      E9 0C065000                 JMP 00DE8200                                 <---- those two lines have been replaced by a big jmp
008E7BF4  |.  6A 02                       PUSH 2                                   ; Arg4 = 2
They jump to here:

Code: Select all

00DE8200      83F9 00                     CMP ECX,0                         <---- is ECX 0?
00DE8203    ^ 0F84 24FAAFFF               JE 008E7C2D                <---- If so jump to the same location we would have if the staff didnt have a club
00DE8209      66:8B51 0B                  MOV DX,WORD PTR DS:[ECX+0B]     <----- ECX isn't zero so just do the two lines we would normally have done
00DE820D      52                          PUSH EDX
00DE820E    ^ E9 E1F9AFFF                 JMP 008E7BF4                              <------ and jump back to continue as normal
That is how most of my protection patches work - check for what is causing the crash and try to jump over or similar just to keep things processing.

Hope some of that at least made some sense! How I found how to change the points from a win from 3 to 2 is also useful (as it teaches how to use memory breakpoints effectively to find useful bits of code) - will try and write that up soon.
Locked