mirror of
https://github.com/gmh5225/awesome-game-security
synced 2026-06-21 13:56:22 +00:00
archive: add 5 repo prompt(s) [skip ci]
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,71 @@
|
||||
Project Path: arc_CrackerCat_strongR-frida-android_veg7qg9p
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_CrackerCat_strongR-frida-android_veg7qg9p
|
||||
├── README.md
|
||||
└── img
|
||||
├── 1.png
|
||||
├── 2.png
|
||||
└── 3.png
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
更新了一下patch,main报错的问题应该没有了
|
||||
# strongR-frida-android
|
||||
|
||||
Follow [FRIDA](https://github.com/frida/frida) upstream to automatic patch and build an anti-detection version of frida-server for android.
|
||||
|
||||
跟随 FRIDA 上游自动修补程序,并为 Android 构建反检测版本的 frida-server。
|
||||
|
||||
**Hint: Don't fork this repository**
|
||||
|
||||
## Patchs
|
||||
|
||||
[Git Patch Files](https://github.com/AAAA-Project/Patchs/tree/master/strongR-frida/frida-core)
|
||||
|
||||
|module|name|
|
||||
|-|-|
|
||||
|frida-core|0001-string_frida_rpc.patch|
|
||||
|frida-core|0002-io_re_frida_server.patch|
|
||||
|frida-core|0003-pipe_linjector.patch|
|
||||
|frida-core|0004-io_frida_agent_so.patch|
|
||||
|frida-core|0005-symbol_frida_agent_main.patch|
|
||||
|frida-core|0006-thread_gum_js_loop.patch|
|
||||
|frida-core|0007-thread_gmain.patch|
|
||||
|frida-core|0008-protocol_unexpected_command.patch|
|
||||
|
||||
## Download
|
||||
|
||||
[Latest Release](https://github.com/hzzheyang/strongR-frida-android/releases/latest)
|
||||
|
||||
## References
|
||||
|
||||
- [https://github.com/feicong/strong-frida](https://github.com/feicong/strong-frida)
|
||||
- [https://github.com/qtfreet00/AntiFrida](https://github.com/qtfreet00/AntiFrida)
|
||||
- [https://t.zsxq.com/miIunQN](https://t.zsxq.com/miIunQN)
|
||||
- [https://github.com/darvincisec/DetectFrida](https://github.com/darvincisec/DetectFrida)
|
||||
- [https://github.com/b-mueller/frida-detection-demo](https://github.com/b-mueller/frida-detection-demo)
|
||||
|
||||
## Thanks
|
||||
|
||||
- [@feicong](https://github.com/feicong)
|
||||
- [@r0ysue](https://github.com/r0ysue)
|
||||
- [@hellodword](https://github.com/hellodword)
|
||||
- [@qtfreet00](https://github.com/qtfreet00)
|
||||
|
||||
## Discussion
|
||||
|
||||
<img src="img/1.png" width = "200" height = "260" alt="" align=center />
|
||||
|
||||
## Advert
|
||||
|
||||
<img src="img/2.png" width = "180" height = "240" alt="" align=center />
|
||||
|
||||
<img src="img/3.png" width = "180" height = "240" alt="" align=center />
|
||||
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,961 @@
|
||||
Project Path: arc_apkunpacker_AntiFrida_Bypass_da_m9r5o
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_apkunpacker_AntiFrida_Bypass_da_m9r5o
|
||||
├── AntiAntiFrida.js
|
||||
├── AntiAntiFrida2.js
|
||||
├── AntiAntiFrida3.js
|
||||
├── AntiFrida_Google.js
|
||||
├── AntiFrida_Jiagu.js
|
||||
└── README.md
|
||||
|
||||
```
|
||||
|
||||
`AntiAntiFrida.js`:
|
||||
|
||||
```js
|
||||
/*
|
||||
Made By @ApkUnpacker on 29-6-2022
|
||||
Uploaded on 3-7-2022 ( so i can remember that i faced 4 days internet ban in my area and in free time made this. lol)
|
||||
*/
|
||||
var ProName = ProcessName();
|
||||
function ProcessName() {
|
||||
var openPtr = Module.getExportByName('libc.so', 'open');
|
||||
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
|
||||
var readPtr = Module.getExportByName('libc.so', 'read');
|
||||
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', 'int']);
|
||||
var closePtr = Module.getExportByName('libc.so', 'close');
|
||||
var close = new NativeFunction(closePtr, 'int', ['int']);
|
||||
var path = Memory.allocUtf8String('/proc/self/cmdline');
|
||||
var fd = open(path, 0);
|
||||
if (fd != -1) {
|
||||
var buffer = Memory.alloc(0x1000);
|
||||
var result = read(fd, buffer, 0x1000);
|
||||
close(fd);
|
||||
result = ptr(buffer).readCString();
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
var ourlib = "libxyz.so";
|
||||
var p_pthread_create = Module.findExportByName("libc.so", "pthread_create");
|
||||
var pthread_create = new NativeFunction(p_pthread_create, "int", ["pointer", "pointer", "pointer", "pointer"]);
|
||||
Interceptor.replace(p_pthread_create, new NativeCallback(function(ptr0, ptr1, ptr2, ptr3) {
|
||||
var ret = ptr(0);
|
||||
if (gmn(ptr0) == ourlib) {
|
||||
console.log("Thread Created ptr0 : ", gmn(ptr0), Mod, ptr0.sub(Mod));
|
||||
}
|
||||
if (gmn(ptr1) == ourlib) {
|
||||
var Mod = Module.findBaseAddress(ourlib)
|
||||
console.log("Thread Created ptr1 : ", gmn(ptr1), Mod, ptr1.sub(Mod));
|
||||
Interceptor.attach(Mod.add(ptr1.sub(Mod)), {
|
||||
onEnter: function(args) {
|
||||
console.log("New Thread Func", ptr1.sub(Mod), "arg : ", args[0], args[1]);
|
||||
},
|
||||
onLeave: function(retval) {
|
||||
console.log("New Thread Func Return : ", retval);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (gmn(ptr2) == ourlib) {
|
||||
var Mod = Module.findBaseAddress(ourlib)
|
||||
console.log("Thread Created ptr2 : ", gmn(ptr2), Mod, ptr2.sub(Mod));
|
||||
Interceptor.attach(Mod.add(ptr2.sub(Mod)), {
|
||||
onEnter: function(args) {
|
||||
console.log("New Thread Func", ptr2.sub(Mod), "arg : ", args[0], args[1]);
|
||||
},
|
||||
onLeave: function(retval) {
|
||||
console.log("New Thread Func Return : ", retval);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (gmn(ptr3) == ourlib) {
|
||||
var Mod = Module.findBaseAddress(ourlib)
|
||||
console.log("Thread Created ptr3 : ", gmn(ptr3), Mod, ptr3.sub(Mod));
|
||||
Interceptor.attach(Mod.add(ptr3.sub(Mod)), {
|
||||
onEnter: function(args) {
|
||||
console.log("New Thread Func", ptr3.sub(Mod), "arg : ", args[0], args[1]);
|
||||
},
|
||||
onLeave: function(retval) {
|
||||
console.log("New Thread Func Return : ", retval);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (ptr1.isNull() && ptr3.isNull()) {
|
||||
console.warn("loading fake pthread_create");
|
||||
/* return -1 if you not want to create that thread */
|
||||
return pthread_create(ptr0, ptr1, ptr2, ptr3);
|
||||
// return -1;
|
||||
} else {
|
||||
return pthread_create(ptr0, ptr1, ptr2, ptr3);;
|
||||
}
|
||||
}, "int", ["pointer", "pointer", "pointer", "pointer"]));
|
||||
|
||||
function gmn(fnPtr) {
|
||||
if (fnPtr != null) {
|
||||
try {
|
||||
return Process.getModuleByAddress(fnPtr).name;
|
||||
} catch (e) {console.error(e);}
|
||||
}
|
||||
}
|
||||
/* few method might check frida presence so added them */
|
||||
var inet_atonPtr = Module.findExportByName("libc.so", "inet_aton");
|
||||
var inet_aton = new NativeFunction(inet_atonPtr, 'int', ['pointer', 'pointer']);
|
||||
Interceptor.replace(inet_atonPtr, new NativeCallback(function(addrs, structure) {
|
||||
var retval = inet_aton(addrs, structure);
|
||||
console.log("inet_aton : ", addrs.readCString())
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'pointer']))
|
||||
var popenPtr = Module.findExportByName("libc.so", "popen");
|
||||
var popen = new NativeFunction(popenPtr, 'pointer', ['pointer', 'pointer']);
|
||||
Interceptor.replace(popenPtr, new NativeCallback(function(path, type) {
|
||||
var retval = popen(path, type);
|
||||
console.log("popen : ", path.readCString());
|
||||
return retval;
|
||||
}, 'pointer', ['pointer', 'pointer']))
|
||||
var symlinkPtr = Module.findExportByName("libc.so", "symlink");
|
||||
var symlink = new NativeFunction(symlinkPtr, 'int', ['pointer', 'pointer']);
|
||||
Interceptor.replace(symlinkPtr, new NativeCallback(function(target, path) {
|
||||
var retval = symlink(target, path);
|
||||
console.log("symlink: ", target.readCString(), path.readCString());
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'pointer']))
|
||||
var symlinkatPtr = Module.findExportByName("libc.so", "symlinkat");
|
||||
var symlinkat = new NativeFunction(symlinkatPtr, 'int', ['pointer', 'int', 'pointer']);
|
||||
Interceptor.replace(symlinkatPtr, new NativeCallback(function(target, fd, path) {
|
||||
var retval = symlinkat(target, fd, path);
|
||||
console.log("symlinkat : ", target.readCString(), path.readCString());
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'int', 'pointer']))
|
||||
var inet_addrPtr = Module.findExportByName("libc.so", "inet_addr");
|
||||
var inet_addr = new NativeFunction(inet_addrPtr, 'int', ['int']);
|
||||
Interceptor.replace(inet_addrPtr, new NativeCallback(function(path) {
|
||||
var retval = inet_addr(path);
|
||||
console.log("inet_addr : ", path.readCString())
|
||||
return retval;
|
||||
}, 'int', ['int']))
|
||||
var socketPtr = Module.findExportByName("libc.so", "socket");
|
||||
var socket = new NativeFunction(socketPtr, 'int', ['int', 'int', 'int']);
|
||||
Interceptor.replace(socketPtr, new NativeCallback(function(domain, type, proto) {
|
||||
var retval = socket(domain, type, proto);
|
||||
console.warn("socket : ", domain, type, proto, "Return : ", retval)
|
||||
return retval;
|
||||
}, 'int', ['int', 'int', 'int']))
|
||||
var connectPtr = Module.findExportByName("libc.so", "connect");
|
||||
var connect = new NativeFunction(connectPtr, 'int', ['int', 'pointer', 'int']);
|
||||
Interceptor.replace(connectPtr, new NativeCallback(function(fd, addr, len) {
|
||||
var retval = connect(fd, addr, len);
|
||||
var family = addr.readU16();
|
||||
var port = addr.add(2).readU16();
|
||||
//port = ((port & 0xff) << 8) | (port >> 8);
|
||||
console.warn("Connect : ", family, "Port : ", port, "Return : ", retval);
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'int']))
|
||||
var sendPtr = Module.findExportByName("libc.so", "send");
|
||||
var send2 = new NativeFunction(sendPtr, 'int', ['int', 'pointer', 'int', 'int']);
|
||||
Interceptor.replace(sendPtr, new NativeCallback(function(socksfd, msg, slen, flag, daddr, dlen) {
|
||||
var retval = send2(socksfd, msg, slen, flag);
|
||||
console.log("send : ", socksfd, msg.readCString(), slen, flag);
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'int', 'int']))
|
||||
var sendtoPtr = Module.findExportByName("libc.so", "sendto");
|
||||
var sendto = new NativeFunction(sendtoPtr, 'int', ['int', 'pointer', 'int', 'int', 'pointer', 'int']);
|
||||
Interceptor.replace(sendtoPtr, new NativeCallback(function(socksfd, msg, slen, flag, daddr, dlen) {
|
||||
var retval = sendto(socksfd, msg, slen, flag, daddr, dlen);
|
||||
// console.log("sendto : ",socksfd,msg.readCString(),slen,flag,daddr,dlen);
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'int', 'int', 'pointer', 'int']))
|
||||
|
||||
const openPtr = Module.getExportByName('libc.so', 'open');
|
||||
const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
|
||||
var readPtr = Module.findExportByName("libc.so", "read");
|
||||
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', "int"]);
|
||||
|
||||
//if process name not work correctly you can replace manually with your package name here
|
||||
var FakeMaps = "/data/data/" + ProName + "/maps";
|
||||
var FOpenMaps = "/data/data/" + ProName + "/fmaps";
|
||||
var FakeTask = "/data/data/" + ProName + "/task";
|
||||
var FakeExE = "/data/data/" + ProName + "/exe";
|
||||
var FakeMounts = "/data/data/" + ProName + "/mounts";
|
||||
var FakeStatus = "/data/data/" + ProName + "/status";
|
||||
var MapsFile = new File(FakeMaps, "w");
|
||||
var TaskFile = new File(FakeTask, "w");
|
||||
var ExEFile = new File(FakeExE, "w");
|
||||
var FMapsFile = new File(FOpenMaps, "w");
|
||||
var FMountFile = new File(FakeMounts, "w");
|
||||
var StatusFile = new File(FakeStatus, "w");
|
||||
var MapsBuffer = Memory.alloc(512);
|
||||
var TaskBuffer = Memory.alloc(512);
|
||||
var ExEBuffer = Memory.alloc(512);
|
||||
var FopenBuffer = Memory.alloc(512);
|
||||
var MountBuffer = Memory.alloc(512);
|
||||
var StatusBuffer = Memory.alloc(512);
|
||||
var Open64MapsBuffer = Memory.alloc(512);
|
||||
Interceptor.replace(openPtr, new NativeCallback(function(pathname, flag) {
|
||||
var FD = open(pathname, flag);
|
||||
var ch = pathname.readCString();
|
||||
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("maps") >= 0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, MapsBuffer, 512)) !== 0) {
|
||||
var MBuffer = MapsBuffer.readCString();
|
||||
MBuffer = MBuffer.replaceAll("/data/local/tmp/re.frida.server/frida-agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("re.frida.server", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("re.frida", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("re.", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida.", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("rida-agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-agent-32.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-helper-32", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-helper", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-agent", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("pool-frida", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("/data/local/tmp", "/data");
|
||||
MBuffer = MBuffer.replaceAll("server", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-server", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("linjector", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("gum-js-loop", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida_agent_main", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("gmain", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("magisk", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll(".magisk", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("/sbin/.magisk", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("libriru", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("xposed", "FakingMaps");
|
||||
MapsFile.write(MBuffer);
|
||||
// console.log("MBuffer : ",MBuffer);
|
||||
}
|
||||
var filename = Memory.allocUtf8String(FakeMaps);
|
||||
return open(filename, flag);
|
||||
}
|
||||
if (ch.indexOf("/proc") >= 0 && ch.indexOf("task") >= 0) {
|
||||
// console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, TaskBuffer, 512)) !== 0) {
|
||||
var buffer = TaskBuffer.readCString();
|
||||
buffer = buffer.replaceAll("re.frida.server", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-agent-64.so", "FakingTask");
|
||||
buffer = buffer.replaceAll("rida-agent-64.so", "FakingTask");
|
||||
buffer = buffer.replaceAll("agent-64.so", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-helper-32", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-helper", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-agent", "FakingTask");
|
||||
buffer = buffer.replaceAll("pool-frida", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida", "FakingTask");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("server", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida-server", "FakingTask");
|
||||
buffer = buffer.replaceAll("linjector", "FakingTask");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "FakingTask");
|
||||
buffer = buffer.replaceAll("gmain", "FakingTask");
|
||||
buffer = buffer.replaceAll("magisk", "FakingTask");
|
||||
buffer = buffer.replaceAll(".magisk", "FakingTask");
|
||||
buffer = buffer.replaceAll("/sbin/.magisk", "FakingTask");
|
||||
buffer = buffer.replaceAll("libriru", "FakingTask");
|
||||
buffer = buffer.replaceAll("xposed", "FakingTask");
|
||||
buffer = buffer.replaceAll("pool-spawner", "FakingTask");
|
||||
buffer = buffer.replaceAll("gdbus", "FakingTask");
|
||||
TaskFile.write(buffer);
|
||||
// console.log(buffer);
|
||||
}
|
||||
var filename2 = Memory.allocUtf8String(FakeTask);
|
||||
return open(filename2, flag);
|
||||
}
|
||||
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("mounts") >= 0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, MountBuffer, 512)) !== 0) {
|
||||
var MNTBuffer = MountBuffer.readCString();
|
||||
MNTBuffer = MNTBuffer.replaceAll("magisk", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("/sbin/.magisk", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("libriru", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("mirror", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("system_root", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy")
|
||||
FMountFile.write(MNTBuffer);
|
||||
// console.log("MNTBuffer : ",MNTBuffer);
|
||||
}
|
||||
var mountname = Memory.allocUtf8String(FakeMounts);
|
||||
return open(mountname, flag);
|
||||
}
|
||||
/*
|
||||
if (ch.indexOf("/proc/") >=0 && ch.indexOf("status") >=0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, StatusBuffer, 512)) !== 0) {
|
||||
var PStatus = StatusBuffer.readCString();
|
||||
if (PStatus.indexOf("TracerPid:") > -1) {
|
||||
StatusBuffer.writeUtf8String("TracerPid:\t0");
|
||||
console.log("Bypassing TracerPID Check");
|
||||
}
|
||||
StatusFile.write(PStatus);
|
||||
}
|
||||
var statusname = Memory.allocUtf8String(FakeStatus);
|
||||
return open(statusname, flag);
|
||||
}
|
||||
*/
|
||||
if (ch.indexOf("/proc") >= 0 && ch.indexOf("exe") >= 0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, ExEBuffer, 512)) !== 0) {
|
||||
var buffer = ExEBuffer.readCString();
|
||||
// console.warn(buffer)
|
||||
buffer = buffer.replaceAll("frida-agent-64.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("re.frida.server", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper-32", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("pool-frida", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("frida-server", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("linjector", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gmain", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent", "StaySafeStayHappy");
|
||||
ExEFile.write(buffer);
|
||||
}
|
||||
var filename3 = Memory.allocUtf8String(FakeExE);
|
||||
return open(filename3, flag);
|
||||
}
|
||||
return FD;
|
||||
}, 'int', ['pointer', 'int']))
|
||||
var fgetsPtr = Module.findExportByName("libc.so", "fgets");
|
||||
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
|
||||
Interceptor.replace(fgetsPtr, new NativeCallback(function(buf, size, fp) {
|
||||
//var retval = fgets(buf, size, fp);
|
||||
var buffer = buf.readCString();
|
||||
buffer = buffer.replaceAll("re.frida.server", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("rida-agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-helper-32", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-helper", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent", "FakingGets");
|
||||
buffer = buffer.replaceAll("pool-frida", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida", "FakingGets");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("server", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-server", "FakingGets");
|
||||
buffer = buffer.replaceAll("linjector", "FakingGets");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "FakingGets");
|
||||
buffer = buffer.replaceAll("gmain", "FakingGets");
|
||||
buffer = buffer.replaceAll("magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll(".magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll("/sbin/.magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll("libriru", "FakingGets");
|
||||
buffer = buffer.replaceAll("xposed", "FakingGets");
|
||||
buf.writeUtf8String(buffer);
|
||||
// console.log(buf.readCString());
|
||||
return fgets(buf, size, fp);
|
||||
}, 'pointer', ['pointer', 'int', 'pointer']))
|
||||
|
||||
var readlinkPtr = Module.findExportByName("libc.so", "readlink");
|
||||
var readlink = new NativeFunction(readlinkPtr, 'int', ['pointer', 'pointer', 'int']);
|
||||
Interceptor.replace(readlinkPtr, new NativeCallback(function(pathname, buffer, bufsize) {
|
||||
var retval = readlink(pathname, buffer, bufsize);
|
||||
|
||||
if(buffer.readCString().indexOf("frida")!==-1 ||
|
||||
buffer.readCString().indexOf("gum-js-loop")!==-1||
|
||||
buffer.readCString().indexOf("gmain")!==-1 ||
|
||||
buffer.readCString().indexOf("linjector")!==-1 ||
|
||||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
|
||||
buffer.readCString().indexOf("pool-frida")!==-1 ||
|
||||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
|
||||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
|
||||
){
|
||||
console.log(buffer.readCString(), "Check in readlink");
|
||||
buffer.writeUtf8String("/system/framework/services.jar");
|
||||
return readlink(pathname, buffer, bufsize);
|
||||
}
|
||||
|
||||
// console.log("readlink : ", pathname.readCString(), buffer.readCString());
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'pointer', 'int']))
|
||||
|
||||
|
||||
var readlinkatPtr = Module.findExportByName("libc.so", "readlinkat");
|
||||
var readlinkat = new NativeFunction(readlinkatPtr, 'int', ['int', 'pointer', 'pointer', 'int']);
|
||||
Interceptor.replace(readlinkatPtr, new NativeCallback(function(dirfd, pathname, buffer, bufsize) {
|
||||
var retval = readlinkat(dirfd, pathname, buffer, bufsize);
|
||||
|
||||
if(buffer.readCString().indexOf("frida")!==-1 ||
|
||||
buffer.readCString().indexOf("gum-js-loop")!==-1||
|
||||
buffer.readCString().indexOf("gmain")!==-1 ||
|
||||
buffer.readCString().indexOf("linjector")!==-1 ||
|
||||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
|
||||
buffer.readCString().indexOf("pool-frida")!==-1 ||
|
||||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
|
||||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
|
||||
){
|
||||
console.log(buffer.readCString(), "Check in readlinkat");
|
||||
buffer.writeUtf8String("/system/framework/services.jar");
|
||||
return readlinkat(dirfd, pathname, buffer, bufsize);
|
||||
}
|
||||
|
||||
// console.log("readlinkat : ", pathname.readCString(), buffer.readCString());
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'pointer', 'int']))
|
||||
|
||||
|
||||
Interceptor.attach(Module.findExportByName(null, "strstr"),{
|
||||
onEnter: function(args){
|
||||
this.frida = false;
|
||||
var str1 = args[0].readCString();
|
||||
var str2 = args[1].readCString();
|
||||
if(str1.indexOf("frida")!==-1 || str2.indexOf("frida")!==-1 ||
|
||||
str1.indexOf("gum-js-loop")!==-1 || str2.indexOf("gum-js-loop")!==-1 ||
|
||||
str1.indexOf("gmain")!==-1 || str2.indexOf("gmain")!==-1 ||
|
||||
str1.indexOf("linjector")!==-1 || str2.indexOf("linjector")!==-1 ||
|
||||
str1.indexOf("/data/local/tmp")!==-1 || str2.indexOf("/data/local/tmp")!==-1 ||
|
||||
str1.indexOf("pool-frida")!==-1 || str2.indexOf("pool-frida")!==-1 ||
|
||||
str1.indexOf("frida_agent_main")!==-1 || str2.indexOf("frida_agent_main")!==-1 ||
|
||||
str1.indexOf("re.frida.server")!==-1 || str2.indexOf("re.frida.server")!==-1 ||
|
||||
str1.indexOf("frida-agent")!==-1 || str2.indexOf("frida-agent")!==-1 ||
|
||||
str1.indexOf("pool-spawner")!==-1 || str2.indexOf("pool-spawner")!==-1 ||
|
||||
str1.indexOf("frida-agent-64.so")!==-1 || str2.indexOf("frida-agent-64.so")!==-1 ||
|
||||
str1.indexOf("frida-agent-32.so")!==-1 || str2.indexOf("frida-agent-32.so")!==-1 ||
|
||||
str1.indexOf("frida-helper-32.so")!==-1 || str2.indexOf("frida-helper-32.so")!==-1 ||
|
||||
str1.indexOf("frida-helper-64.so")!==-1 || str2.indexOf("frida-helper-64.so")!==-1 ||
|
||||
str1.indexOf("/sbin/.magisk")!==-1 || str2.indexOf("/sbin/.magisk")!==-1 ||
|
||||
str1.indexOf("libriru")!==-1 || str2.indexOf("libriru")!==-1 ||
|
||||
str1.indexOf("magisk")!==-1 || str2.indexOf("magisk")!==-1
|
||||
|
||||
){
|
||||
this.frida = true;
|
||||
console.log("strstr : ",str1,str2);
|
||||
}
|
||||
},
|
||||
onLeave: function(retval){
|
||||
if (this.frida) {
|
||||
retval.replace(ptr("0x0"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
//Enabling it might give crash on some apps
|
||||
/*
|
||||
Interceptor.attach(Module.findExportByName("libc.so", "read"), {
|
||||
onEnter: function(args) {
|
||||
try {
|
||||
var buffer = args[1].readCString();
|
||||
if (buffer.indexOf("frida") >= 0) {
|
||||
buffer = buffer.replaceAll("re.frida.server", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent-64.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("rida-agent-64.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("agent-64.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper-32", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("pool-frida", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("server", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-server", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("linjector", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gmain", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("magisk", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll(".magisk", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("/sbin/.magisk", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("libriru", "StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("xposed", "StaySafeStayHappy");
|
||||
args[1].writeUtf8String(buffer);
|
||||
}
|
||||
} catch (e) {
|
||||
//console.error(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
/*
|
||||
var memcpyPtr = Module.findExportByName("libc.so", "memcpy");
|
||||
var memcpy = new NativeFunction(memcpyPtr, 'pointer', ['pointer', 'pointer', 'int']);
|
||||
Interceptor.replace(memcpyPtr, new NativeCallback(function(dest, src, len) {
|
||||
var retval = memcpy(dest, src, len);
|
||||
if(dest.readCString() != null && src.readCString() != null && (dest.readCString().indexOf("frida")>=0 || src.readCString().indexOf("frida")>=0) )
|
||||
{
|
||||
//console.warn("memcpy : ",dest.readCString(),src.readCString());
|
||||
var buffer = dest.readCString();
|
||||
var buffer2 = src.readCString();
|
||||
buffer = buffer.replaceAll("re.frida.server","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent-64.so","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("rida-agent-64.so","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("agent-64.so","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper-32","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-helper","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-agent","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("pool-frida","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("/data/local/tmp","/data");
|
||||
buffer = buffer.replaceAll("server","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida-server","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("linjector","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gum-js-loop","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("frida_agent_main","StaySafeStayHappy");
|
||||
buffer = buffer.replaceAll("gmain","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("re.frida.server","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-agent-64.so","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("rida-agent-64.so","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("agent-64.so","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-agent-32.so","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-helper-32","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-helper","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-agent","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("pool-frida","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("/data/local/tmp","/data");
|
||||
buffer2 = buffer2.replaceAll("server","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida-server","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("linjector","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("gum-js-loop","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("frida_agent_main","StaySafeStayHappy");
|
||||
buffer2 = buffer2.replaceAll("gmain","StaySafeStayHappy");
|
||||
dest.writeUtf8String(buffer);
|
||||
src.writeUtf8String(buffer2);
|
||||
// console.log(buffer,buffer2);
|
||||
return memcpy(dest, src, len);
|
||||
}
|
||||
return retval;
|
||||
}, 'pointer', ['pointer', 'pointer', 'int']))
|
||||
*/
|
||||
|
||||
```
|
||||
|
||||
`AntiAntiFrida2.js`:
|
||||
|
||||
```js
|
||||
/*
|
||||
Made By @ApkUnpacker on 29-6-2022
|
||||
Uploaded on 3-7-2022 ( so i can remember that i faced 4 days internet ban in my area and in free time made this. lol)
|
||||
Less of chance of crash compare to 1st script
|
||||
*/
|
||||
var ProName = ProcessName();
|
||||
|
||||
function ProcessName() {
|
||||
var openPtr = Module.getExportByName('libc.so', 'open');
|
||||
var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
|
||||
var readPtr = Module.getExportByName('libc.so', 'read');
|
||||
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', 'int']);
|
||||
var closePtr = Module.getExportByName('libc.so', 'close');
|
||||
var close = new NativeFunction(closePtr, 'int', ['int']);
|
||||
var path = Memory.allocUtf8String('/proc/self/cmdline');
|
||||
var fd = open(path, 0);
|
||||
if (fd != -1) {
|
||||
var buffer = Memory.alloc(0x1000);
|
||||
var result = read(fd, buffer, 0x1000);
|
||||
close(fd);
|
||||
result = ptr(buffer).readCString();
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function gmn(fnPtr) {
|
||||
try {
|
||||
return Process.getModuleByAddress(fnPtr).name;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
var inet_atonPtr = Module.findExportByName("libc.so", "inet_aton");
|
||||
var inet_aton = new NativeFunction(inet_atonPtr, 'int', ['pointer', 'pointer']);
|
||||
Interceptor.replace(inet_atonPtr, new NativeCallback(function(addrs, structure) {
|
||||
var retval = inet_aton(addrs, structure);
|
||||
console.log("inet_aton : ", addrs.readCString())
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'pointer']))
|
||||
var popenPtr = Module.findExportByName("libc.so", "popen");
|
||||
var popen = new NativeFunction(popenPtr, 'pointer', ['pointer', 'pointer']);
|
||||
Interceptor.replace(popenPtr, new NativeCallback(function(path, type) {
|
||||
var retval = popen(path, type);
|
||||
console.log("popen : ", path.readCString());
|
||||
return retval;
|
||||
}, 'pointer', ['pointer', 'pointer']))
|
||||
var inet_addrPtr = Module.findExportByName("libc.so", "inet_addr");
|
||||
var inet_addr = new NativeFunction(inet_addrPtr, 'int', ['int']);
|
||||
Interceptor.replace(inet_addrPtr, new NativeCallback(function(path) {
|
||||
var retval = inet_addr(path);
|
||||
console.log("inet_addr : ", path.readCString())
|
||||
return retval;
|
||||
}, 'int', ['int']))
|
||||
var socketPtr = Module.findExportByName("libc.so", "socket");
|
||||
var socket = new NativeFunction(socketPtr, 'int', ['int', 'int', 'int']);
|
||||
Interceptor.replace(socketPtr, new NativeCallback(function(domain, type, proto) {
|
||||
var retval = socket(domain, type, proto);
|
||||
console.warn("socket : ", domain, type, proto, "Return : ", retval)
|
||||
return retval;
|
||||
}, 'int', ['int', 'int', 'int']))
|
||||
var connectPtr = Module.findExportByName("libc.so", "connect");
|
||||
var connect = new NativeFunction(connectPtr, 'int', ['int', 'pointer', 'int']);
|
||||
Interceptor.replace(connectPtr, new NativeCallback(function(fd, addr, len) {
|
||||
var retval = connect(fd, addr, len);
|
||||
var family = addr.readU16();
|
||||
var port = addr.add(2).readU16();
|
||||
//port = ((port & 0xff) << 8) | (port >> 8);
|
||||
console.warn("Connect : ", family, "Port : ", port, "Return : ", retval);
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'int']))
|
||||
//Make it true if you want to change maps, high chance of crash
|
||||
var HookMaps = false;
|
||||
const openPtr = Module.getExportByName('libc.so', 'open');
|
||||
const open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
|
||||
var readPtr = Module.findExportByName("libc.so", "read");
|
||||
var read = new NativeFunction(readPtr, 'int', ['int', 'pointer', "int"]);
|
||||
//if process name not work correctly you can replace manually with your package name here
|
||||
var FakeMaps = "/data/data/" + ProName + "/maps";
|
||||
var FakeTask = "/data/data/" + ProName + "/task";
|
||||
var FakeMounts = "/data/data/" + ProName + "/mounts";
|
||||
var FakeStatus = "/data/data/" + ProName + "/status";
|
||||
var MapsFile = new File(FakeMaps, "w");
|
||||
var TaskFile = new File(FakeTask, "w");
|
||||
var FMountFile = new File(FakeMounts, "w");
|
||||
var StatusFile = new File(FakeStatus, "w");
|
||||
var MapsBuffer = Memory.alloc(512);
|
||||
var TaskBuffer = Memory.alloc(512);
|
||||
var MountBuffer = Memory.alloc(512);
|
||||
var StatusBuffer = Memory.alloc(512);
|
||||
Interceptor.replace(openPtr, new NativeCallback(function(pathname, flag) {
|
||||
var FD = open(pathname, flag);
|
||||
var ch = pathname.readCString();
|
||||
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("maps") >= 0) {
|
||||
console.warn("Possible Frida Detection",ch)
|
||||
}
|
||||
if (HookMaps) {
|
||||
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("maps") >= 0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, MapsBuffer, 512)) !== 0) {
|
||||
var MBuffer = MapsBuffer.readCString();
|
||||
MBuffer = MBuffer.replaceAll("/data/local/tmp/re.frida.server/frida-agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("re.frida.server", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-agent-64.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida-agent-32.so", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("frida", "FakingMaps");
|
||||
MBuffer = MBuffer.replaceAll("/data/local/tmp", "/data");
|
||||
MapsFile.write(MBuffer);
|
||||
}
|
||||
var filename = Memory.allocUtf8String(FakeMaps);
|
||||
return open(filename, flag);
|
||||
}
|
||||
}
|
||||
if (ch.indexOf("/proc") >= 0 && ch.indexOf("task") >= 0) {
|
||||
while (parseInt(read(FD, TaskBuffer, 512)) !== 0) {
|
||||
var buffer = TaskBuffer.readCString();
|
||||
buffer = buffer.replaceAll("pool-frida", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida", "FakingTask");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("frida-server", "FakingTask");
|
||||
buffer = buffer.replaceAll("linjector", "FakingTask");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "FakingTask");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "FakingTask");
|
||||
buffer = buffer.replaceAll("gmain", "FakingTask");
|
||||
buffer = buffer.replaceAll("pool-spawner", "FakingTask");
|
||||
buffer = buffer.replaceAll("gdbus", "FakingTask");
|
||||
TaskFile.write(buffer);
|
||||
}
|
||||
var filename2 = Memory.allocUtf8String(FakeTask);
|
||||
return open(filename2, flag);
|
||||
}
|
||||
if (ch.indexOf("/proc/") >= 0 && ch.indexOf("mounts") >= 0) {
|
||||
console.log("open : ", pathname.readCString())
|
||||
while (parseInt(read(FD, MountBuffer, 512)) !== 0) {
|
||||
var MNTBuffer = MountBuffer.readCString();
|
||||
MNTBuffer = MNTBuffer.replaceAll("magisk", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("/sbin/.magisk", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("libriru", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("mirror", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("system_root", "StaySafeStayHappy");
|
||||
MNTBuffer = MNTBuffer.replaceAll("xposed", "StaySafeStayHappy")
|
||||
FMountFile.write(MNTBuffer);
|
||||
}
|
||||
var mountname = Memory.allocUtf8String(FakeMounts);
|
||||
return open(mountname, flag);
|
||||
}
|
||||
return FD;
|
||||
}, 'int', ['pointer', 'int']))
|
||||
var fgetsPtr = Module.findExportByName("libc.so", "fgets");
|
||||
var fgets = new NativeFunction(fgetsPtr, 'pointer', ['pointer', 'int', 'pointer']);
|
||||
Interceptor.replace(fgetsPtr, new NativeCallback(function(buf, size, fp) {
|
||||
var buffer = buf.readCString();
|
||||
buffer = buffer.replaceAll("re.frida.server", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("rida-agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("agent-64.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent-32.so", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-helper-32", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-helper", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-agent", "FakingGets");
|
||||
buffer = buffer.replaceAll("pool-frida", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida", "FakingGets");
|
||||
buffer = buffer.replaceAll("/data/local/tmp", "/data");
|
||||
buffer = buffer.replaceAll("server", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida-server", "FakingGets");
|
||||
buffer = buffer.replaceAll("linjector", "FakingGets");
|
||||
buffer = buffer.replaceAll("gum-js-loop", "FakingGets");
|
||||
buffer = buffer.replaceAll("frida_agent_main", "FakingGets");
|
||||
buffer = buffer.replaceAll("gmain", "FakingGets");
|
||||
buffer = buffer.replaceAll("magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll(".magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll("/sbin/.magisk", "FakingGets");
|
||||
buffer = buffer.replaceAll("libriru", "FakingGets");
|
||||
buffer = buffer.replaceAll("xposed", "FakingGets");
|
||||
buf.writeUtf8String(buffer);
|
||||
return fgets(buf, size, fp);
|
||||
}, 'pointer', ['pointer', 'int', 'pointer']))
|
||||
var readlinkPtr = Module.findExportByName("libc.so", "readlink");
|
||||
var readlink = new NativeFunction(readlinkPtr, 'int', ['pointer', 'pointer', 'int']);
|
||||
Interceptor.replace(readlinkPtr, new NativeCallback(function(pathname, buffer, bufsize) {
|
||||
var retval = readlink(pathname, buffer, bufsize);
|
||||
if(buffer.readCString().indexOf("frida")!==-1 ||
|
||||
buffer.readCString().indexOf("gum-js-loop")!==-1||
|
||||
buffer.readCString().indexOf("gmain")!==-1 ||
|
||||
buffer.readCString().indexOf("linjector")!==-1 ||
|
||||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
|
||||
buffer.readCString().indexOf("pool-frida")!==-1 ||
|
||||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
|
||||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
|
||||
){
|
||||
console.log(buffer.readCString(), "Check in readlink");
|
||||
buffer.writeUtf8String("/system/framework/services.jar");
|
||||
return readlink(pathname, buffer, bufsize);
|
||||
}
|
||||
return retval;
|
||||
}, 'int', ['pointer', 'pointer', 'int']))
|
||||
var readlinkatPtr = Module.findExportByName("libc.so", "readlinkat");
|
||||
var readlinkat = new NativeFunction(readlinkatPtr, 'int', ['int', 'pointer', 'pointer', 'int']);
|
||||
Interceptor.replace(readlinkatPtr, new NativeCallback(function(dirfd, pathname, buffer, bufsize) {
|
||||
var retval = readlinkat(dirfd, pathname, buffer, bufsize);
|
||||
if(buffer.readCString().indexOf("frida")!==-1 ||
|
||||
buffer.readCString().indexOf("gum-js-loop")!==-1||
|
||||
buffer.readCString().indexOf("gmain")!==-1 ||
|
||||
buffer.readCString().indexOf("linjector")!==-1 ||
|
||||
buffer.readCString().indexOf("/data/local/tmp")!==-1 ||
|
||||
buffer.readCString().indexOf("pool-frida")!==-1 ||
|
||||
buffer.readCString().indexOf("frida_agent_main")!==-1 ||
|
||||
buffer.readCString().indexOf("re.frida.server")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-64.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-agent-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-32.so")!==-1 ||
|
||||
buffer.readCString().indexOf("frida-helper-64.so")!==-1
|
||||
){
|
||||
console.log(buffer.readCString(), "Check in readlinkat");
|
||||
buffer.writeUtf8String("/system/framework/services.jar");
|
||||
return readlinkat(dirfd, pathname, buffer, bufsize);
|
||||
}
|
||||
return retval;
|
||||
}, 'int', ['int', 'pointer', 'pointer', 'int']))
|
||||
Interceptor.attach(Module.findExportByName(null, "strstr"),{
|
||||
onEnter: function(args){
|
||||
this.frida = false;
|
||||
var str1 = args[0].readCString();
|
||||
var str2 = args[1].readCString();
|
||||
if(str1.indexOf("frida")!==-1 || str2.indexOf("frida")!==-1 ||
|
||||
str1.indexOf("gum-js-loop")!==-1 || str2.indexOf("gum-js-loop")!==-1 ||
|
||||
str1.indexOf("gmain")!==-1 || str2.indexOf("gmain")!==-1 ||
|
||||
str1.indexOf("linjector")!==-1 || str2.indexOf("linjector")!==-1 ||
|
||||
str1.indexOf("/data/local/tmp")!==-1 || str2.indexOf("/data/local/tmp")!==-1 ||
|
||||
str1.indexOf("pool-frida")!==-1 || str2.indexOf("pool-frida")!==-1 ||
|
||||
str1.indexOf("frida_agent_main")!==-1 || str2.indexOf("frida_agent_main")!==-1 ||
|
||||
str1.indexOf("re.frida.server")!==-1 || str2.indexOf("re.frida.server")!==-1 ||
|
||||
str1.indexOf("frida-agent")!==-1 || str2.indexOf("frida-agent")!==-1 ||
|
||||
str1.indexOf("pool-spawner")!==-1 || str2.indexOf("pool-spawner")!==-1 ||
|
||||
str1.indexOf("frida-agent-64.so")!==-1 || str2.indexOf("frida-agent-64.so")!==-1 ||
|
||||
str1.indexOf("frida-agent-32.so")!==-1 || str2.indexOf("frida-agent-32.so")!==-1 ||
|
||||
str1.indexOf("frida-helper-32.so")!==-1 || str2.indexOf("frida-helper-32.so")!==-1 ||
|
||||
str1.indexOf("frida-helper-64.so")!==-1 || str2.indexOf("frida-helper-64.so")!==-1 ||
|
||||
str1.indexOf("/sbin/.magisk")!==-1 || str2.indexOf("/sbin/.magisk")!==-1 ||
|
||||
str1.indexOf("libriru")!==-1 || str2.indexOf("libriru")!==-1 ||
|
||||
str1.indexOf("magisk")!==-1 || str2.indexOf("magisk")!==-1
|
||||
){
|
||||
this.frida = true;
|
||||
console.log("strstr : ",str1,str2);
|
||||
}
|
||||
},
|
||||
onLeave: function(retval){
|
||||
if (this.frida) {
|
||||
retval.replace(ptr("0x0"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
```
|
||||
|
||||
`AntiAntiFrida3.js`:
|
||||
|
||||
```js
|
||||
/*
|
||||
Some app detect root/frida/lsposed/ etc but in callback shows "something is detected" by a dialog box instead of direct crashing
|
||||
In this code we try to make those dialogbox cancelable so just touch outside those dialog box to remove them
|
||||
and continue usual pentesting.
|
||||
But remember if app have analytics/threatwatch telemetry, they still can see on server that application
|
||||
is being running in insecure environment.
|
||||
*/
|
||||
Java.performNow(function() {
|
||||
try {
|
||||
let AlertDialog = Java.use("android.app.AlertDialog");
|
||||
AlertDialog.show.implementation = function() {
|
||||
console.warn("Hooked AlertDialog.show()");
|
||||
//stacktrace()
|
||||
this.show();
|
||||
this.setCancelable(true);
|
||||
this.setCanceledOnTouchOutside(true);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error :", error);
|
||||
}
|
||||
})
|
||||
|
||||
function stacktrace() {
|
||||
Java.perform(function() {
|
||||
let AndroidLog = Java.use("android.util.Log");
|
||||
let ExceptionClass = Java.use("java.lang.Exception");
|
||||
console.warn(AndroidLog.getStackTraceString(ExceptionClass.$new()));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
var p_pthread_create = Module.findExportByName("libc.so", "pthread_create");
|
||||
var pthread_create = new NativeFunction(p_pthread_create, "int", ["pointer", "pointer", "pointer", "pointer"]);
|
||||
Interceptor.replace(p_pthread_create, new NativeCallback(function(ptr0, ptr1, ptr2, ptr3) {
|
||||
if (ptr1.isNull() && ptr3.isNull()) {
|
||||
console.log("Possible thread creation for checking. Disabling it");
|
||||
return -1;
|
||||
} else {
|
||||
return pthread_create(ptr0, ptr1, ptr2, ptr3);
|
||||
}
|
||||
}, "int", ["pointer", "pointer", "pointer", "pointer"]));
|
||||
} catch (error) {
|
||||
console.log("Error", error)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
`AntiFrida_Google.js`:
|
||||
|
||||
```js
|
||||
/*
|
||||
Created By @apkunpacker
|
||||
It only bypass anti-frida detection which crash app even after frida-server is closed.
|
||||
It won't work if you use it with any java hook ( Java.use("xyz") )
|
||||
Java.use have seperate detections.
|
||||
I hate those guys who sell open source scripts.
|
||||
*/
|
||||
Interceptor.attach(Module.findExportByName(null, "strlen"), {
|
||||
onEnter: function(args) {
|
||||
var cmd = args[0].readCString();
|
||||
if (cmd.indexOf("rwxp") !== -1) {
|
||||
cmd = cmd.replaceAll("rwxp", "r-xp");
|
||||
args[0].writeUtf8String(cmd);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
`AntiFrida_Jiagu.js`:
|
||||
|
||||
```js
|
||||
let Library = "libjiagu_64.so";
|
||||
let Arm64Pattern = "00 03 3f d6 a0 06 00 a9";
|
||||
let PackageName = ProcessName();
|
||||
|
||||
function ProcessName() {
|
||||
let openPtr = Module.getExportByName('libc.so', 'open');
|
||||
let open = new NativeFunction(openPtr, 'int', ['pointer', 'int']);
|
||||
let readPtr = Module.getExportByName('libc.so', 'read');
|
||||
let read = new NativeFunction(readPtr, 'int', ['int', 'pointer', 'int']);
|
||||
let closePtr = Module.getExportByName('libc.so', 'close');
|
||||
let close = new NativeFunction(closePtr, 'int', ['int']);
|
||||
let path = Memory.allocUtf8String('/proc/self/cmdline');
|
||||
let fd = open(path, 0);
|
||||
if (fd != -1) {
|
||||
let buffer = Memory.alloc(0x1000);
|
||||
let result = read(fd, buffer, 0x1000);
|
||||
close(fd);
|
||||
result = ptr(buffer).readCString();
|
||||
return result;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
Interceptor.attach(Module.getExportByName(null, "android_dlopen_ext"), {
|
||||
onEnter: function(args) {
|
||||
let AllLib = args[0].readCString();
|
||||
if (AllLib.indexOf(Library) != -1) {
|
||||
this.HookJiagu = true;
|
||||
}
|
||||
},
|
||||
onLeave: function(args) {
|
||||
if (this.HookJiagu) {
|
||||
let Jiagu = Process.findModuleByName(Library);
|
||||
Memory.scan(Jiagu.base, Jiagu.size, Arm64Pattern, {
|
||||
onMatch: function(found, sizes) {
|
||||
Interceptor.attach(found, function(args) {
|
||||
Memory.protect(this.context.x0, Process.pointerSize, 'rwx');
|
||||
try {
|
||||
let arg0 = this.context.x0.readCString()
|
||||
if (arg0 && (arg0.indexOf("/proc/") != -1 && arg0.indexOf("/maps") != -1)) {
|
||||
this.context.x0.writeUtf8String("/proc/self/cmdline")
|
||||
}
|
||||
} catch (e) {}
|
||||
})
|
||||
},
|
||||
onComplete: function(msg) {
|
||||
console.log("Frida Detection Bypassed");
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
# AntiFrida_Bypass
|
||||
Generic Script To Bypass Some AntiFrida Checks
|
||||
|
||||
Please Star or Fork it if you use it -> it give confidence that community likes it
|
||||
|
||||
Feel Free to send more Bypass as a PR or Improve it.
|
||||
|
||||
Note - It won't support for Bypassing Frida Checks with Direct Syscalls yet
|
||||
|
||||
Roadmap -
|
||||
1. Add Syscall Check Bypass
|
||||
2. Add More Frida Strings
|
||||
3. Add More Method which Might check Frida Presence
|
||||
4. something you guys can suggest me
|
||||
|
||||
```
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,699 @@
|
||||
Project Path: arc_gmh5225_frida-ue4dump_anbgycpo
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_gmh5225_frida-ue4dump_anbgycpo
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
└── script.js
|
||||
|
||||
```
|
||||
|
||||
`LICENSE`:
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 hackcatml
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
# frida-ue4dump
|
||||
UE4 dump frida script for UE >= 4.23 64bit<br>
|
||||
Heavily based on [UE4Dumper](https://github.com/kp7742/UE4Dumper)<br>
|
||||
Only tested on UE4.27.2 sample game(Android, iOS). It might not work correctly on other versions unless you fix the offsets in script.js
|
||||
|
||||
# Usage
|
||||
1. Attach
|
||||
```
|
||||
frida -Ul script.js <UE4 Game>
|
||||
```
|
||||
|
||||
2. Set
|
||||
```
|
||||
Call set(<moduleName>) on terminal(ex. set("libUE4.so"))
|
||||
|
||||
It will set moduleBase, GUObjectArray, GName
|
||||
If it cannot find GUObjectArray, GName, need to provide those values manually
|
||||
```
|
||||
|
||||
3. Dump
|
||||
```
|
||||
dumpSdk()
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
# Credit
|
||||
* [UE4Dumper](https://github.com/kp7742/UE4Dumper)
|
||||
|
||||
```
|
||||
|
||||
`script.js`:
|
||||
|
||||
```js
|
||||
var moduleBase;
|
||||
var GUObjectArray;
|
||||
var GName;
|
||||
|
||||
var nameIds = [];
|
||||
var platform = Process.platform
|
||||
|
||||
// Global
|
||||
var FUObjectItemPadd = 0x0;
|
||||
var FUObjectItemSize = 0x18;
|
||||
// SDK
|
||||
var offset_FUObjectArray_TUObjectArray = 0x10;
|
||||
var offset_TUObjectArray_NumElements = 0x14;
|
||||
// FNamePool
|
||||
var FNameStride = 0x2
|
||||
var offset_GName_FNamePool = platform == "linux" ? 0x30 : 0xc0;
|
||||
var offset_FNamePool_CurrentBlock = 0x8;
|
||||
var offset_FNamePool_CurrentByteCursor = 0xc;
|
||||
var offset_FNamePool_Blocks = 0x10;
|
||||
// FNameEntry
|
||||
var FNameEntry_LenBit = 6;
|
||||
var offset_FNameEntryT_String = 0x2;
|
||||
//Class: UObject
|
||||
var offset_UObject_InternalIndex = 0xC;
|
||||
var offset_UObject_ClassPrivate = 0x10;
|
||||
var offset_UObject_FNameIndex = 0x18;
|
||||
var offset_UObject_OuterPrivate = 0x20;
|
||||
//Class: UField
|
||||
var offset_offset_UField_Next = 0x28;
|
||||
//Class: UStruct
|
||||
var offset_UStruct_SuperStruct = 0x40;
|
||||
var offset_UStruct_Children = 0x48;
|
||||
var offset_UStruct_ChildProperties = 0x50;
|
||||
//Class: FField
|
||||
var offset_FField_Class = 0x8;
|
||||
var offset_FField_Next = 0x20;
|
||||
var offset_FField_Name = 0x28;
|
||||
//Class: UFunction
|
||||
var offset_UFunction_FunctionFlags = 0xb0;
|
||||
var offset_UFunction_Func = 0xd8;
|
||||
//Class: UProperty
|
||||
var offset_UProperty_ElementSize = 0x38;
|
||||
var offset_UProperty_PropertyFlags = 0x40;
|
||||
var offset_UProperty_OffsetInternal = 0x4c;
|
||||
//Class: UBoolProperty
|
||||
var offset_UBoolProperty_FieldSize = 0x88;
|
||||
var offset_UBoolProperty_ByteOffset = 0x89;
|
||||
var offset_UBoolProperty_ByteMask = 0x8a;
|
||||
var offset_UBoolProperty_FieldMask = 0x8b;
|
||||
//Class: UObjectProperty
|
||||
var offset_UObjectProperty_PropertyClass = 0x78;
|
||||
//Class: UClassProperty
|
||||
var offset_UClassProperty_MetaClass = 0x78;
|
||||
//Class: UInterfaceProperty
|
||||
var offset_UInterfaceProperty_InterfaceClass = 0x78;
|
||||
//Class: UArrayProperty
|
||||
var offset_UArrayProperty_InnerProperty = 0x78;
|
||||
//Class: UMapProperty
|
||||
var offset_UMapProperty_KeyProp = 0x78;
|
||||
var offset_UMapProperty_ValueProp = 0x80;
|
||||
//Class: USetProperty
|
||||
var offset_USetProperty_ElementProp = 0x78;
|
||||
//Class: UStructProperty
|
||||
var offset_UStructProperty_Struct = 0x78;
|
||||
//Class: UWorld
|
||||
var offset_UWorld_PersistentLevel = 0x30;
|
||||
//Class: ULevel
|
||||
var offset_ULevel_AActors = 0x98;
|
||||
var offset_ULevel_AActorsCount = 0xA0;
|
||||
|
||||
var UObject = {
|
||||
getClass: function(obj) {
|
||||
var classPrivate = ptr(obj).add(offset_UObject_ClassPrivate).readPointer();
|
||||
// console.log(`classPrivate: ${classPrivate}`);
|
||||
return classPrivate;
|
||||
},
|
||||
getNameId: function(obj) {
|
||||
var nameId = ptr(obj).add(offset_UObject_FNameIndex).readU32();
|
||||
// console.log(`nameId: ${nameId}`);
|
||||
return nameId;
|
||||
},
|
||||
getName: function(obj) {
|
||||
if (this.isValid(obj)){
|
||||
return getFNameFromID(this.getNameId(obj));
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
},
|
||||
getClassName: function(obj) {
|
||||
if (this.isValid(obj)) {
|
||||
var classPrivate = this.getClass(obj);
|
||||
return this.getName(classPrivate);
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
},
|
||||
isValid: function(obj) {
|
||||
var isValid = (ptr(obj) > 0 && this.getNameId(obj) > 0 && this.getClass(obj) > 0);
|
||||
// console.log(`isValid: ${isValid}`);
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
||||
var UField = {
|
||||
getNext: function(field) {//UField*
|
||||
return field.add(offset_offset_UField_Next).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var FField = {
|
||||
getName: function(fField) {
|
||||
return getFNameFromID(fField.add(offset_FField_Name).readU32());
|
||||
},
|
||||
getClassName: function(fField) {
|
||||
return getFNameFromID(fField.add(offset_FField_Class).readPointer().readU32());
|
||||
},
|
||||
getNext: function(fField) {//UField*
|
||||
return fField.add(offset_FField_Next).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UStruct = {
|
||||
getSuperClass: function(structz) {//UStruct*
|
||||
// console.log(`structz: ${structz}`);
|
||||
return structz.add(offset_UStruct_SuperStruct).readPointer()
|
||||
},
|
||||
getChildren: function(structz) {//UField*
|
||||
return structz.add(offset_UStruct_Children).readPointer();
|
||||
},
|
||||
getChildProperties: function(structz) {//UField*
|
||||
return structz.add(offset_UStruct_ChildProperties).readPointer();
|
||||
},
|
||||
getClassName: function(clazz) {
|
||||
return UObject.getName(clazz);
|
||||
},
|
||||
getClassPath: function(object) {
|
||||
var clazz = UObject.getClass(object);
|
||||
var classname = UObject.getName(clazz);
|
||||
|
||||
var superclass = this.getSuperClass(clazz);
|
||||
while (UObject.isValid(superclass)) {
|
||||
classname += ".";
|
||||
classname += UObject.getName(superclass);
|
||||
|
||||
superclass = this.getSuperClass(superclass);
|
||||
}
|
||||
|
||||
return classname;
|
||||
},
|
||||
getStructClassPath: function(clazz) {
|
||||
var classname = UObject.getName(clazz);
|
||||
|
||||
var superclass = this.getSuperClass(clazz);
|
||||
while (UObject.isValid(superclass)) {
|
||||
// console.log(`superclass: ${superclass}`)
|
||||
classname += ".";
|
||||
classname += UObject.getName(superclass);
|
||||
|
||||
superclass = this.getSuperClass(superclass);
|
||||
}
|
||||
|
||||
return classname;
|
||||
}
|
||||
}
|
||||
|
||||
var UFunction = {
|
||||
getFunctionFlags: function(func) {
|
||||
return func.add(offset_UFunction_FunctionFlags).readU32();
|
||||
},
|
||||
getFunc: function(func) {
|
||||
return func.add(offset_UFunction_Func).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UProperty = {
|
||||
getElementSize: function(prop) {
|
||||
return prop.add(offset_UProperty_ElementSize).readU32();
|
||||
},
|
||||
getPropertyFlags: function(prop) {
|
||||
return prop.add(offset_UProperty_PropertyFlags).readU64()
|
||||
},
|
||||
getOffset: function(prop) {
|
||||
return prop.add(offset_UProperty_OffsetInternal).readU32();
|
||||
}
|
||||
};
|
||||
|
||||
var UBoolProperty = {
|
||||
getFieldSize: function(prop) {
|
||||
return prop.add(offset_UBoolProperty_FieldSize).readU8();
|
||||
},
|
||||
getByteOffset: function(prop) {
|
||||
return prop.add(offset_UBoolProperty_ByteOffset).readU8();
|
||||
},
|
||||
getByteMask: function(prop) {
|
||||
return prop.add(offset_UBoolProperty_ByteMask).readU8();
|
||||
},
|
||||
getFieldMask: function(prop) {
|
||||
return prop.add(offset_UBoolProperty_FieldMask).readU8();
|
||||
},
|
||||
};
|
||||
|
||||
var UObjectProperty = {
|
||||
getPropertyClass: function(prop) {//class UClass*
|
||||
return prop.add(offset_UObjectProperty_PropertyClass).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UClassProperty = {
|
||||
getMetaClass: function(prop) {//class UClass*
|
||||
return prop.add(offset_UClassProperty_MetaClass).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UInterfaceProperty = {
|
||||
getInterfaceClass: function(prop) {//class UClass*
|
||||
return prop.add(offset_UInterfaceProperty_InterfaceClass).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UArrayProperty = {
|
||||
getInner: function(prop) {//UProperty*
|
||||
return prop.add(offset_UArrayProperty_InnerProperty).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UMapProperty = {
|
||||
getKeyProp: function(prop) {//UProperty*
|
||||
return prop.add(offset_UMapProperty_KeyProp).readPointer();
|
||||
},
|
||||
getValueProp: function(prop) {//UProperty*
|
||||
return prop.add(offset_UMapProperty_ValueProp).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var USetProperty = {
|
||||
getElementProp: function(prop) {//UProperty*
|
||||
return prop.add(offset_USetProperty_ElementProp).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
var UStructProperty = {
|
||||
getStruct: function(prop) {//UStruct*
|
||||
return prop.add(offset_UStructProperty_Struct).readPointer();
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept operator==(FNameEntryId, EName)
|
||||
function findGName(moduleName) {
|
||||
var base = Module.findExportByName(moduleName, "_Zeq12FNameEntryId5EName");
|
||||
if (base == null) {
|
||||
console.log(`[!] Cannot find GName. You need to find it by yourself`);
|
||||
GName = null;
|
||||
return;
|
||||
}
|
||||
Interceptor.attach(base.add(0x8), {
|
||||
onEnter: function(args) {
|
||||
// console.log(this.context.x8);
|
||||
// console.log(JSON.stringify(this.context.x8).length);
|
||||
if (this.context.x8 != ptr(0x0) && JSON.stringify(this.context.x8).length > 10) {
|
||||
GName = ptr(this.context.x8);
|
||||
Interceptor.detachAll();
|
||||
}
|
||||
},
|
||||
onLeave: function(retval) {
|
||||
// Interceptor.detachAll();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getFNameFromID(index) {
|
||||
var Block = index >> 16;
|
||||
var Offset = index & 65535;
|
||||
|
||||
var FNamePool = GName.add(offset_GName_FNamePool);
|
||||
// console.log(`FNamePool: ${FNamePool}`);
|
||||
// console.log(`Block: ${Block}`);
|
||||
var NamePoolChunk = FNamePool.add(offset_FNamePool_Blocks + Block * Process.pointerSize).readPointer();
|
||||
// console.log(`NamePoolChunk: ${NamePoolChunk}`);
|
||||
var FNameEntry = NamePoolChunk.add(FNameStride * Offset);
|
||||
// console.log(`FNameEntry: ${FNameEntry}`);
|
||||
var FNameEntryHeader = FNameEntry.readU16();
|
||||
// console.log(`FNameEntryHeader: ${FNameEntryHeader}`);
|
||||
var str_addr = FNameEntry.add(offset_FNameEntryT_String);
|
||||
var str_length = FNameEntryHeader >> FNameEntry_LenBit;
|
||||
var wide = FNameEntryHeader & 1;
|
||||
|
||||
if (str_length > 0 && str_length < 250) {
|
||||
var str = str_addr.readUtf8String(str_length);
|
||||
return str;
|
||||
} else {
|
||||
return "None";
|
||||
}
|
||||
}
|
||||
|
||||
function getUObjectBaseObjectFromId(index) {
|
||||
var TUObjectArray = GUObjectArray.add(offset_FUObjectArray_TUObjectArray).readPointer();
|
||||
// console.log(`TUObjectArray: ${TUObjectArray}`)
|
||||
var chunk = TUObjectArray.add(parseInt(index / 0x10000) * Process.pointerSize);
|
||||
var FUObjectItemObjects = chunk.readPointer();
|
||||
// console.log(`FUObjectItemObjects: ${FUObjectItemObjects}`);
|
||||
var UObjectBaseObject = FUObjectItemObjects.add(FUObjectItemPadd + (index % 0x10000) * FUObjectItemSize).readPointer();
|
||||
// console.log(`UObjectBaseObject: ${UObjectBaseObject}`);
|
||||
return UObjectBaseObject;
|
||||
}
|
||||
|
||||
function resolveProp(recurrce, prop) {
|
||||
if (prop) {
|
||||
var cname = FField.getClassName(prop);
|
||||
// console.log(`resolveProp cname: ${cname}`);
|
||||
|
||||
if (cname === "ObjectProperty" || cname === "WeakObjectProperty"
|
||||
|| cname === "LazyObjectProperty" || cname === "AssetObjectProperty"
|
||||
|| cname === "SoftObjectProperty") {
|
||||
var propertyClass = UObjectProperty.getPropertyClass(prop);
|
||||
recurrce.push(...[propertyClass]);
|
||||
return UObject.getName(propertyClass) + "*";
|
||||
} else if (cname === "ClassProperty" || cname === "AssetClassProperty" ||
|
||||
cname === "SoftClassProperty") {
|
||||
var metaClass = UClassProperty.getMetaClass(prop);
|
||||
recurrce.push(...[metaClass]);
|
||||
return "class " + UObject.getName(metaClass);
|
||||
} else if (cname === "InterfaceProperty") {
|
||||
var interfaceClass = UInterfaceProperty.getInterfaceClass(prop);
|
||||
recurrce.push(...[interfaceClass]);
|
||||
return "interface class" + UObject.getName(interfaceClass);
|
||||
} else if (cname === "StructProperty") {
|
||||
var Struct = UStructProperty.getStruct(prop);
|
||||
// console.log(`StructProperty addr: ${Struct}`);
|
||||
// return "None";
|
||||
// return UObject.getName(Struct);
|
||||
// if(UObject.isValid(Struct)){
|
||||
recurrce.push(...[Struct]);
|
||||
return UObject.getName(Struct);
|
||||
// } else {
|
||||
// return "None";
|
||||
// }
|
||||
} else if (cname === "ArrayProperty") {
|
||||
return resolveProp(recurrce, UArrayProperty.getInner(prop)) + "[]";
|
||||
} else if (cname === "SetProperty") {
|
||||
return "<" + resolveProp(recurrce, USetProperty.getElementProp(prop)) + ">";
|
||||
} else if (cname === "MapProperty") {
|
||||
return "<" + resolveProp(recurrce, UMapProperty.getKeyProp(prop)) + "," +
|
||||
resolveProp(recurrce, UMapProperty.getValueProp(prop)) + ">";
|
||||
} else if (cname === "BoolProperty") {
|
||||
return "bool";
|
||||
} else if (cname === "ByteProperty") {
|
||||
return "byte";
|
||||
} else if (cname === "IntProperty") {
|
||||
return "int";
|
||||
} else if (cname === "Int8Property") {
|
||||
return "int8";
|
||||
} else if (cname === "Int16Property") {
|
||||
return "int16";
|
||||
} else if (cname === "Int64Property") {
|
||||
return "int64";
|
||||
} else if (cname === "UInt16Property") {
|
||||
return "uint16";
|
||||
} else if (cname === "UInt32Property") {
|
||||
return "uint32";
|
||||
} else if (cname === "UInt64Property") {
|
||||
return "uint64";
|
||||
} else if (cname === "DoubleProperty") {
|
||||
return "double";
|
||||
} else if (cname === "FloatProperty") {
|
||||
return "float";
|
||||
} else if (cname === "EnumProperty") {
|
||||
return "enum";
|
||||
} else if (cname === "StrProperty") {
|
||||
return "FString";
|
||||
} else if (cname === "TextProperty") {
|
||||
return "FText";
|
||||
} else if (cname === "NameProperty") {
|
||||
return "FName";
|
||||
} else if (cname === "DelegateProperty" || cname === "MulticastDelegateProperty") {
|
||||
return "delegate";
|
||||
} else {
|
||||
return FField.getName(prop) + "(" + cname + ")";
|
||||
}
|
||||
}
|
||||
return "NULL";
|
||||
}
|
||||
|
||||
function writeStructChild(childprop) {
|
||||
var recurrce = [];
|
||||
var child = childprop;
|
||||
while (UObject.isValid(child)) {
|
||||
var prop = child;
|
||||
var oname = FField.getName(prop);
|
||||
var cname = FField.getClassName(prop);
|
||||
// console.log(`oname: ${oname}, cname: ${cname}`);
|
||||
if (cname === "ObjectProperty" || cname === "WeakObjectProperty" || cname === "LazyObjectProperty" || cname === "AssetObjectProperty" || cname === "SoftObjectProperty") {
|
||||
var propertyClass = UObjectProperty.getPropertyClass(prop);
|
||||
console.log(`\t${UObject.getName(propertyClass)}* ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
recurrce.push(propertyClass);
|
||||
} else if (cname === "ClassProperty" || cname === "AssetClassProperty" || cname === "SoftClassProperty") {
|
||||
var metaClass = UClassProperty.getMetaClass(prop);
|
||||
console.log(`\tclass ${UObject.getName(metaClass)}* ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
recurrce.push(metaClass);
|
||||
} else if (cname === "InterfaceProperty") {
|
||||
var interfaceClass = UInterfaceProperty.getInterfaceClass(prop);
|
||||
console.log(`\tinterface class ${UObject.getName(interfaceClass)}* ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`)
|
||||
} else if (cname === "StructProperty") {
|
||||
var Struct = UStructProperty.getStruct(prop);
|
||||
console.log(`\t${UObject.getName(Struct)} ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
recurrce.push(Struct);
|
||||
} else if (cname === "ArrayProperty") {
|
||||
console.log(`\t${resolveProp(recurrce, UArrayProperty.getInner(prop))}[] ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "SetProperty") {
|
||||
console.log(`\t${resolveProp(recurrce, USetProperty.getElementProp(prop))} ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "MapProperty") {
|
||||
console.log(`\t${resolveProp(recurrce, UMapProperty.getKeyProp(prop))}, ${resolveProp(recurrce, UMapProperty.getValueProp(prop))}> ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "BoolProperty") {
|
||||
console.log(`\tbool ${oname} //(ByteOffset: ${ptr(UBoolProperty.getByteOffset(prop))}, ByteMask: ${UBoolProperty.getByteMask(prop)}, FieldMask: ${UBoolProperty.getFieldMask(prop)}) [Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "ByteProperty") {
|
||||
console.log(`\tbyte ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "IntProperty") {
|
||||
console.log(`\tint ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "Int8Property") {
|
||||
console.log(`\tint8 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "Int16Property") {
|
||||
console.log(`\tint16 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "Int64Property") {
|
||||
console.log(`\tint64 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "UInt16Property") {
|
||||
console.log(`\tint16 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "UInt32Property") {
|
||||
console.log(`\tint32 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "UInt64Property") {
|
||||
console.log(`\tint64 ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "DoubleProperty") {
|
||||
console.log(`\tdouble ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "FloatProperty") {
|
||||
console.log(`\tfloat ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "EnumProperty") {
|
||||
console.log(`\tenum ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "StrProperty") {
|
||||
console.log(`\tFString ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "TextProperty") {
|
||||
console.log(`\tFText ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "NameProperty") {
|
||||
console.log(`\tFName ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "DelegateProperty" || cname === "MulticastDelegateProperty" || cname === "MulticastInlineDelegateProperty" || cname === "MulticastSparseDelegateProperty") {
|
||||
console.log(`\tdelegate ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else if (cname === "XigPtrProperty") {
|
||||
console.log(`\tXigPtrProperty ${oname}; //[Offset: ${ptr(UProperty.getOffset(prop))}, Size: ${UProperty.getElementSize(prop)}]`);
|
||||
} else {
|
||||
console.log(`\t${cname} ${oname}; //[Size: ${UProperty.getElementSize(prop)}]`);
|
||||
}
|
||||
|
||||
child = FField.getNext(child);
|
||||
}
|
||||
return recurrce;
|
||||
}
|
||||
|
||||
function writeStructChild_Func(childprop) {
|
||||
var recurrce = [];
|
||||
var child = childprop;
|
||||
// console.log(`child: ${child}`);
|
||||
while (UObject.isValid(child)) {
|
||||
var prop = child;
|
||||
var oname = UObject.getName(prop);
|
||||
var cname = UObject.getClassName(prop);
|
||||
|
||||
if (cname.startsWith("Function") || cname === "DelegateFunction") {
|
||||
var returnVal = "void";
|
||||
var params = "";
|
||||
|
||||
var funcParam = UStruct.getChildProperties(prop);
|
||||
// console.log(`funcParam: ${funcParam});
|
||||
while (UObject.isValid(funcParam)) {
|
||||
var PropertyFlags = UProperty.getPropertyFlags(funcParam);
|
||||
// console.log(`PropertyFlags: ${PropertyFlags});
|
||||
if ((PropertyFlags & 0x0000000000000400) == 0x0000000000000400) {
|
||||
returnVal = resolveProp(recurrce, funcParam);
|
||||
} else {
|
||||
if ((PropertyFlags & 0x0000000000000100) == 0x0000000000000100) {
|
||||
params += "out ";
|
||||
}
|
||||
/*if((PropertyFlags & 0x0000000008000000) == 0x0000000008000000){
|
||||
params += "ref ";
|
||||
}*/
|
||||
if ((PropertyFlags & 0x0000000000000002) == 0x0000000000000002) {
|
||||
params += "const ";
|
||||
}
|
||||
// console.log(`funcParam will go in...: ${funcParam});
|
||||
params += resolveProp(recurrce, funcParam);
|
||||
// params += "blahblah";
|
||||
params += " ";
|
||||
params += FField.getName(funcParam);
|
||||
params += ", ";
|
||||
}
|
||||
|
||||
funcParam = FField.getNext(funcParam);
|
||||
}
|
||||
|
||||
if (params.length > 0) {
|
||||
params = params.slice(0, -2);
|
||||
}
|
||||
|
||||
var FunctionFlags = UFunction.getFunctionFlags(prop);
|
||||
// console.log(`FunctionFlags: ${FunctionFlags});
|
||||
|
||||
if ((FunctionFlags & 0x00002000) == 0x00002000) {
|
||||
returnVal = "static " + returnVal;
|
||||
}
|
||||
/*if((FunctionFlags & 0x00000001) == 0x00000001){
|
||||
returnVal = "final " + returnVal;
|
||||
}
|
||||
if((FunctionFlags & 0x00020000) == 0x00020000){
|
||||
returnVal = "public " + returnVal;
|
||||
}
|
||||
if((FunctionFlags & 0x00040000) == 0x00040000){
|
||||
returnVal = "private " + returnVal;
|
||||
}
|
||||
if((FunctionFlags & 0x00080000) == 0x00080000){
|
||||
returnVal = "protected " + returnVal;
|
||||
}*/
|
||||
|
||||
console.log(`\t${returnVal} ${oname}(${params}); // ${ptr(UFunction.getFunc(prop) - moduleBase)}`);
|
||||
} else {
|
||||
console.log(`\t${cname} ${oname}; //[Size: ${UProperty.getElementSize(prop)}]`);
|
||||
}
|
||||
|
||||
child = UField.getNext(child);
|
||||
}
|
||||
return recurrce;
|
||||
}
|
||||
|
||||
function writeStruct(clazz) {
|
||||
var recurrce = [];
|
||||
|
||||
var currStruct = clazz;
|
||||
while (UObject.isValid(currStruct)) {
|
||||
// console.log(`currStruct: ${currStruct}`)
|
||||
var name = UObject.getName(currStruct);
|
||||
// console.log(`name: ${name}`);
|
||||
if (name === "None" || name.indexOf("/Game/") > -1 || name.indexOf("_png") > -1 || name === "") {
|
||||
// console.log(`name is ${name} gonna break`);
|
||||
break;
|
||||
}
|
||||
|
||||
var nameId = UObject.getNameId(currStruct);
|
||||
// console.log(nameId);
|
||||
if (!nameIds.includes(nameId)) {
|
||||
nameIds.push(nameId);
|
||||
console.log(`Class: ${UStruct.getStructClassPath(currStruct)}`)
|
||||
recurrce.push(...writeStructChild(UStruct.getChildProperties(currStruct)));
|
||||
recurrce.push(...writeStructChild_Func(UStruct.getChildren(currStruct)));
|
||||
}
|
||||
currStruct = UStruct.getSuperClass(currStruct);
|
||||
}
|
||||
// console.log(`recurse: ${recurrce}`);
|
||||
for (var item in recurrce) {
|
||||
writeStruct(recurrce[item]);
|
||||
}
|
||||
}
|
||||
|
||||
function dumpObjects() {
|
||||
if (GUObjectArray === undefined) {
|
||||
console.log(`Do set(<moduleName>) first`);
|
||||
return;
|
||||
} else if (GUObjectArray === null) {
|
||||
console.log(`Provide GUObjectArray address`);
|
||||
return;
|
||||
}
|
||||
var ObjectCount = GUObjectArray.add(offset_FUObjectArray_TUObjectArray).add(offset_TUObjectArray_NumElements).readU32();
|
||||
console.log(`ObjectCount: ${ObjectCount}`);
|
||||
|
||||
for (var i = 0; i < ObjectCount; i++) {
|
||||
var UObjectBaseObject = getUObjectBaseObjectFromId(i);
|
||||
if (UObject.isValid(UObjectBaseObject)) {
|
||||
var name = UObject.getName(UObjectBaseObject);
|
||||
console.log(`${i}. name: ${name}`);
|
||||
var className = UObject.getClassName(UObjectBaseObject);
|
||||
console.log(`${i}. class: ${className}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function dumpSdk() {
|
||||
if (GUObjectArray === undefined) {
|
||||
console.log(`Do set(<moduleName>) first`);
|
||||
return;
|
||||
} else if (GUObjectArray === null) {
|
||||
console.log(`Provide GUObjectArray address`);
|
||||
return;
|
||||
}
|
||||
var ObjectCount = GUObjectArray.add(offset_FUObjectArray_TUObjectArray).add(offset_TUObjectArray_NumElements).readU32();
|
||||
|
||||
for (var i = 0; i < ObjectCount; i++) {
|
||||
var UObjectBaseObject = getUObjectBaseObjectFromId(i);
|
||||
// console.log(`UObjectBaseObject: ${UObjectBaseObject}`);
|
||||
if (UObject.isValid(UObjectBaseObject)) {
|
||||
var clazz = UObject.getClass(UObjectBaseObject);
|
||||
writeStruct(clazz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function set(moduleName) {
|
||||
moduleBase = Module.findBaseAddress(moduleName);
|
||||
GUObjectArray = Module.findExportByName(moduleName, "GUObjectArray");
|
||||
if (GUObjectArray == null) {
|
||||
console.log(`[!] Cannot find GUObjectArray. You need to find it by yourself`);
|
||||
}
|
||||
findGName(moduleName);
|
||||
|
||||
var int = setInterval(() => {
|
||||
if (GName !== undefined) {
|
||||
console.log(`\n[*] set ${moduleName} base: ${moduleBase}, GUObjectArray: ${GUObjectArray}, GName: ${GName}`);
|
||||
clearInterval(int);
|
||||
return;
|
||||
} else if (GName === null) {
|
||||
console.log(`\n[*] set ${moduleName} base: ${moduleBase}, GUObjectArray: ${GUObjectArray}, GName: ${GName}`);
|
||||
clearInterval(int);
|
||||
return;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,451 @@
|
||||
Project Path: arc_hackcatml_frida-watchpoint-tutorial_9ar8p4q4
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_hackcatml_frida-watchpoint-tutorial_9ar8p4q4
|
||||
├── README.md
|
||||
├── script.js
|
||||
└── script_set_watchpoint_all_threads.js
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
# frida-watchpoint-tutorial
|
||||
A great feature called [setHardwareWatchpoint](https://frida.re/news/2024/09/06/frida-16-5-0-released/) was introduced in Frida version 16.5.0. This makes it easy to determine where memory is being read from or written to.
|
||||
|
||||
I will explore how to use this feature through a sample Unreal Engine v4.27.2 game.<br>
|
||||
Readers can download it from the [release](https://github.com/hackcatml/frida-watchpoint-tutorial/releases/tag/v1.0.0) section.<br>
|
||||
Since I made the Unreal game myself and already know the logic, I will skip the SDK dump and analysis. I recommend that readers try dumping and analyzing the game logic themselves.
|
||||
|
||||
To save the readers' time, I recommend reading the final section and then immediately attaching the script to test it.<br>
|
||||
The first three sections are more like trial and error before writing the final section.
|
||||
|
||||
- [Android Unreal Engine Tutorial](#android-unreal-engine-tutorial)
|
||||
- [What about iOS?](#what-about-ios)
|
||||
- [Finding a thread](#finding-a-thread)
|
||||
- [Are you sure you really can't set the watchpoint on all threads?](#are-you-sure-you-really-cant-set-the-watchpoint-on-all-threads)
|
||||
|
||||
## Android Unreal Engine Tutorial
|
||||
|
||||
EndlessRunner is a game where you collect coins while running.
|
||||
Let’s run the game and attach a Frida script.
|
||||
```
|
||||
frida -UF -l script.js
|
||||
```
|
||||
|
||||
As the game progresses, you will see the following logs:
|
||||
The number of coins is stored at 0x74cf563e30.
|
||||
```
|
||||
[Galaxy S10::EndlessRunner ]->
|
||||
[*] AddCoin() is called, class instance is 0x74cf563b20
|
||||
[*] TotalCoins are stored at class instance + 0x310
|
||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||||
74cf563e30 01 00 00 00 00 00 00 00 40 72 56 cf 74 00 00 00 ........@rV.t...
|
||||
|
||||
[*] AddCoin() is called, class instance is 0x74cf563b20
|
||||
[*] TotalCoins are stored at class instance + 0x310
|
||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||||
74cf563e30 02 00 00 00 00 00 00 00 40 72 56 cf 74 00 00 00 ........@rV.t...
|
||||
|
||||
[*] AddCoin() is called, class instance is 0x74cf563b20
|
||||
[*] TotalCoins are stored at class instance + 0x310
|
||||
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
|
||||
74cf563e30 03 00 00 00 00 00 00 00 40 72 56 cf 74 00 00 00 ........@rV.t...
|
||||
```
|
||||
|
||||
Where is memory 0x74cf563e30 being written to?
|
||||
Let’s set a watchpoint.
|
||||

|
||||
|
||||
[!] **Which thread should we set the watchpoint on?**<br>
|
||||
In [setHardwareWatchpoint](https://frida.re/news/2024/09/06/frida-16-5-0-released/) example, a watchpoint is set on `Process.enumerateThreads()[0]`.
|
||||
In an Unreal Engine game, this is the main thread, but you will find that setting the watchpoint here doesn’t yield any results.
|
||||
If you take a closer look at the thread names, you’ll notice a thread called `GameThread`. This is where the watchpoint needs to be set.
|
||||

|
||||
|
||||
If you’re unsure which thread to set the watchpoint on, you can try setting it on all threads. The game may crash, but you’ll be able to obtain the thread name.
|
||||
In this case, the installWatchpoint function in the script would be modified as follows:
|
||||
```javascript
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = Process.enumerateThreads();
|
||||
for (const thread of threads) {
|
||||
Process.setExceptionHandler(e => {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr} (${thread.id} ${thread.name})`);
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Once you set a watchpoint at 0x74cf563e30 and progress in the game, the script will print out where memory 0x74cf563e30 is being accessed and written to.
|
||||

|
||||
|
||||
Memory at 0x74cf563e30 is being accessed and written to at 0x751ae5a47c, so let’s examine the instruction at that location.
|
||||
It stores the value of `w9` at `x0 + 310`.
|
||||

|
||||
|
||||
So, before the value is stored at `x0 + 310`, if we change the value of `w9`, we can increase the number of coins.
|
||||
Before hooking the registers at 0x751ae5a47c, let’s unset the watchpoint and detach the interceptor to avoid unexpected crashes.
|
||||

|
||||
|
||||
Now, let’s hook at 0x751ae5a47c and add +100 to the value of the `x9` register at the onEnter point.
|
||||

|
||||
|
||||
Success!<br>
|
||||

|
||||
|
||||
## What about iOS?
|
||||
|
||||
The method is the same for Android.
|
||||
However, finding the thread to set the watchpoint on is a bit more troublesome compared to Android.
|
||||
This is because, when you print the thread names, no meaningful thread names are shown.
|
||||

|
||||
|
||||
Also, if you set a watchpoint on all threads and observe, incorrect information is displayed.
|
||||
In the picture below, it appears that the `com.apple.CoreMotion.MotionThread` is writing to memory.
|
||||

|
||||
|
||||
However, when you set the watchpoint only on `com.apple.CoreMotion.MotionThread` and run the game, you don't get any results.
|
||||

|
||||
|
||||
Perhaps the best method is to set the watchpoint on each thread one by one and check.
|
||||
Since no meaningful thread names are visible, could it be one of the undefined threads?
|
||||
Bingo!
|
||||

|
||||
|
||||
## Finding a thread
|
||||
|
||||
Readers who have read up to this point may have some questions.
|
||||
"Aha, so I just need to find the right thread and set the watchpoint! But what if there are more than 100 threads? In that case, do I need to check each one manually?"
|
||||
|
||||
Indeed, setting a watchpoint on each thread one by one to check is impractical.
|
||||
Upon reflection, the memory write operation I’m interested in is likely being performed by a game function.
|
||||
So, if I hook a game function and get the thread ID when that function is called, that thread is probably the game thread.
|
||||
|
||||
I know for sure that the `AddCoin` function is called each time a coin is obtained in the EndlessRunner game.
|
||||
Based on this, let's rewrite the script.
|
||||
|
||||
```javascript
|
||||
const base = Module.findBaseAddress('libUE4.so');
|
||||
const addCoin = base.add(0x69a4450);
|
||||
let targetThread = null;
|
||||
|
||||
Interceptor.attach(addCoin, {
|
||||
onEnter: function(args) {
|
||||
this.instance = args[0];
|
||||
console.log(`\n[*] AddCoin() is called, class instance is ${args[0]}`);
|
||||
targetThread = Process.enumerateThreads().filter(t => t.id === Process.getCurrentThreadId())[0];
|
||||
console.log(`\n[*] target thread found: ${targetThread.id} ${targetThread.name}`);
|
||||
},
|
||||
onLeave: function(ret) {
|
||||
console.log(`[*] TotalCoins are stored at class instance + 0x310`);
|
||||
console.log(hexdump(ptr(this.instance).add(0x310), {length: 16}));
|
||||
}
|
||||
})
|
||||
|
||||
let unsetWatchPoint = false;
|
||||
let _addr, _size, _conditions;
|
||||
let threads = null;
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
if (targetThread === null) {
|
||||
console.log(`\n[!] Need to find a target thread first`);
|
||||
return;
|
||||
}
|
||||
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = [];
|
||||
threads.push(targetThread);
|
||||
for (const thread of threads) {
|
||||
Process.setExceptionHandler(e => {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
}
|
||||
}
|
||||
|
||||
function reInstallWatchPoint() {
|
||||
for (const thread of threads) {
|
||||
thread.setHardwareWatchpoint(0, _addr, _size, _conditions);
|
||||
}
|
||||
}
|
||||
|
||||
var int = setInterval(() => {
|
||||
if (unsetWatchPoint) {
|
||||
reInstallWatchPoint();
|
||||
unsetWatchPoint = false;
|
||||
}
|
||||
}, 0);
|
||||
```
|
||||
|
||||
Here are the results:
|
||||

|
||||
|
||||
But what if you don’t know which game function is being called at all?
|
||||
In that case, you need to find a commonly called game function within the game.
|
||||
In Unreal Engine games, a function called `operator==(FNameEntryId, EName)` is called repeatedly within the game.
|
||||
This function is used to find the `GName` object. If you're curious, you can refer to [frida-ue4dump wiki](https://github.com/hackcatml/frida-ue4dump/wiki#how-to-find-gname-offset-manually).
|
||||
|
||||
Here’s the script for that case:
|
||||
```javascript
|
||||
let targetThread = null;
|
||||
const _Zeq12FNameEntryId5EName = Module.findExportByName('libUE4.so', "_Zeq12FNameEntryId5EName");
|
||||
|
||||
Interceptor.attach(_Zeq12FNameEntryId5EName, {
|
||||
onEnter: function(args) {
|
||||
targetThread = Process.enumerateThreads().filter(t => t.id === Process.getCurrentThreadId())[0];
|
||||
console.log(`\n[*] target thread found: ${targetThread.id} ${targetThread.name}`);
|
||||
Interceptor.detachAll();
|
||||
},
|
||||
})
|
||||
|
||||
let unsetWatchPoint = false;
|
||||
let _addr, _size, _conditions;
|
||||
let threads = null;
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
if (targetThread === null) {
|
||||
console.log(`\n[!] Need to find a target thread first`);
|
||||
return;
|
||||
}
|
||||
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = [];
|
||||
threads.push(targetThread);
|
||||
for (const thread of threads) {
|
||||
Process.setExceptionHandler(e => {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
}
|
||||
}
|
||||
|
||||
function reInstallWatchPoint() {
|
||||
for (const thread of threads) {
|
||||
thread.setHardwareWatchpoint(0, _addr, _size, _conditions);
|
||||
}
|
||||
}
|
||||
|
||||
var int = setInterval(() => {
|
||||
if (unsetWatchPoint) {
|
||||
reInstallWatchPoint();
|
||||
unsetWatchPoint = false;
|
||||
}
|
||||
}, 0);
|
||||
```
|
||||
|
||||
## Are you sure you really cant set the watchpoint on all threads
|
||||
|
||||
The tutorials above were written to find the correct thread for setting the watchpoint, as setting a watchpoint on all threads was thought to cause the game to freeze or crash due to overload.
|
||||
However, could it be that the script for setting the watchpoint on all threads was poorly written, causing the game to freeze or crash?
|
||||
Let's take a closer look at that script.
|
||||
```javascript
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = Process.enumerateThreads();
|
||||
for (const thread of threads) {
|
||||
Process.setExceptionHandler(e => {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr} (${thread.id} ${thread.name})`);
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Oh no... putting `Process.setExceptionHandler` inside the for loop...
|
||||
The thread where the breakpoint exception occurred is not being properly unset.
|
||||
After modifying the script as follows and running the game, it turned out that setting the watchpoint on all threads did not cause the game to freeze or crash.
|
||||
```javascript
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = Process.enumerateThreads();
|
||||
Process.setExceptionHandler(e => {
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
|
||||
for (const thread of threads) {
|
||||
if (thread.id === Process.getCurrentThreadId()) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for (const thread of threads) {
|
||||
try {
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Try attaching the script to the game and testing it.
|
||||
```
|
||||
frida -UF -l script_set_watchpoint_all_threads.js
|
||||
```
|
||||

|
||||
|
||||
|
||||
## Contact
|
||||
- Channel: https://t.me/hackcatml1
|
||||
- Chat: https://t.me/hackcatmlchat
|
||||
|
||||
```
|
||||
|
||||
`script.js`:
|
||||
|
||||
```js
|
||||
const base = Module.findBaseAddress('libUE4.so');
|
||||
const addCoin = base.add(0x69a4450);
|
||||
|
||||
Interceptor.attach(addCoin, {
|
||||
onEnter: function(args) {
|
||||
this.instance = args[0];
|
||||
console.log(`\n[*] AddCoin() is called, class instance is ${args[0]}`);
|
||||
},
|
||||
onLeave: function(ret) {
|
||||
console.log(`[*] TotalCoins are stored at class instance + 0x310`);
|
||||
console.log(hexdump(ptr(this.instance).add(0x310), {length: 16}));
|
||||
}
|
||||
})
|
||||
|
||||
let unsetWatchPoint = false;
|
||||
let _addr, _size, _conditions;
|
||||
let threads = null;
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = Process.enumerateThreads().filter(t => t.name === "GameThread");
|
||||
for (const thread of threads) {
|
||||
Process.setExceptionHandler(e => {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
}
|
||||
}
|
||||
|
||||
function reInstallWatchPoint() {
|
||||
for (const thread of threads) {
|
||||
thread.setHardwareWatchpoint(0, _addr, _size, _conditions);
|
||||
}
|
||||
}
|
||||
|
||||
var int = setInterval(() => {
|
||||
if (unsetWatchPoint) {
|
||||
reInstallWatchPoint();
|
||||
unsetWatchPoint = false;
|
||||
}
|
||||
}, 0);
|
||||
```
|
||||
|
||||
`script_set_watchpoint_all_threads.js`:
|
||||
|
||||
```js
|
||||
const base = Module.findBaseAddress('libUE4.so');
|
||||
const addCoin = base.add(0x69a4450);
|
||||
|
||||
Interceptor.attach(addCoin, {
|
||||
onEnter: function(args) {
|
||||
this.instance = args[0];
|
||||
console.log(`\n[*] AddCoin() is called, class instance is ${args[0]}`);
|
||||
},
|
||||
onLeave: function(ret) {
|
||||
console.log(`[*] TotalCoins are stored at class instance + 0x310`);
|
||||
console.log(hexdump(ptr(this.instance).add(0x310), {length: 16}));
|
||||
}
|
||||
})
|
||||
|
||||
let unsetWatchPoint = false;
|
||||
let _addr, _size, _conditions;
|
||||
let threads = null;
|
||||
function installWatchpoint(addr, size, conditions) {
|
||||
_addr = addr;
|
||||
_size = size;
|
||||
_conditions = conditions;
|
||||
threads = Process.enumerateThreads();
|
||||
Process.setExceptionHandler(e => {
|
||||
if (['breakpoint', 'single-step'].includes(e.type)) {
|
||||
console.log(`\n[!] ${e.context.pc} tried to "${_conditions}" at ${_addr}`);
|
||||
for (const thread of threads) {
|
||||
if (thread.id === Process.getCurrentThreadId()) {
|
||||
thread.unsetHardwareWatchpoint(0);
|
||||
unsetWatchPoint = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
for (const thread of threads) {
|
||||
try {
|
||||
thread.setHardwareWatchpoint(0, addr, size, conditions);
|
||||
console.log(`[*] HardwareWatchpoint set at ${addr} (${thread.id} ${thread.name})`);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
function reInstallWatchPoint() {
|
||||
for (const thread of threads) {
|
||||
try {
|
||||
thread.setHardwareWatchpoint(0, _addr, _size, _conditions);
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
var int = setInterval(() => {
|
||||
if (unsetWatchPoint) {
|
||||
reInstallWatchPoint();
|
||||
unsetWatchPoint = false;
|
||||
}
|
||||
}, 0);
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,596 @@
|
||||
Project Path: arc_jcalabres_hook-updater_nb78ne9s
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_jcalabres_hook-updater_nb78ne9s
|
||||
├── README.md
|
||||
├── apktool.jar
|
||||
├── helper.py
|
||||
├── img
|
||||
│ └── example1.jpg
|
||||
├── logger.py
|
||||
├── parser.py
|
||||
├── solver.py
|
||||
└── updater.py
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
```java
|
||||
___ ___ ___ ___ _ _____ __ __
|
||||
/\ /\/___\/___\/\ /\ /\ /\ / _ \/ \/_\ /__ \/__\/__\
|
||||
/ /_/ // /// // //_/____/ / \ \/ /_)/ /\ //_\\ / /\/_\ / \//
|
||||
/ __ / \_// \_// __ \_____\ \_/ / ___/ /_// _ \/ / //__/ _ \
|
||||
\/ /_/\___/\___/\/ \/ \___/\/ /___,'\_/ \_/\/ \__/\/ \_/
|
||||
for Android
|
||||
```
|
||||
Did you created any Frida hooks with the Java API and the app has been updated being unable to use them again?
|
||||
|
||||
With this tool, you will be able to update your non-functional Frida hooks to a new ones in only a few minutes!
|
||||
|
||||
It's provided as a solution that intends to update hooks between the same application and slightly different versions e.g. applications with minor update differences and low/mid levels of obfuscation.
|
||||
|
||||
# Table of Contents
|
||||
1. [Features](#features)
|
||||
2. [Requirements](#requirements)
|
||||
3. [Setup](#setup)
|
||||
4. [Usage](#usage)
|
||||
5. [Examples](#examples)
|
||||
6. [How it works?](#how-it-works)
|
||||
7. [Metrics](#metrics)
|
||||
8. [What's next?](#whats-next)
|
||||
9. [Contributing](#contributing)
|
||||
|
||||
# Features
|
||||
|
||||
* Generates an updated Frida hooks file.
|
||||
* Updates hooks of applications with low/mid levels of obfuscation.
|
||||
* All non-standard API hooks are updated.
|
||||
* Compatibility with the most common OS.
|
||||
|
||||
# Requirements
|
||||
|
||||
* *Apktool*
|
||||
* *Python3*
|
||||
|
||||
# Setup
|
||||
|
||||
## MacOS:
|
||||
```
|
||||
brew install apktool
|
||||
```
|
||||
|
||||
## Windows:
|
||||
```bash
|
||||
choco install apktool
|
||||
```
|
||||
|
||||
## Linux:
|
||||
|
||||
Follow this instructions from the [apktool](https://ibotpeaches.github.io/Apktool/install/) website.
|
||||
|
||||
# Usage
|
||||
|
||||
```python
|
||||
updater.py [-h] -old OLD -new NEW -hooks HOOKS -out OUT
|
||||
```
|
||||
|
||||
# Examples
|
||||
|
||||
You can execute the tool with the following parameters:
|
||||
|
||||
```
|
||||
python3 updater.py -old examples/*****/7.4.1.46567.apk -new examples/*****/7.5.0.46863.apk -hooks examples/*****/hooks.js -out examples/*****/updated_hooks.js
|
||||
```
|
||||
An example of execution:
|
||||
|
||||

|
||||
|
||||
If the solver has found the new classes and methods, a summary with the new classes and methods will be shown.
|
||||
|
||||
# How it works?
|
||||
|
||||
The user specifies two different APKs for the same application. It also specifies the old hooks file and the new hooks file paths. Then, the solver of the tool will try to find similarities between the old smali files and the new smali files from the updated APK.
|
||||
|
||||
The application uses multiple metrics and a score system to detect similarities between Java classes and Java methods that are in the Smali format.
|
||||
|
||||
>Smali is a languaje created for representing decompiled Android bytecodes and it can be obtained using the original [baksmali](https://github.com/JesusFreke/smali) tool or using other tools that have integrated it such as apktool or jadx.
|
||||
|
||||
We can summarize the behaviour of this tool in different stages.
|
||||
|
||||
## Stage 1 - Unpacking APKs
|
||||
|
||||
The application unpacks the APKs into a work directory.
|
||||
|
||||
## Stage 2 - Finding the old smali files
|
||||
|
||||
The application find all the classes specified in the configuration file and copies all the smali files that contain these classes into a the work directory.
|
||||
|
||||
## Stage 3 - Finding the new smali files
|
||||
|
||||
The application finds similarities between the specified classes and all the smali files that are inside the new provided APK. Whenever the tool finds these classes, it will copy the smali files into the work folder. Check [Class finder metrics](#class-finder-metrics) for more information.
|
||||
|
||||
## Stage 4 - Finding the new methods
|
||||
|
||||
The application will find similarities between the specified methods and the methods inside the found smali files using multiple metrics. Check [Method-finder-metrics](#class-finder-metrics) for more information.
|
||||
|
||||
## Stage 5 - Solver results
|
||||
|
||||
The results obtained will be written into the new hook file.
|
||||
|
||||
# Metrics
|
||||
|
||||
## Class finder metrics
|
||||
|
||||
The metrics used to find the new classes in a smali file are:
|
||||
|
||||
* Number of fields.
|
||||
* Similarities between fields.
|
||||
* Number of methods.
|
||||
* Similarities between methods.
|
||||
* Similarities between method signatures.
|
||||
|
||||
## Method finder metrics
|
||||
|
||||
* Similarities between signatures.
|
||||
* Similarities between Smali lines of code.
|
||||
|
||||
# What's next?
|
||||
|
||||
* Add support to dump application's DEX files in runtime.
|
||||
* Improve class finder and method finder metrics.
|
||||
* Code refactoring, make clean code.
|
||||
|
||||
# Contributing
|
||||
|
||||
Pull requests and issues are welcome to this repository.
|
||||
```
|
||||
|
||||
`helper.py`:
|
||||
|
||||
```py
|
||||
from random import betavariate
|
||||
from sys import implementation
|
||||
from types import new_class
|
||||
|
||||
from charset_normalizer import CharsetNormalizerMatches
|
||||
from sqlalchemy import false
|
||||
from logger import Logger
|
||||
import subprocess
|
||||
import shutil
|
||||
import re
|
||||
import os
|
||||
|
||||
class Helper:
|
||||
|
||||
def __init__(self):
|
||||
self.config_w_file, self.config = {}, {}
|
||||
self.aux = Aux()
|
||||
self.work_directory = os.path.join(os.getcwd(), "work")
|
||||
self.old_apk_directory = os.path.join(self.work_directory, "old_apk")
|
||||
self.new_apk_directory = os.path.join(self.work_directory, "new_apk")
|
||||
self.old_smali_directory = os.path.join(self.work_directory, "old_smali")
|
||||
self.new_smali_directory = os.path.join(self.work_directory, "new_smali")
|
||||
|
||||
def folder_rm_exists(self, folder_path):
|
||||
if os.path.isdir(folder_path):
|
||||
shutil.rmtree(folder_path)
|
||||
|
||||
def gen_new_hooks(self, results, old_hooks, new_hooks):
|
||||
text_in = open(old_hooks, "r").read()
|
||||
file_out = open(new_hooks, "w")
|
||||
text_out, custom_line = "", ""
|
||||
found = False
|
||||
for line in text_in.splitlines():
|
||||
for k, v in results.items() :
|
||||
(old_class, old_method) = k
|
||||
single_quote_old_class = f"Java.use('{self.aux.smali_to_class(old_class)}')"
|
||||
double_quote_old_class = single_quote_old_class.replace("'",'"')
|
||||
if found:
|
||||
found = False
|
||||
break
|
||||
for values in v:
|
||||
if "\\u" in line:
|
||||
line = self.aux.unscape_unicode(line)
|
||||
if "<init>" in old_method.split("(")[0]:
|
||||
old_method = old_method.replace("<init>","$init")
|
||||
new_class = self.aux.scape_unicode(self.aux.smali_to_class(values[0]))
|
||||
new_method = self.aux.scape_unicode(values[1].split("(")[0])
|
||||
if single_quote_old_class in line or double_quote_old_class in line:
|
||||
single_quote_new_class = f"Java.use('{new_class}')"
|
||||
custom_line = line.split("Java.use")[0] + single_quote_new_class +";"
|
||||
if ".implementation" in line:
|
||||
if "." + old_method.split("(")[0]+".overload" in line or "." + old_method.split("(")[0]+".implementation" in line:
|
||||
args = self.aux.get_smali_args(values[1])
|
||||
custom_line = line.split(".")[0]+"."+new_method+f".overload({args}).implementation =" +line.split("=")[-1]
|
||||
found = True
|
||||
break
|
||||
elif f'["{old_method.split("(")[0]}"].overload' in line or f'["{old_method.split("(")[0]}"].implementation' in line:
|
||||
args = self.aux.get_smali_args(values[1])
|
||||
custom_line = line.split('"')[0]+'"'+self.aux.scape_unicode(old_method.split("(")[0])+'"].overload('+args+').implementation ='+line.split("=")[-1]
|
||||
found = True
|
||||
break
|
||||
if not custom_line:
|
||||
text_out += line+"\n"
|
||||
else:
|
||||
text_out += custom_line+"\n"
|
||||
custom_line = ""
|
||||
file_out.write(text_out)
|
||||
Logger.log(f"Created updated Frida hooks in {new_hooks}.","success")
|
||||
|
||||
def get_frida_config(self, file):
|
||||
hooks = self.aux.remove_comments(open(file, "r").read())
|
||||
hooks_lines = "\n".join([ll.strip() for ll in hooks.splitlines() if ll.strip()]).splitlines()
|
||||
filter = ["android.", "java.", "javax.", "okhttp3."]
|
||||
java_use = "Java.use("
|
||||
config = {}
|
||||
Logger.log(f"Finding Frida hooks to update from {file}.","info")
|
||||
for i in range(len(hooks_lines)):
|
||||
line = hooks_lines[i]
|
||||
if java_use in line:
|
||||
match = self.aux.between_quotes(line)
|
||||
if match:
|
||||
class_hook = match.group(0).replace('"', "").replace("'","")
|
||||
if "\\u" in class_hook:
|
||||
class_hook = self.aux.unscape_unicode(class_hook)
|
||||
if class_hook !="" and not any(substring in class_hook for substring in filter):
|
||||
next_line = hooks_lines[i+1]
|
||||
args = 0
|
||||
if ".overload" in next_line:
|
||||
args = len(next_line.split(".overload")[1].rsplit(".implementation")[0].split(","))
|
||||
method_hook = next_line.rsplit(".overload")[0].split(".")[-1]
|
||||
else:
|
||||
args = 0
|
||||
method_hook = next_line.rsplit(".implementation")[0].split(".")[-1]
|
||||
match = self.aux.between_quotes(method_hook)
|
||||
if match and match.group(0)!="":
|
||||
method_hook = self.aux.between_quotes(method_hook).group(0).replace('"',"")
|
||||
method_hook +="(" + str(args) + ")"
|
||||
if "\\u" in method_hook:
|
||||
method_hook = self.aux.unscape_unicode(method_hook)
|
||||
if "$init(" in method_hook:
|
||||
method_hook = f"<init>({args})"
|
||||
Logger.log(f"Found hook with class {class_hook} and method {method_hook}.","success")
|
||||
smali_class_hook = self.aux.class_to_smali(class_hook)
|
||||
if smali_class_hook in config:
|
||||
config[smali_class_hook].append(method_hook)
|
||||
else:
|
||||
config[smali_class_hook] = [method_hook]
|
||||
return config
|
||||
|
||||
def unpack_apks(self, old_apk_path, new_apk_path):
|
||||
Logger.log("Unpacking the old apk file.","info")
|
||||
self.folder_rm_exists(self.work_directory)
|
||||
subprocess.call(['java', '-jar', 'apktool.jar','d', old_apk_path, '-o', self.old_apk_directory, '-f'], stdout=open(os.devnull, 'w'),
|
||||
stderr=subprocess.STDOUT)
|
||||
Logger.log("Unpacking the new apk file.","info")
|
||||
subprocess.call(['java', '-jar', 'apktool.jar','d', new_apk_path, '-o', self.new_apk_directory, '-f'], stdout=open(os.devnull, 'w'),
|
||||
stderr=subprocess.STDOUT)
|
||||
|
||||
def get_smali_config(self, hooks, old_apk, new_apk):
|
||||
self.config = self.get_frida_config(hooks)
|
||||
self.unpack_apks(old_apk, new_apk)
|
||||
self.get_old_smali()
|
||||
self.get_new_smali()
|
||||
return self.config_w_file, self.new_smali_directory
|
||||
|
||||
def get_old_smali(self):
|
||||
Logger.log("Finding the old smali file.","info")
|
||||
self.folder_rm_exists(self.old_smali_directory)
|
||||
os.mkdir(self.old_smali_directory)
|
||||
for config_class, values in self.config.items():
|
||||
try:
|
||||
output = subprocess.check_output(f'grep -rin \'.class .* {config_class}\' {self.old_apk_directory}/smali*', shell=True).decode("utf-8")
|
||||
smali_file = output.split(":")[0]
|
||||
shutil.copyfile(smali_file, os.path.join(self.old_smali_directory, os.path.basename(smali_file)))
|
||||
self.config_w_file[(config_class, os.path.join(self.old_smali_directory, os.path.basename(smali_file)))] = values
|
||||
Logger.log(f"Old class {config_class} has been found in {os.path.basename(smali_file)}.","success")
|
||||
except Exception as e:
|
||||
Logger.log(f"Old class {config_class} has not been found.","error")
|
||||
|
||||
def get_new_smali(self):
|
||||
self.folder_rm_exists(self.new_smali_directory)
|
||||
os.mkdir(self.new_smali_directory)
|
||||
Logger.log("Copying all the smali files to the work folder.","info")
|
||||
for dir in os.listdir(self.new_apk_directory):
|
||||
if "smali" in dir:
|
||||
path_dir = os.path.join(self.new_apk_directory,dir)
|
||||
shutil.copytree(path_dir, os.path.join(self.new_smali_directory, dir), dirs_exist_ok=True)
|
||||
|
||||
class Aux():
|
||||
|
||||
def unscape_unicode(self, in_str):
|
||||
in_str = in_str.encode('unicode-escape')
|
||||
in_str = in_str.replace(b'\\\\u', b'\\u')
|
||||
in_str = in_str.decode('unicode-escape')
|
||||
return in_str
|
||||
|
||||
def scape_unicode(self, string):
|
||||
string = string.encode('unicode-escape').decode("utf-8")
|
||||
return string
|
||||
|
||||
def remove_comments(self, string):
|
||||
pattern = r"(\".*?\"|\'.*?\')|(/\*.*?\*/|//[^\r\n]*$)"
|
||||
regex = re.compile(pattern, re.MULTILINE|re.DOTALL)
|
||||
def _replacer(match):
|
||||
if match.group(2) is not None:
|
||||
return ""
|
||||
else:
|
||||
return match.group(1)
|
||||
return regex.sub(_replacer, string)
|
||||
|
||||
def between_quotes(self, string):
|
||||
return re.search(r"\"([^\"\\]*(\\.[^\"\\]*)*)\"|\'([^\'\\]*(\\.[^\'\\]*)*)\'", string)
|
||||
|
||||
def class_to_smali(self, class_hook):
|
||||
return "L"+class_hook.replace(".", "/")+";"
|
||||
|
||||
def smali_to_class(self, string):
|
||||
return string[1:-1].replace("/",".")
|
||||
|
||||
def get_smali_args(self, string):
|
||||
if len(string.split(";"))>1:
|
||||
args = ""
|
||||
for arg in string.split("(")[1].split(")")[0].split(";"):
|
||||
arg = arg.replace("/",".").replace(";",",")
|
||||
if arg != "":
|
||||
if arg[:2].isupper():
|
||||
arg = arg[2:]
|
||||
elif arg[:1].isupper:
|
||||
arg = arg[1:]
|
||||
args+='"'+arg+'"'+", "
|
||||
return args.strip(", ")
|
||||
else:
|
||||
return ""
|
||||
```
|
||||
|
||||
`logger.py`:
|
||||
|
||||
```py
|
||||
HEADER = '\033[93m'
|
||||
INFO = '\033[96m'
|
||||
SUCCESS = '\033[92m'
|
||||
SPECIAL = '\033[95m'
|
||||
FAIL = '\033[91m'
|
||||
BOLD = '\033[1m'
|
||||
END = '\033[0m'
|
||||
|
||||
class Logger():
|
||||
|
||||
def log(msg, type):
|
||||
if type == "info":
|
||||
print(BOLD + INFO + "[*] " + msg + END)
|
||||
elif type == "success":
|
||||
print(BOLD + SUCCESS + "[+] " + msg + END)
|
||||
elif type == "error":
|
||||
print(BOLD + FAIL + "[-] " + msg + END)
|
||||
elif type == "summary":
|
||||
print(BOLD + SPECIAL + "[~] " + msg + END)
|
||||
elif type == "header":
|
||||
print(BOLD + HEADER + msg + END)
|
||||
```
|
||||
|
||||
`parser.py`:
|
||||
|
||||
```py
|
||||
class Parser():
|
||||
|
||||
def get_class(self, file):
|
||||
return open(file, "r").readline().split(" ")[-1][0:-1]
|
||||
|
||||
def get_fields(self, file):
|
||||
lines = open(file, "r").readlines()
|
||||
fields = []
|
||||
for line in lines:
|
||||
if ".field" in line:
|
||||
fields.append(line)
|
||||
if ".method" in line:
|
||||
break
|
||||
return fields
|
||||
|
||||
def get_methods(self, file):
|
||||
lines = open(file, "r").readlines()
|
||||
methods = []
|
||||
method_txt = ""
|
||||
add = False
|
||||
for line in lines:
|
||||
if ".method" in line:
|
||||
add = True
|
||||
elif add and ".end method" in line:
|
||||
add = False
|
||||
methods.append(method_txt)
|
||||
method_txt = ""
|
||||
if add:
|
||||
method_txt += line
|
||||
return methods
|
||||
|
||||
def get_signatures(self, methods):
|
||||
signatures = []
|
||||
for method in methods:
|
||||
signature = method.splitlines()[0]
|
||||
signatures.append(signature)
|
||||
return signatures
|
||||
|
||||
def find_method_by_signature(self, method, file):
|
||||
lines = open(file, "r").readlines()
|
||||
add = False
|
||||
method_txt = ""
|
||||
for line in lines:
|
||||
if method in line:
|
||||
add = True
|
||||
elif add and ".end method" in line:
|
||||
add = False
|
||||
method_txt += line
|
||||
if add and line != '\n':
|
||||
method_txt += line
|
||||
return method_txt
|
||||
```
|
||||
|
||||
`solver.py`:
|
||||
|
||||
```py
|
||||
from logger import Logger
|
||||
from parser import Parser
|
||||
import os
|
||||
|
||||
class Solver:
|
||||
|
||||
def __init__(self):
|
||||
self.parser = Parser()
|
||||
self.weight_number_fields = 25
|
||||
self.weight_number_methods = 25
|
||||
self.weight_fields = 50
|
||||
self.weight_methods = 50
|
||||
self.weight_signature = 25
|
||||
|
||||
def solve(self, config_w_file, new_smali_path):
|
||||
if config_w_file:
|
||||
Logger.log("Starting the solver...", "info")
|
||||
found = {}
|
||||
for key, signatures in config_w_file.items():
|
||||
(old_class, old_smali) = key
|
||||
new_smali = self.find_smali(old_smali, new_smali_path)
|
||||
if new_smali == "":
|
||||
Logger.log(f"New smali file not found for class {old_class}", "error")
|
||||
break
|
||||
class_name = self.parser.get_class(new_smali)
|
||||
if new_smali != "":
|
||||
Logger.log(f"New class {class_name} has been found in {os.path.basename(new_smali)}.", "success")
|
||||
for signature in signatures:
|
||||
old_method = self.parser.find_method_by_signature(signature.split('(')[0], old_smali)
|
||||
new_method = self.find_method(old_method, new_smali, signature)
|
||||
if new_method != "":
|
||||
Logger.log(f"Old method partial signature {signature} has been found as {new_method}.", "success")
|
||||
self.print_summary(old_class, old_smali, class_name, signature, new_smali, new_method)
|
||||
if old_class in found:
|
||||
found[(old_class, signature)].append([class_name, new_method])
|
||||
else:
|
||||
found[(old_class, signature)] = [[class_name, new_method]]
|
||||
else:
|
||||
Logger.log(f"Old method partial signature {signature} has not been found.", "error")
|
||||
else:
|
||||
Logger.log(f"New class has not been found.", "error")
|
||||
else:
|
||||
Logger.log(f"Old classes have been not found. Aborting...", "error")
|
||||
return found
|
||||
|
||||
def find_method(self, old_method, new_smali, signature):
|
||||
new_methods = self.parser.get_methods(new_smali)
|
||||
new_signatures = self.parser.get_signatures(new_methods)
|
||||
found_signature, last_score = "", 0
|
||||
for new_signature in new_signatures:
|
||||
signature_name = signature.split("(")[0]
|
||||
args_signature = int(signature.split("(")[1].split(")")[0])
|
||||
if args_signature == 0 and signature_name+"(" in new_signature:
|
||||
found_signature = new_signature.split(' ')[-1]
|
||||
return found_signature
|
||||
elif args_signature>0 and args_signature==len(new_signature.split(";"))-1:
|
||||
if signature_name+"(" in new_signature:
|
||||
found_signature = new_signature.split(' ')[-1]
|
||||
return found_signature
|
||||
if found_signature == "":
|
||||
for new_method in new_methods:
|
||||
score = self.eval_smali(old_method, new_method)
|
||||
if last_score == 0 or score >= last_score:
|
||||
found_signature = self.parser.get_signatures([new_method])[0].split(" ")[-1]
|
||||
last_score = score
|
||||
return found_signature
|
||||
|
||||
def find_smali(self, old_smali, new_smali_path):
|
||||
smali_found, last_score = "", 0
|
||||
old_fields = self.parser.get_fields(old_smali)
|
||||
old_methods = self.parser.get_methods(old_smali)
|
||||
for dirpath, dirname, files in os.walk(new_smali_path):
|
||||
for smali in files:
|
||||
new_methods = self.parser.get_methods(os.path.join(dirpath,smali))
|
||||
score = self.eval_fields(old_fields, self.parser.get_fields(os.path.join(dirpath,smali)))
|
||||
score += self.eval_methods(old_methods, new_methods)
|
||||
score += self.eval_signatures(self.parser.get_signatures(old_methods), self.parser.get_signatures(new_methods))
|
||||
if last_score == 0 or score >= last_score:
|
||||
smali_found = os.path.join(dirpath,smali)
|
||||
last_score = score
|
||||
return smali_found
|
||||
|
||||
def eval_fields(self, old_fields, new_fields):
|
||||
score, fields_found = 0, 0
|
||||
if len(old_fields)==len(new_fields):
|
||||
score+=self.weight_number_fields
|
||||
for field in old_fields:
|
||||
if field in new_fields:
|
||||
fields_found+=1
|
||||
if len(new_fields) !=0:
|
||||
score += (fields_found*self.weight_fields)/(len(new_fields))
|
||||
return int(score)
|
||||
|
||||
def eval_methods(self, old_methods, new_methods):
|
||||
score, methods_found = 0, 0
|
||||
if len(old_methods)==len(new_methods):
|
||||
score+=self.weight_number_methods
|
||||
for method in old_methods:
|
||||
if method in new_methods:
|
||||
methods_found+=1
|
||||
if len(new_methods) !=0:
|
||||
score += (methods_found*self.weight_methods)/(len(new_methods))
|
||||
return int(score)
|
||||
|
||||
def eval_signatures(self, old_signatures, new_signatures):
|
||||
score, signatures_found = 0, 0
|
||||
for signature in old_signatures:
|
||||
if signature in new_signatures:
|
||||
signatures_found+=1
|
||||
if len(new_signatures) !=0:
|
||||
score += (signatures_found*self.weight_methods)/(len(new_signatures))
|
||||
return int(score)
|
||||
|
||||
def eval_smali(self, old_method, new_method):
|
||||
score, lines_found = 0, 0
|
||||
lines = old_method.split("\n")
|
||||
for line in lines:
|
||||
if line in new_method.split("\n"):
|
||||
lines_found += 1
|
||||
score = (lines_found*100)/(len(lines))
|
||||
return int(score)
|
||||
|
||||
def print_summary(self, old_class, old_smali, class_name, signature, new_smali, new_method):
|
||||
Logger.log(f"Class: {old_class} --> {class_name}","summary")
|
||||
Logger.log(f"Method: {signature}* --> {new_method}","summary")
|
||||
Logger.log(f"File: {old_smali} --> {new_smali}","summary")
|
||||
```
|
||||
|
||||
`updater.py`:
|
||||
|
||||
```py
|
||||
import argparse
|
||||
from logger import Logger
|
||||
from helper import Helper
|
||||
from solver import Solver
|
||||
|
||||
LOGO = """
|
||||
___ ___ ___ ___ _ _____ __ __
|
||||
/\ /\/___\/___\/\ /\ /\ /\ / _ \/ \/_\ /__ \/__\/__\
|
||||
/ /_/ // /// // //_/____/ / \ \/ /_)/ /\ //_\\ / /\/_\ / \//
|
||||
/ __ / \_// \_// __ \_____\ \_/ / ___/ /_// _ \/ / //__/ _ \
|
||||
\/ /_/\___/\___/\/ \/ \___/\/ /___,'\_/ \_/\/ \__/\/ \_/
|
||||
"""
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Hook updater!')
|
||||
parser.add_argument('-old', help='Outdated Old .apk file.', required=True),
|
||||
parser.add_argument('-new', help='New .apk file.', required=True),
|
||||
parser.add_argument('-hooks', help='Frida hooks.', required=True)
|
||||
parser.add_argument('-out', help='New Frida hooks path.', required=True)
|
||||
return parser.parse_args()
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
Logger.log(LOGO, "header")
|
||||
helper = Helper()
|
||||
config_w_file, smali_directory = helper.get_smali_config(args.hooks, args.old, args.new)
|
||||
|
||||
solver = Solver()
|
||||
results = solver.solve(config_w_file, smali_directory)
|
||||
helper.gen_new_hooks(results, args.hooks, args.out)
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user