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 


Need Help with Code Injection Logic

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> General Gamehacking
View previous topic :: View next topic  
Author Message
Hugo_the_Dwarf
Newbie cheater
Reputation: 0

Joined: 17 Apr 2019
Posts: 13

PostPosted: Mon Apr 22, 2019 1:53 am    Post subject: Need Help with Code Injection Logic Reply with quote

So I've been messing around with adding "newer" features into an older game (Turok 2: Seeds of Evil) I've implemented a low level health regen portion, that I just need to balance out now. But the problem I'm facing now is some crashing and comparing logic that isn't working right. The code is below:

EDIT: I'll update this with the final code once all the issues are resolved, or after a few weeks have passed with what was solved.
EDIT2: I figured out my crash, and have updated the below code with the final product, To assembly experts uncommented parts that Bloodybone hadn't touched are probably dying on the inside but this is the best I could do at my level, and it works

Code:
aobscanmodule(DamageUpdate,horus_x64.exe,F3 0F 11 43 70 48 8B) // should be unique
alloc(NEWDamageUpdate,$1000,"horus_x64.exe"+54A23)

registersymbol(DamageUpdate)
registersymbol(NEWDamageUpdate)

label(code)
label(return)
label(p_string)
label(stringcmp)
label(stringcmp_loop)

NEWDamageUpdate:

code:
  push rax
  mov rax,[rbx+78]
  //mov rax,[rax+60]
  push rax
  push rcx
  push rdx
  lea rcx,[rax+60] // The difference between lea and mov is that mov, copies the value from the address in this case [rax+60] and lea gets the address of [rax+60]
  lea rdx,[p_string]
  call short stringcmp // The short prefix means that the call will be a "short call" which means that it only takes to bytes but can only jump a short distance instead of the normal five for a long or even 14 or 15 bytes for the far call
  test al,al // This checks if al is 0 and if it is the Zero-Flag is set
  pop rdx
  pop rcx
  pop rax
  jne short Final // This jumps if the Zero-Flag is not set meaning that al had to be anything but 0 before
    movd eax,xmm0 //I changed the movq to a moved because a float is only 4 bytes long and a movq moves 8 bytes
    cmp eax,(float)0
    jge short Final
      mov rax,[1407F2EA8]
      lea rax,[rax+70]
      add [rax],10000
  Final:
  pop rax
  movss [rbx+70],xmm0
  jmp return

stringcmp: // This function returns 1 if the both strings passed are 1:1 and 0 if they're not
/*
This function takes 2 pointers where each points to the start of the string
and then both of them are compared to each other
*/
// Save Registers
push rsi
push rdi
// Move the pointers to the start of the strings in to the saved registers
mov rsi,rcx
mov rdi,rdx
// Set rax to 0
xor rax,rax
stringcmp_loop:
lodsb // This instruction moves the byte at [rsi] into al and increments rsi
/* You could also write it like this:
mov byte ptr al,[rsi]
inc rsi

Which means that it moves the Char of the first string into al and makes rsi point to the next char in the first string
*/
cmp byte ptr [rdi],al // And then Compares it to the Char of the other String
je short @f // This then jumps to the next @@ if the Chars are the same
  pop rdi
  pop rsi
  xor al,al // Sets al to zero
  ret
@@:
inc rdi // rdi now points to the next Char in the second string
test al,al // Check if the CHar is 0 meaning the string has ended
jne short stringcmp_loop // If the string has not ended, loop
  pop rdi
  pop rsi
  mov al,1
  ret

p_string:
db 50,'layer',0 // This equals the String 'Player' and the 0 at the end means that the String is zero-teminated

DamageUpdate:
  jmp NEWDamageUpdate
return:


[DISABLE]

DamageUpdate:
  db F3 0F 11 43 70


dealloc(NEWDamageUpdate)
unregistersymbol(DamageUpdate)
unregistersymbol(NEWDamageUpdate)


Explanation on the Data used in the code:
RBX is the register holding the Actor that has taken damage (RBX+70 points to the HP value 'float')
XMM0 is the new calculated HP 'float' (RBX+70 is 100.00, XMM0 is 70.00 as the Actor took 30 dmg)
I'm using RAX to hold a 'pointer' that contains the actor's base definition ( RBX+78 ) offset inside of that pointer by 60 is the actor's classname 'string'
so using
mov rax,[rbx+78]
mov rax,[rax+60]

to get the actor's classname

Now where I'm struggling but I'll explain what I'm trying to do. I'm trying to make new logic that when something is killed, the player is healed by a small amount.
The struggle is:

