分析了下某通信APP,发现他的数据发包参数中有三四个参数在so文件中生成返回;
通过ida动态调试了下APP的so,发现有些调试问题,老是阻断,ida报错的 tid:5056
这个问题没能找到解决方法,有老哥知道了,感谢告知;
这个方法走不通,就来一波曲线救国,经过一般折腾发现的registerNatives;
我本来的目的就是查找到动态注册函数的地址,那么他的地址肯定会在registerNatives方法中存在;
那么我们先看看这个register的方法的使用
写个简单的demo
const char *classPathName ="com/ex/jni_4/MainActivity";
JNINativeMethod method[] = {{"getString", "()Ljava/lang/String;",(void *) native_getString}};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){
JNIEnv *env =NULL;
vm->GetEnv((void **) &env,JNI_VERSION_1_6);
jclass jclass1 = env->FindClass(classPathName);
if (jclass1 ==NULL){
return JNI_ERR;
}
void* p = method;
env->RegisterNatives(jclass1,method,1);
env->DeleteLocalRef(jclass1);
return JNI_VERSION_1_6;
}
registerNatives有四个参数
env是他的第一个参数(这个要注意)
第二个参数是他的Java类
第三个是要注册的所以方法(在JNINativeMethod 的数组)
第四个是要注册的方法数量
hook registerNatives方法的demo(这里就放出一个js)
var ModuleScanning = function (args) {
Process.enumerateModules({
onMatch: function (exp) {
if (exp.namein args) {
console.log("[*] Module:" + exp.name+ ",Address:" + exp.base);
}
},
onComplete: function () {
}
});
};
var RevealNativeMethods = function () {
console.log("native");
var pSize= Process.pointerSize;
var env= Java.vm.getEnv();
var RegisterNatives= 215, FindClassIndex= 6;// search "215" @ https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html
var jclassAddress2NameMap= {};
var moduleDict= {};
function getNativeAddress(idx) {
return env.handle.readPointer().add(idx * pSize).readPointer();
}
// intercepting FindClass to populate Map
Interceptor.attach(getNativeAddress(FindClassIndex), {
onEnter: function (args) {
jclassAddress2NameMap[args[0]]= args[1].readCString();
},
onLeave: function (args) {
}
});
// RegisterNative(jClass*, .., JNINativeMethod *methods[nMethods], uint nMethods) // https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#977
Interceptor.attach(getNativeAddress(RegisterNatives), {
onEnter: function (args) {
for (var i= 0, nMethods= parseInt(args[3]); i< nMethods; i++) {
/*
https://android.googlesource.com/platform/libnativehelper/+/master/include_jni/jni.h#129
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
*/
var structSize= pSize* 3;// = sizeof(JNINativeMethod)
var methodsPtr= ptr(args[2]);
var methodName= methodsPtr.add(i* structSize ).readPointer();
var signature= methodsPtr.add(i* structSize+ pSize).readPointer();
var fnPtr= Memory.readPointer(methodsPtr.add(i* structSize+ pSize* 2));// void* fnPtr
var jClass= jclassAddress2NameMap[args[0]].split('/');
var moduleName= DebugSymbol.fromAddress(fnPtr)['moduleName'];
console.log(JSON.stringify({
module: moduleName,// https://www.frida.re/docs/javascript-api/#debugsymbol
package: jClass.slice(0,-1).join('.'),
class: jClass[jClass.length- 1],
method: methodName.readCString(),// char* name
signature: signature.readCString(),// char* signature TODO Java bytecode signature parser { Z: 'boolean', B: 'byte', C: 'char', S: 'short', I: 'int', J: 'long', F: 'float', D: 'double', L: 'fully-qualified-class;', '[': 'array' } https://github.com/skylot/jadx/blob/master/jadx-core/src/main/java/jadx/core/dex/nodes/parser/SignatureParser.java
address: fnPtr
}));
moduleDict[moduleName]= "1";
}
},
onLeave: function (args) {
ModuleScanning(moduleDict);
}
});
};
Java.perform(RevealNativeMethods);
效果图
module是这个so的名称,我这边没能拿到,但不影响我的目标;后面我找到原因了在修改
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
这个是源码中定义的NINativeMethod的结构体
方法名,方法签名,方法地址
由于没有打印出该so的基址,
只能通过adb来看了
ps 命令拿到pid
cat /proc/(pid)/maps
method":"ai","signature":"()Z","address":"0x244905ff
0x244905ff - 0xd1005000 = ida 中 ai方法的地址
引用
JNI调用和动态注册探索 · 饭先生 | blog
Android逆向之旅—抖音火山视频的Native注册混淆函数获取方法 | 尼古拉斯.赵四