Найти вручную зарегистрированный (запутанный) адрес собственной функции
Я пытаюсь понять приложение для Android, которое содержит собственный метод с именем foo
в классе com.app.Bar
Внутри класса Bar
есть статическое предложение, которое загружает общий объект System.loadLibrary("libfoo.so")
я предполагаю, что это сборка с -fvisibility = hidden, потому что единственный экспорт JNI_OnLoad
нет JNIEXPORT void JNICALL Java_com_app_Bar_foo
что значит public native int foo
не следует соглашению об именах.
- Каков процесс, когда
foo
вызывается? - Как я могу извлечь адрес
foo
? Я знаком с Фридой - Есть ли способ вывести все
JNINativeMethod[] methods
?
Что я пробовал до сих пор?
JNIAnalyzer выводит 13K строк, которые выглядят как
JNI_OnLoad@@Base+0x712e
Фрида скрипт, чтобы попытаться найти
foo
адрес (не работал)
function intercept(address) {
try {
Interceptor.attach(address, {
onEnter: function(args) {
console.log("onEnter", address);
},
onLeave: function(ignored) {}
});
} catch (e) {
console.error(e);
}
}
function Main() {
var dlopen = new NativeFunction(Module.findExportByName(null, 'dlopen'), 'pointer', ['pointer', 'int']);
var dlsym = new NativeFunction(Module.findExportByName(null, 'dlsym'), 'pointer', ['pointer', 'pointer']);
Process.enumerateModulesSync().forEach(function(m) {
if (m.name === "libfoo.so") {
console.log("Module", JSON.stringify(m));
var handle = dlopen(Memory.allocUtf8String(m.path), 1);
var symb = Memory.allocUtf8String("foo");
var exports = Module.enumerateExportsSync(m.name);
console.log(JSON.stringify({
handle: handle,
symb: symb,
dlsym: dlsym(handle, symb),
exports: exports.map(function(ex){ return ex.address + ": " + ex.name })
}, null, 2));
// intercept all exports
exports.forEach(function(ex){
intercept(ex.address);
});
// explicit intercept foo by known offset
intercept(m.base.add(0x22334)); // this outputs "Error: unable to intercept function at 0x86c96328; please file a bug"
}
});
console.log("sleep..");
Thread.sleep(1.5);
console.log("invoke", Java.use('com.clazz.foo').signToken("A".repeat(32)));
}
Java.perform(Main);
2 ответа
Я решил это с помощью Frida
Зацепление art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)
а также art::JNI::FindClass
после libart.so
модуль загружен.
var fIntercepted = false;
function revealNativeMethods() {
if (fIntercepted === true) {
return;
}
var jclassAddress2NameMap = {};
var androidRunTimeSharedLibrary = "libart.so"; // may change between devices
Module.enumerateSymbolsSync(androidRunTimeSharedLibrary).forEach(function(symbol){
switch (symbol.name) {
case "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib":
/*
$ c++filt "_ZN3art3JNI21RegisterNativeMethodsEP7_JNIEnvP7_jclassPK15JNINativeMethodib"
art::JNI::RegisterNativeMethods(_JNIEnv*, _jclass*, JNINativeMethod const*, int, bool)
*/
var RegisterNativeMethodsPtr = symbol.address;
console.log("RegisterNativeMethods is at " + RegisterNativeMethodsPtr);
Interceptor.attach(RegisterNativeMethodsPtr, {
onEnter: function(args) {
var methodsPtr = ptr(args[2]);
var methodCount = parseInt(args[3]);
for (var i = 0; i < methodCount; i++) {
var pSize = Process.pointerSize;
/*
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; // JNINativeMethod contains 3 pointers
var namePtr = Memory.readPointer(methodsPtr.add(i * structSize));
var sigPtr = Memory.readPointer(methodsPtr.add(i * structSize + pSize));
var fnPtrPtr = Memory.readPointer(methodsPtr.add(i * structSize + (pSize * 2)));
// output schema: className#methodName(arguments)returnVal@address
console.log(
// package & class, replacing forward slash with dot for convenience
jclassAddress2NameMap[args[0]].replace(/\//g, '.') +
'#' + Memory.readCString(namePtr) + // method
Memory.readCString(sigPtr) + // signature (arguments & return type)
'@' + fnPtrPtr // C side address
);
}
},
onLeave: function (ignoredReturnValue) {}
});
break;
case "_ZN3art3JNI9FindClassEP7_JNIEnvPKc": // art::JNI::FindClass
Interceptor.attach(symbol.address, {
onEnter: function(args) {
if (args[1] != null) {
jclassAddress2NameMap[args[0]] = Memory.readCString(args[1]);
}
},
onLeave: function (ignoredReturnValue) {}
});
break;
}
});
fIntercepted = true;
}
Java.perform(revealNativeMethods);
я предполагаю, что это сборка с -fvisibility=hidden, потому что единственным экспортом является JNI_OnLoad, нет JNIEXPORT void JNICALL Java_com_app_Bar_foo, что означает, что public native int foo не соответствует соглашению об именах.
Это не всегда так, потому что вы также можете использовать RegisterNatives
внутри JNI_OnLoad
объявить ваши методы JNI, не придерживаясь типичного соглашения об именах.
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
ALOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
Что происходит, когда вызывается foo?
Может быть, вы можете взглянуть на эту статью https://arophix.com/2017/12/17/andoid-jni-summary/
Как я могу извлечь адрес Foo? (Я знакома с Фридой)
Я не уверен насчет Frida, для Cydia Substrate вы можете использовать dlsym
найти адрес указанного символа .so
например,
void* find_symbol(const char* libraryname, const char* symbolname)
{
void *imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);
if (imagehandle != NULL) {
void * sym = dlsym(imagehandle, symbolname);
if (sym != NULL) {
LOGE("symbol (%s) is found at address %p (%p) in lib %s", symbolname, sym, &sym, libraryname);
return sym;
} else {
LOGE("find_symbol() can't find symblo (%s).", symbolname);
return NULL;
}
} else {
LOGE("dlopen error: %s, when opening lib %s",dlerror(), libraryname);
return NULL;
}
}
Вот полное руководство по использованию Cydia Substrate https://arophix.com/2017/11/30/android-substrate-hooking/,
-------- Обновления --------
Для Фриды, может быть, это похоже. Вот ссылка, которая может помочь в вашем случае. https://www.notsosecure.com/instrumenting-native-android-functions-using-frida/