Jump to content
  • 0

Question about hex language (il2cpp)


Question

Posted

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

12 answers to this question

Recommended Posts

  • 0
Posted
19 hours ago, qwer098 said:

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

Headshot is a Boolean meaning it returns true or false, setting R0 to 1 makes it return true, setting it to 0 makes it return false, BX LR ends the function and returns to the code that called it.

  • 0
Posted (edited)
19 hours ago, qwer098 said:

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

Hi! Hex isn't really a language but rather a data representation. Hex can forms almost everything, almost the same to other data-types like dword or etc. You can even convert a plain-text into a hex or others. Comes to the question;
 

mov /// Moves signed int/value into target register/operand
r0 /// Register location
#1 /// Moves signed value 1 into the register

#1 is equals to True which the game/projectiles will always register any shots as an Headshot. Some games,  #1 value is inherit range of value that the game already sets. For example; if the game has an auto-update enabled predefine as follow:
 

1 #Auto Update enabled
2 #Connection Error
3 #Update Received
4 #No Updates

mov r0, #[1-4] /// Choose the corresponding, and it will be that

Mostly it would be 0-1 (False/True) that 0 will be nothing or false, the game will ignore and continue with next instruction. As for "bx lr"; it's a common way to indicate "The End of Function". This tells the game that;" Hey it's the end, Jump to default or next function". If it's a lazy patching, this will neglect any instruction after "bx lr" in that same function. This can leads to Memory Detection, although it's mostly fine.

Edited by MainC
  • 0
Posted (edited)
On 9/7/2022 at 12:28 AM, qwer098 said:

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

Most tutorials on hex patching do not explain how hex patching works. I'll explain exactly what hex patching is and how it works, from the very beginning.

@MainCand @BadCasedid a great job explaining, but they didn't go in-depth.

To understand what the libil2cpp.so file is, you have to understand how the game's libil2cpp.so file is generated. All games with libil2cpp.so files are made with Unity's Il2cpp Backend. Unity is a game engine where you write your code in the C# programming language, and the engine compiles it into an apk. Most of the game's code, such as the 'headshot' method, is converted from C# to IL (Intermediate Language) to C++. This C++ code is then compiled to assembly code, which is encoded into hex and stored in the libil2cpp.so file. Different devices use different assembly languages (called architectures) - android has armv7 (32bit) and armv8 (64bit). There is a libil2cpp.so file for each architecture. In most Unity games, there is armv7, armv8, and sometimes x86 support.  In armv7, armv8, and x86, all assembly instructions are 4 bits long when encoded into hex.

When the game executes a method like the 'headshot' method, the hex for the headshot method is taken from the libil2cpp.so file for the device's respective architecture, decoded back into assembly code, and run.

This is not the exact process, but I left some information out and simplified some stuff.


To visualize this, I like comparing c code to assembly code to hex. Arm Hex Converter Online can be used to converted between assembly code and hex, and Compiler Explorer can be used to convert c code to assembly code. There is currently no way to perfectly convert from assembly code back to c code, so you will have to learn assembly code to understand it.

So what does 

Mov R0, #1
bx lr

mean? I like looking at it in c. This is the same as:

return(1)

There are multiple ways to write this in assembly, so compiler explorer might contradict us.  But

Mov R0, #1
bx lr

 is the simplest way to do it.

 

Let's make up an example of how we would use hex patching and walk through it step by step. Say that the offset for the 'headshot' method is 0x67AB0AB. This means that the function's code begins at the 67AB0AB's byte of the libil2cpp.so file. The offsets are usually in hexadecimal - this is what most hex editors use, and what most tools / resources like dnspy and Il2cppDumper use. This represents the decimal (base10) number 108703915. So, we know that the function begins at the 108703915th byte of the libil2cpp.sp file. If we go to this offset in our hex editor (most hex editors use hexadecimal offsets, so we go to offset 67AB0AB, the first 8 bytes are 06 00 00 15 00 88 FC BF. Each assembly instruction is 4 bytes, so we are looking at the first 2 assembly instructions of the function. We edit this to our hex (if we want to working with armv7, and we want the function to always return 1, we use the hex 01 0 0A0 E3 1E FF 2F E1, which decodes to

