Unity : ACTk

Section's for general approaches on hacking various options in games. No online-related discussions/posts OR warez!
Post Reply
User avatar
RCE Fanatics
RCE Fanatics
Posts: 842
Joined: Fri Feb 15, 2019 5:45 pm
Reputation: 1274

Unity : ACTk

Post by cfemen »

ACTk = Anti Cheat Toolkit.
Most common anti cheat plugin for Unity thats obfuscating vars and spawns fake values.

i guess for some its interesting if i share some infos about ACTk, i hacked some games in the past with it and analyzed it :)

if the game has mono:
open assembly.dll
search for ObsfuscatedInt/ObfuscatedFloat

change the code so thats only the return is left on follow functions:
Decrypt Function:
return value;
Encrypt Function:
return value;
return 0;
return hiddenValue;

thats it now you can scan for the real values.
if you found the real values, find out what writes to this address and save the mono symbols of the address.
now start game again with the original dll -> go to the symbol and write your inject :)

dont let you fool from ATKc :?
if you are searching for a Integer in the function/method that you found you would expect:

Code: Select all

mov [rax+4],ecx // something typical for Integers
ATKc may obfuscates this into something like this:

Code: Select all

movups [rdi+40],xmm0 // ACTk method to write a Integer
the real value is on rdi+44 or rdi+3C (4 bytes plus/minus)

Actually you can use the "Anti-Cheat" to cheat more, and with more i mean finding values that would be hard to find (very rare items with only 1 or 2 in the inventory -> not enough to scan for it)
Especially useful for special items that dont use the same shared instructions like the normal items in the inventory.

How To Do?

Use Mono and find InternalDecrypt(every type has its own!) from ACTk now look for the XOR
(sample from my machine, your InternalDecrypt may be slightly different coz JIT)

Code: Select all

  movsxd  rax,dword ptr [rsi+04]
  movsxd  rcx,dword ptr [rsi]
  mov rdi,rax
  xor edi,ecx

every obfuscated(Integer) value will use this function!
set a breakpoint after the xor edi,ecx to see the real value.
if its the same amount as the value you are looking for -> copy the value from rsi+4 convert it to decimal and you can now scan for a exact value and bypass the complete obfuscation/fake values.

or you can just write a filter to set the amount to 999 or more:

Code: Select all

xor edi,ecx
cmp edi,2 // 2 of the rare items
jne @f
mov edi,3E7
now do trigger the game to read/write the value you are looking for and you will get 999.

ok how about IL2CPP?

IL2CPP generates completly different AOBs than Mono, and you cant just edit the DLL or see symbols.
But the AOBs will be the same on every machine :)

i searched for ACTk's InternalDecrypt on some IL2CPP+ACTk games to find the important AOBs, and i created some own projects with ACTk and compiled some tests, the AOBs never changed.

you may get more than 1 result -> just return the function or NOP the XOR and if nothing happens with the values then its not the right spot :lol:

Code: Select all

// Integer
//04 33 3B E8 will lead to this InternalDecrypt:
mov edi,[rbx+04]
xor edi,[rbx]

Code: Select all

// Floats
//33 C9 31 44 24 40 will lead to this InternalDecrypt:
xor ecx,ecx
xor [rsp+40],eax

Code: Select all

// Doubles
//48 31 44 24 50 will lead to this InternalDecrypt:
xor ecx,ecx
xor [rsp+50],rax
and yeah every value in game will use one of the InternalDecrypt functions :) now you can use breakpoints to see the real value and copy the obfuscated value, convert it to scan for it or just let it run to the RET to find the game function thats read/writes the value.

last but not least:


after the RET from InternalDecrypt is always a call
this call on the screenshot is GenerateCryptoKey for Integers right after the RET from InternalDecrypt for Integers.

inject on the beginning of GenerateCryptoKey -> return 0 -> now you can scan for the real value and easily change every integer.

Floats/Doubles are a bit harder coz ACTk spawns a lot more fake values and the values in InternalDecrypt are always on the stack.
but i think you got the idea how useful it is if you are in the InternalDecrypt function and you can see/manipulate/backtrace all obfuscated vars.

so thats it :)

The Il2CPP AOBs are for the ACTk 2.x
just updated ACTk to newest version, its still the same
i will update this post if somethings change and add the AOBs :)

User avatar
RCE Fanatics
RCE Fanatics
Posts: 842
Joined: Fri Feb 15, 2019 5:45 pm
Reputation: 1274

Re: Unity : ACTk

Post by cfemen »

Some additionally tips:

i just created an table for Ape Out and i couldnt find with my AOBs the InternalDecrypt, so i show another way that i used to find it (Ape Out uses IL2CPP)

every ObscuredInt / Float / Double has an ToString Function ( and several overloadings for it)

search for:

33 D2 E8 ** ** ** ** 33 D2 89 44 24 30 48 8D 4C

44 24 30 E8 ** ** ** ** 33 D2 F3 0F 11 44 24 30

44 24 30 E8 ** ** ** ** 33 D2 F2 0F 11 44 24 30

and you will find ToString

screenshot from my sample with an float:

first call in the ToString is always InternalDecrypt

screenshot from Ape Out with same AOBs for floats ( game does not show any UI values, its just an example for the AOBs to find InternalDecrypt)

and again : these AOBs are only for IL2CPP compiled games, if the game uses Mono look first post how to find the things :)


Another trick:

you already know how to find GenerateKey, return 0 is mostly enough to find/scan for Ints.
for floats/doubles its good to get rid of the fakes:

find ObscuredCheatingDetector:IsRunning
go into InternalDecrypt and the call after the xor is it:


return 0 and the fake values are killed :)

now you can scan for ints/floats/doubles, but keep in mind floats/doubles might using some additionally math.

example floats:
ingame value : 13 = 10.21000004
ingame value : 29 = 24.73872185
ingame value : 81 = 106.8166885

but with return 0 on GenerateKey and return 0 on ObscuredCheatingDetector you can now easily scan for values and change them without injecting.


How to edit values directly:
Lets take a look how a Obfuscated Value is stored:
Unity will always have a nativePtr on 0x10, so the Struct/Class values will always be after 0x10.
( 0x00 = nativeKlass Ptr| 0x08 = -nativeMonitor Ptr)
Fun Fact : every Component is a nativePtr (coz this Cheat Engine will crash if you try to Mono-Dissect)
as example : if you want a perfekt pointer to the player positions you need to call UnityEngine:Component:GetTransform with 1 argument (your player address)
coz everything that accesses the positions will be handled native in the UnityEngine.dll
okay enough, lets go back to ACTk:
on my sample struct the ObfuscatedInt is at +0x18:


cheat engine interpreted it as 2 floats.
the first "float" is the key
the second "float" the obfuscated value

so get the key xor it with the value, manipulate it and save it:


Code: Select all

local ObscuredInt = Class+0x18
local Key = readInteger( ObscuredInt )
local ObfuscatedValue = readInteger( ObscuredInt + 0x04 )
local Value = Key ~ ObfuscatedValue
Value = Value + 1
Value = Key ~ Value
writeInteger( ObscuredInt + 0x04, Value )
so know you know how its stored and you can edit Obfuscated values without any inject, you just need the Class+Offset.
another Fun Fact : enums/enum structs will also be saved without that the cheat engine mono features can detect it.
as example : if you have a enum struct with 3 bools (true,false,true) and a string then you will just see in memory 101 and then a address(0x10 = string-length | 0x14 = unicode-chars)
cheat engine mono dissect wont show you any names/symbols if there is a enum/enum struct in the dissected class.

Post Reply

Who is online

Users browsing this forum: No registered users