1) FIXED BY "Bloodybone" not able to compare the classname properly (this is due to my own lack of experience/knowledge) the player Actor classname is "Player" (magical huh) however when injected the memory view has in the "comment" header ("Play") so the string is too long for the cmp operations memory bytes. Any tips on how to compare large strings (or how I'd compare each char in the RAX register holding the String (that is if I even loaded the string correctly to begin with, not sure how to debug that right in CE)

2) Compare is not working with XMM0 as I'm not sure how I can properly work with that Register compared to the other registers, the compare is not working (with my implementation) as even if the number is 0 or below, it's not triggering (well going through with the code I want to run, it just jumps to the end all the time)

3) Application Crash if inner most code is run. What I'm using is a static pointer, then getting the offset for the Player's HP.

I may have to regather what static address I'm using, but what I have in the cheat table to see my HP is a pointer 1407F2EA8[Offset '+70'] I got this pointer from looking at the HUD's graphical update logic (and yes the one I posted is the player's real HP, not the graphical display value)


Any tips or guidance would be appreciated as I'm enjoying injecting new types of code other than the standard "don't subtract value, or unlimited x" just this lifesteal stuff isn't working out as I'd hope. Passive HP regen works fine tho.


Last edited by Hugo_the_Dwarf on Mon Apr 22, 2019 3:47 pm; edited 2 times in total
Back to top
View user's profile Send private message
Bloodybone
Newbie cheater
Reputation: 0

Joined: 07 Dec 2016
Posts: 21
Location: Germany

PostPosted: Mon Apr 22, 2019 5:48 am    Post subject: Reply with quote

Let me know if this code works:

Code:
label(code)
label(return)
label(p_string)
label(stringcmp)
label(stringcmp_loop)

NEWDamageUpdate:

code:
  push rax
  mov rax,[rbx+78]
  //mov rax,[rax+60]
  push rax
  push rcx
  push rdx
  lea rcx,[rax+60]
  lea rdx,[p_string]
  call short stringcmp
  test al,al
  pop rdx
  pop rcx
  pop rax
  jne Final
    movd eax, xmm0
    cmp eax,0
    jge Final
      mov rax,1407F2EA8
      mov rax,[rax+70]
      add rax,1000
      push rdi
      mov rdi,1407f2ea8
      mov [rdi+70],rax
      pop rdi
  Final:
  pop rax
  movss [rbx+70],xmm0
  jmp return

stringcmp:
push rsi
push rdi
mov rsi,rcx
mov rdi,rdx
xor rax,rax
stringcmp_loop:
lodsb
cmp byte ptr [rdi],al
je short @f
  pop rdi
  pop rsi
  xor al,al
  ret
@@:
inc rdi
test al,al
jne short stringcmp_loop
  pop rdi
  pop rsi
  mov al,1
  ret

p_string:
db 50,'layer',0

DamageUpdate:
  jmp NEWDamageUpdate
return:
Back to top
View user's profile Send private message
Hugo_the_Dwarf
Newbie cheater
Reputation: 0

Joined: 17 Apr 2019
Posts: 13

PostPosted: Mon Apr 22, 2019 10:35 am    Post subject: Reply with quote

Hey Bloodybone,

I tried out the String Compare logic and it works I'm just trying to follow your changes, and understand it more (the assembly commands, etc)

I'm super new to assembly (not programming, so stuff can go over my head on what it does)

Mainly the prefix/verb "short" in the call and jumps in your new code (does having it or not having that have different effects, as seen with my jumps)

I also noticed you used lea instead of the mov I had (I'm watching a YT vid on MOV vs LEA so maybe I'll understand soon)

Either way that solves my issue number 1). Would you be able to add some comments to your code so I can have an idea on what the steps are (I'm still trying to dig through, if you don't want to that's fine, just figure I'd learn faster if I had a rough idea of the steps and actions)

So Thanks again for this String Compare logic. I hope I can understand it enough to recreate it for other projects.
Back to top
View user's profile Send private message
Bloodybone
Newbie cheater
Reputation: 0

Joined: 07 Dec 2016
Posts: 21
Location: Germany

PostPosted: Mon Apr 22, 2019 11:00 am    Post subject: Reply with quote

I'm gonna add some comments to the code I originally posted Smile

Edit:

Here is the Code with the Comments, I hope they'll help Smile

Code:
label(code)
label(return)
label(p_string)
label(stringcmp)
label(stringcmp_loop)

NEWDamageUpdate:

