Analysis of anti-tampering and detection mechanisms in Pixel Gun 3D's libunityads.so library.
Architecture: ARM64 (AArch64)
| # | Detection Type | Function | Offset | Severity |
|---|---|---|---|---|
| 1 | Frida/Injection Detection | IsReady(), GetPlacementState() |
0x1220, 0x2474 | Critical |
| 2 | APK Integrity Check | IsReady(), GetPlacementState() |
0x1220, 0x2474 | High |
| 3 | SELinux Context Check | IsReady(), GetPlacementState() |
0x1220, 0x2474 | Medium |
| 4 | Directory Permission Check | IsReady(), SupportedOrientations(), GetPlacementState() |
Multiple | Medium |
| 5 | XOR String Obfuscation | All detection functions | N/A | Evasion |
Location: IsReady() @ 0x1220, GetPlacementState() @ 0x2474
Mechanism:
// Opens /proc/self/fd (XOR obfuscated)
DIR *dir = opendir("/proc/self/fd");
while ((entry = readdir(dir))) {
// Read symlink target for each fd
readlink("/proc/self/fd/<n>", buf, 255);
// Check if target contains suspicious string (e.g., "frida", "lspatch")
if (sub_3394(buf, pattern)) { // strstr-like function
return 1; // DETECTED
}
}What it detects:
- Frida injection (frida-server, re.frida.server)
- LSPatch/LSPosed modifications
- Other injection frameworks with open file handles
Location: IsReady() @ 0x1220, GetPlacementState() @ 0x2474
Mechanism:
// Get APK path via JNI
sourceDir = ApplicationInfo.sourceDir;
// Open APK via direct syscall (bypasses hooks)
int fd_syscall = syscall(__NR_openat, -100, sourceDir, O_RDONLY);
// Open APK via libc (potentially hooked)
FILE *fp = fopen(sourceDir, "r");
int fd_libc = fileno(fp);
// Compare file stats
fstat(fd_syscall, &stat1);
fstat(fd_libc, &stat2);
// If st_dev or st_ino differ, file is being redirected/hooked
if (stat1.st_dev != stat2.st_dev || stat1.st_ino != stat2.st_ino) {
return 1; // DETECTED - APK redirection
}What it detects:
- APK file redirection hooks
- Virtual filesystem overlays
- File-based tampering frameworks
Location: IsReady() @ 0x1220, GetPlacementState() @ 0x2474
Mechanism:
// Open /proc/self/attr/current or similar (XOR obfuscated path)
FILE *f = fopen(obfuscated_path, "r");
fgets(buf, 256, f);
char *context = strtok(buf, " \t\n");
// Compare against expected context (XOR obfuscated)
if (strcmp(context, expected_context)) {
// Context mismatch - send detection message
UnitySendMessage(...);
}What it detects:
- Modified SELinux contexts (Magisk, root)
- Process running in unexpected domain
- Zygisk/Riru injection (context changes)
Location: IsReady(), SupportedOrientations(), GetPlacementState()
Mechanism:
dataDir = ApplicationInfo.dataDir;
snprintf(path, 200, "%s/..", dataDir);
if (!access(path, W_OK)) { // Parent dir IS writable
return 1; // Rooted device or permission bypass
}All sensitive strings are XOR-encrypted at compile time:
// Global XOR keys (can be swapped!)
char byte_B7A0 = 'f'; // 0x66
char byte_B7A4 = 'c'; // 0x63
// Example: Building "/proc/self/fd"
name[0] = byte_B7A0 ^ 0x49; // '/'
name[1] = byte_B7A0 ^ 0x16; // 'p'
name[2] = byte_B7A0 ^ 0x14; // 'r'
// ... etcAnti-Analysis Trick: GetOrientationState() swaps the XOR keys:
__int64 GetOrientationState() {
char v0 = byte_B7A4;
byte_B7A4 = 100; // 'd'
byte_B7A0 = v0; // now 'c'
return 1;
}| Function | Offset | Contains Detection | Returns |
|---|---|---|---|
IsReady() |
0x1220 | Frida, APK, SELinux, DirPerm | 1 |
GetPlacementState() |
0x2474 | Frida, APK, SELinux, DirPerm | 0 |
SupportedOrientations() |
0x1FB8 | DirPerm only | 0 |
IsDeviceSupported() |
0x1FB0 | None (decoy) | 0 |
GetVersion() |
0x3268 | None | 10224 |
GetOrientationState() |
0x3274 | Swaps XOR keys | 1 |
IsInitialized() |
0x3294 | None | 0 |
You should be able to figure it out yourself with all this given information (if you can't for some reason dm me on discord _pythr).
Key hooks needed:
opendir()- Block /proc/self/fd enumerationreadlink()- Hide frida file descriptorsaccess()- Return expected permissionsfstat()- Return consistent stats for APKfopen()/fgets()- Spoof SELinux context filesyscall()- Intercept direct openat syscalls
docs/functions.md- Detailed function analysisdocs/strings.md- XOR-decoded strings