Mov R0, #1
bx lr

. We only have to edit the first 8 bytes (2 instructions) of the function because if we always edit the first instructions to return, the function will always return before it executes any other instructions. This is how return statements work in almost all programming languages. For this reason, we edit only two instructions and do not have to overwrite the whole function. We need to edit two instructions and not just one because

return(1)

takes two instructions in assembly. If we want to return a number that cannot be expressed in one statement (ex: for armv8, the maximum number you can directly use is 65535, or 0xffff in hexadecimal), or we want to do something more complicated than always returning a number, we may need more than two instructions. In this case, we overwrite as many instructions as we need. If our new function is very complex, it might be longer than the original function. This will rarely happen, but just in case, you should use workarounds when your new function is long.

 

I do not currently know how to find where a function ends, only how it starts. A .so file is a linux shared library file (armv7, armv8, and x86 architectures use linux), so if anybody wants to try to find out an easy way to find out where a function ends, or even better, a way to list all of the functions in a given .so file, (it would be awesome if somebody does this!), this is a starting point to start researching.

 

Hopefully this is a helpful guide and it explains hex patching in-depth! Sorry I wrote so much, I got a little carried away 😛

 

NOTE: In most assembly languages, including armv7, armv8, and x86, the numbers 1 and 0 are used respectively instead of true and false. The hexadecimal representations of 1 and 0 (0x1 and 0x1) can also mean true and false respectively.

Edited by HorridModz
  • 1
Posted (edited)

@HorridModz Provides a Nice detailed explanation. Hex patching is rather easy as it's only a form of data that simply overwrited / added, the important thing is: to understand the assembly itself. Probably I'll provide a little more coverage about the topic.

[ Usage ]

  • - Replacement: You can only replace hex at fixed length. The hex length is depends on Data types that you're dealing with, it could be a Set / Subset Instruction. In general it can take 2-4 bytes, make sure to read the instruction as a string not in hex form. More simple coverage on the next section.
  • - Addition: This used when doing references such as memory allocation. To manually add a custom instruction; you need to write it in empty/unread memory region (the indication is: it's filled with 00) and then reference the game function to your allocated memory. It's the general idea, you shouldn't be worry about it; most tools already provide this feature.

Why no substraction? You can't remove a function even after proper patching and 'disabling' any reference to that function, directly or memorily. It leads to data corrupt/crashing; so it's uncommon. You can use this to cut fake data (such as malware app that filled with 00 to make a large size) because "they" only add additional hex at the end. There's more reason to this.

Data Types ]

  • - Function/Instructional data takes 4 length; 
mov r0, r0 #00 00 A0 E1
bx lr #1E FF 2F E1
  • - Inner Function/Subset Instruction takes 2-4 length. It's called as thumb and can be found on 32-bit architecture.
mov r0, r0 #00 46
bx lr #70 47

[ Patching ]
- Lazy Patch: You can 'remove' instruction without removal, simply fills with 00. This off course wouldn't work if the app have high security but the benefit is: You don't need to understand Assembly.
- Proper Patch: You can just memorize this common patch and applies it anywhere; it's simple and not a time consuming. Well, for more instruction patches; you need to learn assembly. Learn returning values and Jump instruction (BL/JMP) patches would mostly help.

[Patch 1]
Instruction: mov r0, r0
Arm Encoded: 00 00 A0 E1
Thumb Encoded: 00 46

[Patch 2]: Usually a boolean/takes value
Instruction: mov r0, #0
Arm Encoded: 00 00 A0 E3
Thumb Encoded: 4F F0 00 00

[End Patch]: Indicate closing, put after patches
Instruction: bx lr
Arm Encoded: 1E FF 2F E1
Thumb Encoded: 70 47

[ Misc ]

  • - 00 is equal to 1 Hex
  • - Hex can present in 00 or 0x00
  • - Thumb can be found on 32-Bit Architecture (x86, Armeabi / Armv7 / Arm32 )
  • - Thumb can also takes 4 length; the same length as Arm encoded
  • - To differentiate Thumb and Arm encoding; 1) Copy the instruction hex, 2) Compare hex and instruction, including after and before offset
