View previous topic :: View next topic |
Author |
Message |
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
Posted: Fri Mar 05, 2021 6:50 am Post subject: Game crashing with alloc using mono but not with AOB |
|
|
Hi,
I've been playing around with mono instead of using AOBs and for some reason one injection always makes my game crash. I'm using the same method in multiple locations so I don't know why this instance is making it crash. Furthermore, when I use an AOB instead at the same location it works fine.
Here is my script for the Mono injection (I have another script that activates mono so that's ok):
Code: | {$STRICT}
define(time_bytes2, 48 8B F1)
[ENABLE]
{$lua}
if syntaxcheck then return end
if LaunchMonoDataCollector() ~= 0 then
local mID_1 = mono_findMethod('Assembly-CSharp','GameTime', 'Update')
mono_compile_method(mID_1)
end
{$asm}
alloc(newmem,$1000)
label(return)
label(timePtr)
registersymbol(timePtr)
assert(GameTime:Update+e, time_bytes2)
newmem:
mov rsi,rcx
mov [timePtr],rsi
jmp return
timePtr:
dq 0
GameTime:Update+e:
jmp newmem
return:
[DISABLE]
GameTime:Update+e:
db time_bytes2
unregistersymbol(timePtr)
dealloc(newmem) |
That makes my game crash, however this AOB works fine:
Code: | [ENABLE]
aobscan(INJECT,48 8B F1 C7 45 DC 00 00 00 00 48 83) // should be unique
alloc(newmem,$1000,INJECT)
label(code)
label(return)
label(timePtr)
registersymbol(timePtr)
newmem:
code:
mov rsi,rcx
mov [timePtr],rsi
mov [rbp-24],00000000
jmp return
timePtr:
dq 0
INJECT:
jmp newmem
nop 5
return:
registersymbol(INJECT)
[DISABLE]
INJECT:
db 48 8B F1 C7 45 DC 00 00 00 00
unregistersymbol(INJECT)
unregistersymbol(timePtr)
dealloc(newmem) |
Any idea what I'm missing here? perhaps it's just a silly mistake that I am completely missing right now.
|
|
Back to top |
|
|
Dark Byte Site Admin Reputation: 458
Joined: 09 May 2003 Posts: 25288 Location: The netherlands
|
Posted: Fri Mar 05, 2021 9:48 am Post subject: |
|
|
look at the alloc. You're not passing it the allocation region preference, so there is a (big) chance the allocated memory will be further then 2GB away from the jmp
therefore the jmp is 14 bytes, but your script doesn't seem to take into account that it's 14 bytes long
_________________
Do not ask me about online cheats. I don't know any and wont help finding them.
Like my help? Join me on Patreon so i can keep helping |
|
Back to top |
|
|
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
Posted: Fri Mar 05, 2021 11:48 am Post subject: |
|
|
I believe I am not understanding how alloc works then, I was assuming that the allocation was at the injection point which now I understand is false.
so I know how to assign it with an AOB, however I am not familiar on how to assign the location in my alloc when using mono, how do I accomplish this?
Also you mention my jump is 14 bytes, I'm not quite sure how you've come to this number, and I do apologize for the newb questions, but how did you know that the jump is 14 bytes?
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4290
|
Posted: Fri Mar 05, 2021 1:12 pm Post subject: |
|
|
There are several instructions that use the "jmp" mnemonic. One jumps to a rel32 displacement- i.e. it can jump to any location within +-2GiB of the next instruction (RIP-relative). This takes up 5 bytes: one for the opcode and 4 bytes for the rel32 displacement.
Another jumps to an address stored in a register or at a memory location (r/m64). What CE does is store the address to jump to immediately after the jump instruction and use that as the target. This takes up a byte for the opcode, 5 bytes for the r/m64, and 8 bytes for the destination address - 14 bytes in total.
Code: | jmp dest
// if dest is >2GB away:
jmp [target]
target:
dq dest |
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
Posted: Fri Mar 05, 2021 1:45 pm Post subject: |
|
|
Ok... I don't quite understand yet, but I think I am getting there.
I am allocating memory with alloc(newmem,$1000). This memory is "somewhere" and I am using jmp newmem to get to it. However as I am not specifying where this memory is, my jmp instruction assumes that I mean a rel32 displacement where in fact I am likely further away than the 2GB range. So in a way I'm not reaching the correct location and probably causing an access violation?
so I think I understand the problem, but unfortunately I don't quite understand the solution with the code you provided. I do apologize again as I think I'm going to need a bit more clarification on the code part as to how I can fix what I currently have.
|
|
Back to top |
|
|
sbryzl Master Cheater Reputation: 6
Joined: 25 Jul 2016 Posts: 252
|
Posted: Fri Mar 05, 2021 2:47 pm Post subject: |
|
|
Code: | alloc(newmem,$1000,GameTime:Update) |
This should allocate the memory in the general location you want it, if there is available memory there.
|
|
Back to top |
|
|
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
Posted: Fri Mar 05, 2021 2:54 pm Post subject: |
|
|
ah yeah that's what I thought as well but unfortunately the game still crashes when I add GameTime:Update to alloc:
Code: | {$STRICT}
define(time_bytes2, 48 8B F1)
[ENABLE]
{$lua}
if syntaxcheck then return end
if LaunchMonoDataCollector() ~= 0 then
local mID_1 = mono_findMethod('Assembly-CSharp','GameTime', 'Update')
mono_compile_method(mID_1)
end
{$asm}
alloc(newmem,$1000,GameTime:Update)
label(return)
label(timePtr)
registersymbol(timePtr)
assert(GameTime:Update+e, time_bytes2)
newmem:
mov rsi,rcx
mov [timePtr],rsi
jmp return
timePtr:
dq 0
GameTime:Update+e:
jmp newmem
return:
[DISABLE]
GameTime:Update+e:
db time_bytes2
unregistersymbol(timePtr)
dealloc(newmem) |
|
|
Back to top |
|
|
sbryzl Master Cheater Reputation: 6
Joined: 25 Jul 2016 Posts: 252
|
Posted: Fri Mar 05, 2021 3:43 pm Post subject: |
|
|
I don't know why it's crashing when enabled but when disabling there are only 3 bytes inserted which isn't enough to cover for even a 5 byte jump.
Usually when I get I crash I have an error message and I just leave that open and push it to the side then go to the location of the injection to see what happened. Press space on the jump to see where it goes. And if you have enough time before the crash put a trace on it to get some better details of what happened or set a breakpoint then inject then trace.
Did you need to include this in your mono script?
mov [rbp-24],00000000
|
|
Back to top |
|
|
ParkourPenguin I post too much Reputation: 140
Joined: 06 Jul 2014 Posts: 4290
|
Posted: Fri Mar 05, 2021 4:17 pm Post subject: |
|
|
Kajih wrote: | However as I am not specifying where this memory is, my jmp instruction assumes that I mean a rel32 displacement where in fact I am likely further away than the 2GB range. | I didn't explain this part well.
When CE tries to assemble "jmp destination", it looks at how far away the jump is: the difference between the address of the destination and the address of the jmp instruction. If it's close enough, CE uses the shorter 5-byte jump; if it's too far, CE uses the 14-byte jump.
Kajih wrote: | So in a way I'm not reaching the correct location and probably causing an access violation? | You are reaching the right location. It's just that you're not returning to the correct location.
The aobscan template assumes the jump instruction at the injection point is 5 bytes long. The template will generate the correct amount of nops and place the return label based on that assumption:
Code: | // injection point:
48 8b f1 - mov rsi,rcx
c7 45 dc 00 00 00 00 - mov [rbp-24],0
48 83...
// after script enabled:
e9 ?? ?? ?? ?? - jmp ??
0f 1f 44 00 00 - nop 5
48 83... | Notice that the two mov instructions in the original code take up 10 bytes of space, and the code after the script was enabled also takes up 10 bytes of space. This way "jmp return" continues executing the same code starting with the bytes 48 83...
If a 14-byte jump is assembled, however, all that gets screwed up and you probably jump back in the middle of an instruction, likely crashing the game.
More problems I just noticed:
Code: | define(time_bytes2, 48 8B F1)
...
[DISABLE]
GameTime:Update+e:
db time_bytes2 | You're only restoring 3 bytes. Even if CE correctly assembled a 5-byte jump this would probably crash when the script is disabled.
Use as many bytes as you're overwriting in the code injection.
Code: | define(time_bytes2,48 8B F1 C7 45 DC 00 00 00 00) |
You also removed the nop padding instructions.
Code: | GameTime:Update+e:
jmp newmem
nop 5 // important
return: |
_________________
I don't know where I'm going, but I'll figure it out when I get there. |
|
Back to top |
|
|
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
|
Back to top |
|
|
Kajih Cheater Reputation: 1
Joined: 08 Feb 2021 Posts: 32
|
Posted: Sat Mar 06, 2021 2:37 am Post subject: |
|
|
yeah I get it now lol (slow learner I guess). It just so happens that every single injection I have created thus far coincidently happened on instructions that already had five bytes so my approach wasn't causing any issues.
I was trying to inject at two different locations in my tests, one had 3 bytes (example provided) and one had 8 bytes, I thought that they were crashing for the same reason (in a way it was, just different amount of bytes to take into account). So naturally both of these were making the game crash because I wasn't compensating properly.
I want to thank you guys again for taking the time to respond.
|
|
Back to top |
|
|
|