Mono Script Simplifier
Posted: Mon Dec 12, 2022 7:10 pm
This is a fairly foolproof method for writing simple scripts for mono games. It should handle the initial code of a method changing with different values. For instance this is the start of the 'PersonController:Update' method in Raft:
The problem is that the '9D0' will probably change if the version of Raft changes, if you add mods, or it can just if you run it again in some games. This much code is needed for a long JMP to the code you want to inject, which has been happening to me sometimes when CE can't find memory near the method you're injecting into. The most resilient way I've found is to use readmem() in your aa script to copy the actual bytes, register a symbol, and restore them in [disable] and unregister the symbol. That's a lot of code I don't want to write for 20 methods I wrote some LUA to create some AA commands to make it super easy. It actually disassembles enough bytes for a long jmp and reassembles them for you to return. This should handle things like call to a relative address and rearranging code as well, but needs some love to account for things like conditional jumps since your code may be far away.
Here's a sample for Raft to give you unlimited oxygen:
* MonoMethodInit - takes the method to hook and an optional list of types to define using GETMONOSTRUCT(). So even if offsets change, you can use 'PlayerStats.stat_oxygen' for example for the offset.
* MonoMethodHook - hooks the method to jmp to the code following MonoMethodInit
* MonoMethodRestore - restores the original bytes that were replaced by hooking the method
As you can see in the code, I do 'jmp original_PersonController_Update' to continue executing the relocated code and jumping back to after where the replaced code was. Other options are 'jmp return_PersonController_Update' which skips the relocated code, so you need to make sure you took care of what it does. Also you could just 'ret' if you don't want the method to actually execute.
The attached table has a script with LUA to create the AA commands you can drop into your own tables (or copy the lua to your table script), a sample script with the code for the unlimited oxygen, and a script with the text being what is actually generated. For example the injected code section looks like this:
Followed by the code in the sample script, followed by the hook:
The restore section looks like this:
Code: Select all
push rbp
mov rbp,rsp
sub rsp,000009D0
mov [rbp-28],rsi
Here's a sample for Raft to give you unlimited oxygen:
Code: Select all
[enable]
alloc(newmem,$1000)
newmem:
MonoMethodInit(PersonController:Update,PersonController,PlayerStats,Stat_Oxygen,Stat)
push rbx
push rax
mov rbx,[rcx+PersonController.playerStats]
test rbx,rbx
jz @f
mov rbx,[rbx+PlayerStats.stat_oxygen]
test rbx,rbx
jz @f
mov eax,[rbx+Stat.maxValue]
mov [rbx+Stat.value],eax
@@:
pop rax
pop rbx
jmp original_PersonController_Update
MonoMethodHook(PersonController:Update)
[disable]
MonoMethodRestore(PersonController:Update)
dealloc(newmem)
* MonoMethodHook - hooks the method to jmp to the code following MonoMethodInit
* MonoMethodRestore - restores the original bytes that were replaced by hooking the method
As you can see in the code, I do 'jmp original_PersonController_Update' to continue executing the relocated code and jumping back to after where the replaced code was. Other options are 'jmp return_PersonController_Update' which skips the relocated code, so you need to make sure you took care of what it does. Also you could just 'ret' if you don't want the method to actually execute.
The attached table has a script with LUA to create the AA commands you can drop into your own tables (or copy the lua to your table script), a sample script with the code for the unlimited oxygen, and a script with the text being what is actually generated. For example the injected code section looks like this:
Code: Select all
label(original_PersonController_Update)
ALIGN 10
original_PersonController_Update:
push rbp
mov rbp,rsp
sub rsp,000009D0
mov [rbp-28],rsi
label(return_PersonController_Update)
return_PersonController_Update:
jmp 136FBB6000F
ALIGN 10
label(code_PersonController_Update)
code_PersonController_Update:
Code: Select all
PersonController:Update:
jmp code_PersonController_Update
Code: Select all
PersonController:Update:
db 55 48 8B EC 48 81 EC D0 09 00 00 48 89 75 D8