code:
  push rax
  mov rax,[rbx+78]
  //mov rax,[rax+60]
  push rax
  push rcx
  push rdx
  lea rcx,[rax+60] // The difference between lea and mov is that mov, copies the value from the address in this case [rax+60] and lea gets the address of [rax+60]
  lea rdx,[p_string]
  call short stringcmp // The short prefix means that the call will be a "short call" which means that it only takes to bytes but can only jump a short distance instead of the normal five for a long or even 14 or 15 bytes for the far call
  test al,al // This checks if al is 0 and if it is the Zero-Flag is set
  pop rdx
  pop rcx
  pop rax
  jne Final // This jumps if the Zero-Flag is not set meaning that al had to be anything but 0 before
    movd eax,xmm0 I changed the movq to a moved because a float is only 4 bytes long and a movq moves 8 bytes
    cmp eax,0
    jge Final
      mov rax,1407F2EA8
      mov rax,[rax+70]
      add rax,1000
      push rdi
      mov rdi,1407f2ea8
      mov [rdi+70],rax
      pop rdi
  Final:
  pop rax
  movss [rbx+70],xmm0
  jmp return

stringcmp: // This function returns 1 if the both strings passed are 1:1 and 0 if they're not
/*
This function takes 2 pointers where each points to the start of the string
and then both of them are compared to each other
*/
// Save Registers
push rsi
push rdi
// Move the pointers to the start of the strings in to the saved registers
mov rsi,rcx
mov rdi,rdx
// Set rax to 0
xor rax,rax
stringcmp_loop:
lodsb // This instruction moves the byte at [rsi] into al and increments rsi
/* You could also write it like this:
mov byte ptr al,[rsi]
inc rsi

Which means that it moves the Char of the first string into al and makes rsi point to the next char in the first string
*/
cmp byte ptr [rdi],al // And then Compares it to the Char of the other String
je short @f // This then jumps to the next @@ if the Chars are the same
  pop rdi
  pop rsi
  xor al,al // Sets al to zero
  ret
@@:
inc rdi // rdi now points to the next Char in the second string
test al,al // Check if the CHar is 0 meaning the string has ended
jne short stringcmp_loop // If the string has not ended, loop
  pop rdi
  pop rsi
  mov al,1
  ret

p_string:
db 50,'layer',0 // This equals the String 'Player' and the 0 at the end means that the String is zero-teminated

DamageUpdate:
  jmp NEWDamageUpdate
return:
Back to top
View user's profile Send private message
Hugo_the_Dwarf
Newbie cheater
Reputation: 0

Joined: 17 Apr 2019
Posts: 13

PostPosted: Mon Apr 22, 2019 1:29 pm    Post subject: Reply with quote

Thank you for your comments, this is helping me greatly understand what's going on.

I'm still fuzzy with the stuff going on in the stringcmp function but I think I can piece things together from your comments and some Google-Fu.

now I need to Google about what registers do what since there is AL, AC, etc? and the RAX. I know XMM# are mostly for floats/scalar values?

Still much I need to study up on, I wouldn't figure a simple feature would be so troublesome for a beginner for me to do Sad so thank you for your insight.

Also the explaination on the short, long, and far was very helpful

EDIT:
I figured out the crash and it was with this section:

Code:
      mov rax 1407F2EA8
      mov rax,[rax+70]
      mov rax,[rax]
      add rax, 1000


The fixed logic is:

Code:
      mov rax,[1407F2EA8]
      lea rax,[rax+70]
      add [rax],10000


any other combo or lack of '[]'s causes a hard crash (I finally found the "watchlist" in the view section of memory view, it's a godsend for debugging)



I'm going to update the OP with the final code, and paste the broken code below for future ref for whoever wanted to see my failure.

Code:
aobscanmodule(DamageUpdate,horus_x64.exe,F3 0F 11 43 70 48 8B) // should be unique
alloc(NEWDamageUpdate,$1000,"horus_x64.exe"+54A23)

registersymbol(DamageUpdate)
registersymbol(NEWDamageUpdate)

label(code)
label(return)

NEWDamageUpdate:

code:
  push rax
  mov rax,[rbx+78]
  mov rax,[rax+60]
  cmp rax,'Play'
  je Final
    movq rax, xmm0
    cmp rax,0
    jnle Final
      mov rax 1407F2EA8
      mov rax,[rax+70]
      mov rax,[rax]
      add rax, 1000
  Final:
  pop rax
  movss [rbx+70],xmm0
  jmp return

DamageUpdate:
  jmp NEWDamageUpdate
return:


[DISABLE]

DamageUpdate:
  db F3 0F 11 43 70


dealloc(NEWDamageUpdate)
unregistersymbol(DamageUpdate)
unregistersymbol(NEWDamageUpdate)
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