Edited by MainC
  • 0
Posted
On 9/7/2022 at 1:28 PM, qwer098 said:

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

I recommended used arm code

 

  • 0
Posted
On 9/11/2022 at 9:41 PM, HorridModz said:

 

I do not currently know how to find where a function ends, only how it starts

functions start with a push instruction and end with a pop instruction, as you can see in the official documentation https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/how-to-call-a-function-from-arm-assembler

  • 0
Posted
On 9/7/2022 at 1:28 PM, qwer098 said:

After watching the tutorial video about hex editing, I had a question about hex language.

In that video (game was archero), he found offset of 'headshot' and replaced the value to 'mov r0,#1' and 'bx lr' that converted into arm32. And all attack became headshot.

What is meaning of 'mov r0,#1' and 'bx lr'? Is there any othet useful texts that can be used for hitbox or aimbot mod?

Sorry for my bad English, translator didnt work properly, so I just write it down.

It's like this 

Do auto headshots ? 

And surely you want it Yes = true no = false

And example 

Boolean Offset 0x39493  

What are Booleans ? Boolean like yes or not , True or false 

And how to answer the Boolean offset example above? 

The answer is like this 

~A Mov r0, #1 What's this ? 

This is arm code not hex 

And if it's just ~A mov r0, #1 game will crash so we end with code arm ~A bx lr 

So auto headshot? ~A mov r0, #1 (Yes) if not ~A mov r0, #0

 

 

 

  • 0
Posted (edited)
On 11/21/2022 at 12:00 AM, under_score said:

functions start with a push instruction and end with a pop instruction, as you can see in the official documentation https://community.arm.com/arm-community-blogs/b/architectures-and-processors-blog/posts/how-to-call-a-function-from-arm-assembler

This is certainly True, there's also different start-end for function. Identifying start-end Function is also still confuse me but there's some pattern:

  • > MOV [Random?] - BX PC / BX LR
  • > PUSH - POP
  • > ADD - LDR

Some Function can contain one instruction: CDPLT or Memory Segment that also have start-end. Altho those Pattern covers most of the Function, some Memory Segment such as .bss also have SHT Size which defines start-end, I'm not sure if this is a function but the Methodology I'm currently using is:

  • - Using Dissasemblers such as IDA Pro or Ghidra
  • - Using Patterns in Hex form
  • - Find repeated AoB:
Quote

00 C6 8F E2  C4 CA 8C E2 D8 F6 BC E5 00 C6 8F E2 C4 CA 8C E2

In the end, I'm still confused to find function in other Memory Segment. The Patterns above is mostly are close to each other (In the same Memory Segment?) but what about in other segment?

Edited by MainC
Question
  • 0
Posted
3 hours ago, MainC said:

I'm still confused to find function in other Memory Segment.

thats not how Executable & Linkable Format (the executable type used by linux and Android ) works

all functions must be in the .text segment (xa in gg)

because of how permissions work in linux/Android:

 

(r = readable, w=writable, x = executable)

.text: r-x, meaning it can be read and executed, but not written

.data: r, meaning it can only be read, not executed or written

if you try to place a function (label) on .data, its not going to work, because the kernel sees that its not executable

 

  • 0
Posted

Hi! Thank You for replying. Here's some thought:

Quote

.data: r, meaning it can only be read, not executed or written

Since it's Read Only, what the difference with .rodata? Isn't it .data is still writeable and .rodata not?

Quote

all functions must be in the .text segment (xa in gg)

The core function that contain codes is definitely on .TEXT segment. Upon checking, it's True that all the previous Pattern are on .TEXT Segment except: (ADD-LDR) are on separate .PLT Segment. Also come to realize that Other Segment (.bss, .data) are mostly for preservation, got it from this: Section and Label. This clears my confusion about Function on Other Segment, since Function are basicly calls to .data or .bss

  • 0
Posted
16 hours ago, MainC said:

Since it's Read Only, what the difference with .rodata? Isn't it .data is still writeable and .rodata not?

.rodata is just a alias for .data in nasm.

.data is not writable.

but i think .bss is writable

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.