Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- How to call GTA V Natives easy Using Hash.
- i'm going to explain to you how you going to use it in this tutorial it dosn't not contain any info on how make SPRX and only how GTA calling natives by the native hash
- First of all you have to know that the natives called by an Interpreter by their hashes, that means that inside the executable they are gets called by the hash.
- To find where the natives are stored, you need to find the function where the hashes are passed as parameters.
- This is quite easy, you just need to get one of these hashes, and search with IDA where they are used.
- this way makes it very easy to find them
- example, here is the code where the executable store the hash and the function of GET_PLAYER_PED.
- Code:
- lis %r3, 0x6E31 # 0x6E31E993
- lis %r4, ((offset_GET_PLAYER_PED+0x10000)@h)
- ori %r3, %r3, -0x166D # 0x6E31E993
- addic %r4, %r4, -0x4D28 # offset_GET_PLAYER_PED
- bl AddNative
- You can see that the 2 high order bytes of the native's hash are stored into the third register.
- Then the 2 low order bytes are added to the third register too.
- The executable do exactly the same thing for all the natives, this mean that you just need to get the value of 'lis %r3' to get where the natives hash are added.
- I'm gonna do the work for you, the value of 'lis %r3' is 0x3C60, this mean that if you want to know where a particular hash is added, you just need the two high order bytes of the hash, you search via your disassembler where 0x3C60XXXX is used.
- So, now you can find where is added a particular hash.
- If you have completly read the assembly code, you may have see that the hash is stored in r3 and a function is stored in r4.
- Then a function (that I called AddNative) is called.
- Let's look at this function. (look at it on your disassembler, the code is too long to be posted here).
- It may be hard to read, so I did a C version.
- Code:
- struct Native
- {
- struct Native* pLastNativesTable; // + 0x00
- unsigned int uiNativeFunctions[7]; // + 0x04
- unsigned int uiNativeCount; // + 0x20
- unsigned int uiNativeHashes[7]; // + 0x24
- };
- struct Native* g_Natives[256];
- void AddNative(unsigned int a_uiNativeHash, unsigned int a_uiNativeFunction)
- {
- unsigned int l_uiTableIndex;
- struct Native* l_pNative;
- unsigned int l_uiIndex;
- l_uiTableIndex = a_uiNativeHash & 0xFF;
- l_pNative = g_Natives[l_uiTableIndex];
- if(l_pNative == 0 || l_pNative->uiNativeCount == 7)
- {
- l_pNative = (struct Native*)malloc(sizeof(struct Native));
- memset(l_pNative, 0x00, sizeof(struct Native));
- l_pNative->pLastNativesTable = g_Natives[l_uiTableIndex];
- g_Natives[l_uiTableIndex] = l_pNative;
- }
- l_uiIndex = l_pNative->uiNativeCount;
- l_pNative->uiNativeFunctions[l_uiIndex] = a_uiNativeFunction;
- l_pNative->uiNativeHashes[l_uiIndex] = a_uiNativeHash;
- l_pNative->uiNativeCount++;
- }
- You can see that the hashes are simply stored in a global array.
- This mean that we just need to find the address of this global array, if you understand PowerPC it's easy as hell with all the hint I gave you.
- So know, how to get a native's function via hash ?
- Just like this,
- Code:
- unsigned int GetNativeFunction(unsigned int a_uiNativeHash)
- {
- unsigned int l_uiTableIndex;
- struct Native* l_pNative;
- int l_bQuitLoop;
- int i;
- l_bQuitLoop = 0;
- l_uiTableIndex = a_uiNativeHash & 0xFF;
- l_pNative = g_Natives[l_uiTableIndex];
- if(l_pNative)
- {
- while(l_bQuitLoop == 0)
- {
- for(i = 0; i < l_pNative->uiNativeCount && l_bQuitLoop == 0; i++)
- {
- if(l_pNative->uiNativeHashes[i] == a_uiNativeHash)
- {
- return l_pNative->uiNativeFunctions[i];
- }
- }
- if((l_pNative = l_pNative->pLastNativesTable) == 0)
- {
- l_bQuitLoop = 1;
- }
- }
- }
- return 0;
- }
- So now, we know how these natives are stored, where they are stored, how to find a specific native's function via the hash.
- How to call the function ? How the parameters are passed ? How the return value are fetched ?
- Well, to call the natives you need to be in the game's main thread.
- This is the difficult part, many guys on the internet fail at this part.
- But with my method it's quite easy !
- If you read the decompiled script you can see that many natives are called at every frame.
- And one that is called very very often is PLAYER_ID (0x8AEA886C).
- So, why don't we hook this native ? It is called from the main thread, and even more ! By the scripting interpreter itself !
- To do this, you need to write a function, I just modified my GetNativeFunction to do so.
- Code:
- unsigned int HookNative(unsigned int a_uiNativeHash, unsigned int a_uiNewFunction)
- {
- unsigned int l_uiTableIndex;
- struct Native* l_pNative;
- int l_bQuitLoop;
- int i;
- l_bQuitLoop = 0;
- l_uiTableIndex = a_uiNativeHash & 0xFF;
- l_pNative = g_Natives[l_uiTableIndex];
- if(l_pNative)
- {
- while(l_bQuitLoop == 0)
- {
- for(i = 0; i < l_pNative->uiNativeCount && l_bQuitLoop == 0; i++)
- {
- if(l_pNative->uiNativeHashes[i] == a_uiNativeHash)
- {
- l_pNative->uiNativeFunctions[i] = a_uiNewFunction;
- return 1;
- }
- }
- if((l_pNative = l_pNative->pLastNativesTable) == 0)
- {
- l_bQuitLoop = 1;
- }
- }
- }
- return 0;
- }
- So now, you are on the main thread, but you need to call the original function.
- With the good parameters.
- Let's see how are passed the parameters on the call of the natives functions.
- For this I'm gonna analyze GET_ENTITY_COORDS native, now that you know where to find them let's see what it looks like.
- Code:
- mflr %r0
- bl sub_1776544 // <-- store LR
- stdu %sp, var_90(%sp)
- std %r0, arg_A0(%sp)
- mr %r31, %r3 // <-- R3 is the first parameter passed to the native function, and it's now stored to R31
- addic %r3, %sp, arg_70
- lwz %r4, 8(%r31) // Here you can see that it loads something from R31 + 8 and store it to R4
- lwz %r5, 4(%r4) // Here you can see that it loads something from R4 + 4 and store it to R5
- lwz %r4, 0(%r4) // Here you can see that it loads something from R4 + 0 and store it to R4
- cntlzw %r5, %r5
- extsw %r4, %r4
- extrwi %r5, %r5, 1,26
- xori %r5, %r5, 1
- bl sub_398EF0 // Here it calls the GET_ENTITY_COORDS function
- lwz %r3, 0(%r31) // Here you can see that it loads something from R31 + 0 and store it to R3
- lfs %fp1, arg_70(%sp)
- lfs %fp2, arg_74(%sp)
- lfs %fp3, arg_78(%sp)
- stfs %fp1, 0(%r3) // Here you can see that it store a float from fp1 and store it to R3 + 0
- stfs %fp2, 4(%r3) // Here you can see that it store a float from fp2 and store it to R3 + 4
- stfs %fp3, 8(%r3) // Here you can see that it store a float from fp3 and store it to R3 + 8
- addi %sp, %sp, 0x90
- b loc_17765A8
- By analyzing others native, you can assume that a structure is passed to the native.
- And this structure looks likes this.
- Code:
- struct NativeArg
- {
- unsigned int* p_uiReturnValues;
- unsigned int ui_Unknown;
- unsigned int* p_uiArgValues;
- };
- So, know we know exactly how to hook properly a native.
- Code:
- void (*Original_Player_ID)(struct NativeArg*);
- void Hook_Player_ID(struct NativeArg* a_pArg)
- {
- // Here you make your calls to the natives.
- // Here you must call the original PLAYER_ID() function
- Original_Player_ID(a_pArg);
- }
- // in your main function
- Original_Player_ID = (void (*)(struct NativeArg*))GetNativeFunction(0x8AEA886C);
- if(HookNative(0x8AEA886C, Hook_Player_ID))
- {
- // successfully hook
- }
- else
- {
- // hook failed
- }
- Finally, you just need to make the calls to the natives you want to.
- For this you need get the native's function address, you need to allocate properly the structure that pass your parameters and fetch the return values.
- Then call the native with the structure as a parameters, to fetch returned values, you just need to look at the structure
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement