1. Code analysis -> IDA-Pro (the free version is more than adequate for BotF):
https://hex-rays.com/ida-free/
On the first time opening resp disassembling a file, IDA-Pro baffles new users with a confusing amount of esoteric windows for a lot of needlessly complicated nerd stuff. Just close them. In the beginning you only need:
'IDA View-A' (disassembly view) + 'Hex View-A' -> Right click -> Syncronise with 'Hex(or IDA) View-A' (screen bottom area -> out of sync glitch)
To display the opcode (hex repesentation) in a line with the asm-instruction -> Options -> general -> number of opcode bytes
Click any instruction or byte -> trek.exe position bottom left
Navigation:
(G) or -> Jump -> Jump to adress [enter asm address]
For most objects: [mouse hovering = preview pop-up] resp. [double click = goto]
[click on dynamic var, subroutine or location] -> (X) or Right click -> Jumpt to xref to operant -> in pop-up "xrefs to..." [double click = goto]
The more identified/labeled dynamic data/sub's, the easier to understand the code (known data in Modding Index -> Tutorials: -Coding, Wiki & patches).
To rename them in IDA-Pro: [click on dynamic var, subroutine or location] -> (N) or Right click -> Rename [enter new label (has to be unique)]
Also you can add own notes in the disassemly view via -> Edit -> comments or Right click (cursor needs distance to the right)
What a code area could look like after this:
Code: Select all
44F439 call GetTaskForceEntry
44F43E mov edx, eax
44F440 add eax, 38h
44F443 call GetSectorLstEntry
44F448 mov [esp+18h], eax
44F44C mov eax, [eax+0Ah]
44F44F sar eax, 10h
44F452 xor ebp, ebp
44F454 cmp eax, 0FFFFFFFFh
44F457 jz short MonsterSwitchViaShipID
44F459 imul eax, 328h
44F45F mov ebp, ds:adr_systInfo
44F465 add ebp, eax
44F467
44F467 MonsterSwitchViaShipID:
44F467 mov eax, [edx+6]
44F46A sar eax, 10h
44F46D call GetGShipListEntry
44F472 mov ax, [eax+52h]
44F476 sub eax, 73h
Sometimes it's a bit confusing at first. E.g. following code actually reads the difficulty level (located immediately afterwards from the minor race setting):
Code: Select all
44C1E0 mov eax, ds:MinorRacesSetting+2
44C1E5 sar eax, 10h
Local variables: (current subroutine only)
Code: Select all
467316 fmul [esp+420h+var_10C] // local variables (default)
467316 fmul dword ptr [esp+314h] // standard asm repesentation
To switch between local-variable / standard-asm display -> [click on asm-instruction] -> press (K)
Whilst local variables view makes it easier to keep track of stack variables[esp+], since they can be labeled, the standard asm representation is still needed for recoding(cf. #2/3) and advanced analysis(for an example see
HERE).
2. Thinking up the code changes
First you need to find the(all) responsible code sequence(s). In the majority of cases there is way too much code to change (e.g. adding new minors = 2 years+ fulltime task).
As an easy example we take the 'liberating foreign home systems fix'
Search for the number of the lexicon.dic entry (606) in IDA-Pro -> (Alt+I) or Search -> immediate value (best -> Find all occurences -> navigate via occurences window)
Sub_48B2E0 (load lexicon.dic entry number[eax]) was known from recoding posts, so it's fast to find:
Code: Select all
4EBCB6 mov eax, 25Eh // = 606
4EBCBB call GetLexiconString
Not too far above from this we find the switch statements for the liberation pop-up:
Code: Select all
4EBC5A wish_to_liberate_?: ; CODE XREF: System_attack_GUI+619
4EBC5A jnz no_free_choice
4EBC60 cmp byte ptr [ebp+30h], 3 // see systInfo
4EBC64 jnz no_free_choice
4EBC6A mov bl, [ebp+4Ch] // see systInfo
4EBC6D cmp bl, 23h
4EBC70 jz no_free_choice
4EBC76 xor eax, eax
4EBC78 xor edx, edx
4EBC7A mov al, bl
4EBC7C mov dx, [ebp+44h] // see systInfo
4EBC80 cmp eax, edx
4EBC82 jz no_free_choice
We need an additional check & switch for conquered home systems aka [systInfo+30h=6] i.e. 6 extra bytes at 4EBC64, but we can't simply insert more commands into trek.exe, because
the length of the code must stay the same. So we have to determine the easiest/fastes way to optimise the syntax for the desired effects (for unknown asm-commands: see links above, recoding examples and use google).
Here I 'redirected' the jumps at 4EBC5A & (former)-4EBC70 to (former)-4EBC64 resp 4EBC82 (i.e. 6-byte long become 2-byte short = 8 bytes free). This was possible due to the matching jump targets and conditions (ungainly but fast to do).
Take another look at the unmodded code above:
4EBC5A -> 4EBC64
4EBC70 -> 4EBC82
Alternatively, one could remove/overwrite the owner-inhabitants check at 4EBC80 (in theory redundant, but in event of bugs...).
Writing the new code to notepad: (optional in preparation for OllyDbg cf. #3)
Code: Select all
at 4EBC5A:
jnz (skip next 3 statements i.e. goto address of "jnz 4EBD44") -> 2byte/nop
cmp byte ptr [ebp+30h], 3
jz (skip next 2 statements i.e. goto address of "mov bl, [ebp+4Ch]") -> 2byte/nop
cmp byte ptr [ebp+30h], 6
jnz 4EBD44
mov bl, [ebp+4Ch]
cmp bl, 23h
jz 4EBC82
nop...
4EBC76 xor eax, eax // remains unchanged
Take note:
Since the targets of the first two jump instructions are part of the new code, one would have to calculate the addresses. However, just typing in nop's in length of the instructions and going back once OllyDbg displays the target locations is faster and less error-prone.
Also note that all "xrefs to..."(cf. #1) have to be adapted when changing start point of subroutines and locations (i.e. targets of jumps and calls)
Here is how OllyDbg displays the above code example: (je = jz)
Code: Select all
004EBC5A 75 0A JNZ SHORT trek.004EBC66
004EBC5C 807D 30 03 CMP BYTE PTR SS:[EBP+30],3
004EBC60 74 0A JE SHORT trek.004EBC6C
004EBC62 807D 30 06 CMP BYTE PTR SS:[EBP+30],6
004EBC66 0F85 D8000000 JNZ trek.004EBD44
004EBC6C 8A5D 4C MOV BL,BYTE PTR SS:[EBP+4C]
004EBC6F 80FB 23 CMP BL,23
004EBC72 74 0E JE SHORT trek.004EBC82
004EBC74 90 NOP
004EBC75 90 NOP
3. Rewriting the code -> OllyDbg:
https://www.ollydbg.de/
Start OllyDbg.exe -> Open trek.exe
(Ctrl+G) or Right click -> Go to -> Expression -> [enter asm address]
Doubleclick first code line you want to change and in "Assemble at ..." [enter new asm statement]
After this, line gets red, indicating that command is modified.
For documentation purpose:
[Mark modified code lines] (Ctrl+C) or Right click -> Copy -> to clipboard (& paste in your documentation file)
Column width is adjustable via mouse (e.g. for opcode length display)
Save code changes to exe:
Right click -> Copy to executable -> All modifications (ignore any strange warning messages, just continue)
In pop-up "Copy to executable file" -> Copy all
In pop-up window with changed code (it's just a documentation!) -> Right click -> Save file (as trek.exe/overwriting creates old trek.bak backup copy)
Pitfalls:
For some nerdish reasons OllyDbg sometimes...
- fails to read (i.e. display) its own code modifications correctly when analysing changed files (see hints by Flocke -> below)
- doesn't understand some asm-statements (e.g. fnstsw ax), fastest solution: in IDA-Pro (Alt+T) or Search -> text [enter statement] -> in OllyDbg look at the found asm-address (it's FSTSW AX)
- doesn't accept its own syntax (remove all labels e.g. call trek.00123456 -> call 123456)
- uses opcode different from BotF defaults. For most, it doesn't matter (e.g. xor commands), but sometimes might de-optimise length of code.
If everything else fails just type nop's & afterwards insert opcode via hex-editor.
I don't know how many bugs is too many but that point is reached somewhere before however many in BotF is.