Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


Game crashing with alloc using mono but not with AOB

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking
View previous topic :: View next topic  
Author Message
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Fri Mar 05, 2021 6:50 am    Post subject: Game crashing with alloc using mono but not with AOB Reply with quote

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
View user's profile Send private message
Dark Byte
Site Admin
Reputation: 458

Joined: 09 May 2003
Posts: 25288
Location: The netherlands

PostPosted: Fri Mar 05, 2021 9:48 am    Post subject: Reply with quote

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
View user's profile Send private message MSN Messenger
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Fri Mar 05, 2021 11:48 am    Post subject: Reply with quote

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
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4290

PostPosted: Fri Mar 05, 2021 1:12 pm    Post subject: Reply with quote

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
View user's profile Send private message
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Fri Mar 05, 2021 1:45 pm    Post subject: Reply with quote

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. Embarassed
Back to top
View user's profile Send private message
sbryzl
Master Cheater
Reputation: 6

Joined: 25 Jul 2016
Posts: 252

PostPosted: Fri Mar 05, 2021 2:47 pm    Post subject: Reply with quote

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
View user's profile Send private message
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Fri Mar 05, 2021 2:54 pm    Post subject: Reply with quote

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
View user's profile Send private message
sbryzl
Master Cheater
Reputation: 6

Joined: 25 Jul 2016
Posts: 252

PostPosted: Fri Mar 05, 2021 3:43 pm    Post subject: Reply with quote

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
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 140

Joined: 06 Jul 2014
Posts: 4290

PostPosted: Fri Mar 05, 2021 4:17 pm    Post subject: Reply with quote

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
View user's profile Send private message
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Fri Mar 05, 2021 6:26 pm    Post subject: Reply with quote

The code works now that I included those 5 extra bytes.

I am still a bit unclear why I am including those in at all, I'm assuming I can't add the injection code on just those 3 bytes, from what I can gather, CE will add a 5 byte instruction there, so If I don't include at least 5 bytes, the jump instruction won't work correctly. I had removed the nop 5 padding as you mentioned because I thought I was just writing at the initial 3 bytes I didn't have to nop the remaining 5... so I was also misunderstanding what that line was doing as well I guess.

lol I think I need to take a step back and re-read everything you guys have explained so I can get my head around this a bit more because right now I think I'm more confused than I started :p

However I do appreciate you guys taking the time to explain this, very much appreciated. Very Happy



screen.png
 Description:
 Filesize:  3.31 KB
 Viewed:  1838 Time(s)

screen.png


Back to top
View user's profile Send private message
Kajih
Cheater
Reputation: 1

Joined: 08 Feb 2021
Posts: 32

PostPosted: Sat Mar 06, 2021 2:37 am    Post subject: Reply with quote

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. Mr. Green
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites