文章结构:
1 .首先用yang的那个dump so脚本hook不到,然后用他那个hook regestive的脚本也hook不到注册函数。
2.为什么我hook了dlsym、jni的RegisterNative、枚举所有模块的所有导出函数都没有找到我要的函数。
参考资料
RegisterNatives函数具体的流程
static jint RegisterNatives(JNIEnv* env,
2460 jclass java_class,
2461 const JNINativeMethod* methods,
2462 jint method_count) {
2463 if (UNLIKELY(method_count < 0)) {
2464 JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d",
2465 method_count);
2466 return JNI_ERR; // Not reached except in unit tests.
2467 }
2468 CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR);
2469 ScopedObjectAccess soa(env);
2470 StackHandleScope<1> hs(soa.Self());
2471 Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
2472 if (UNLIKELY(method_count == 0)) {
2473 LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for "
2474 << c->PrettyDescriptor();
2475 return JNI_OK;
2476 }
2477 CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR);
2478 for (jint i = 0; i < method_count; ++i) {
2479 const char* name = methods[i].name;
2480 const char* sig = methods[i].signature;
2481 const void* fnPtr = methods[i].fnPtr;
2482 if (UNLIKELY(name == nullptr)) {
2483 ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i);
2484 return JNI_ERR;
2485 } else if (UNLIKELY(sig == nullptr)) {
2486 ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i);
2487 return JNI_ERR;
2488 } else if (UNLIKELY(fnPtr == nullptr)) {
2489 ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i);
2490 return JNI_ERR;
2491 }
2492 bool is_fast = false;
2493 // Notes about fast JNI calls:
2494 //
2495 // On a normal JNI call, the calling thread usually transitions
2496 // from the kRunnable state to the kNative state. But if the
2497 // called native function needs to access any Java object, it
2498 // will have to transition back to the kRunnable state.
2499 //
2500 // There is a cost to this double transition. For a JNI call
2501 // that should be quick, this cost may dominate the call cost.
2502 //
2503 // On a fast JNI call, the calling thread avoids this double
2504 // transition by not transitioning from kRunnable to kNative and
2505 // stays in the kRunnable state.
2506 //
2507 // There are risks to using a fast JNI call because it can delay
2508 // a response to a thread suspension request which is typically
2509 // used for a GC root scanning, etc. If a fast JNI call takes a
2510 // long time, it could cause longer thread suspension latency
2511 // and GC pauses.
2512 //
2513 // Thus, fast JNI should be used with care. It should be used
2514 // for a JNI call that takes a short amount of time (eg. no
2515 // long-running loop) and does not block (eg. no locks, I/O,
2516 // etc.)
2517 //
2518 // A '!' prefix in the signature in the JNINativeMethod
2519 // indicates that it's a fast JNI call and the runtime omits the
2520 // thread state transition from kRunnable to kNative at the
2521 // entry.
2522 if (*sig == '!') {
2523 is_fast = true;
2524 ++sig;
2525 }
2526
2527 // Note: the right order is to try to find the method locally
2528 // first, either as a direct or a virtual method. Then move to
2529 // the parent.
2530 ArtMethod* m = nullptr;
2531 bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->GetVm()->IsCheckJniEnabled();
2532 for (ObjPtr<mirror::Class> current_class = c.Get();
2533 current_class != nullptr;
2534 current_class = current_class->GetSuperClass()) {
2535 // Search first only comparing methods which are native.
2536 m = FindMethod<true>(current_class, name, sig);
2537 if (m != nullptr) {
2538 break;
2539 }
2540
2541 // Search again comparing to all methods, to find non-native methods that match.
2542 m = FindMethod<false>(current_class, name, sig);
2543 if (m != nullptr) {
2544 break;
2545 }
2546
2547 if (warn_on_going_to_parent) {
2548 LOG(WARNING) << "CheckJNI: method to register "" << name << "" not in the given class. "
2549 << "This is slow, consider changing your RegisterNatives calls.";
2550 warn_on_going_to_parent = false;
2551 }
2552 }
2553
2554 if (m == nullptr) {
2555 c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail);
2556 LOG(ERROR)
2557 << "Failed to register native method "
2558 << c->PrettyDescriptor() << "." << name << sig << " in "
2559 << c->GetDexCache()->GetLocation()->ToModifiedUtf8();
2560 ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static");
2561 return JNI_ERR;
2562 } else if (!m->IsNative()) {
2563 LOG(ERROR)
2564 << "Failed to register non-native method "
2565 << c->PrettyDescriptor() << "." << name << sig
2566 << " as native";
2567 ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native");
2568 return JNI_ERR;
2569 }
2570
2571 VLOG(jni) << "[Registering JNI native method " << m->PrettyMethod() << "]";
2572
2573 if (UNLIKELY(is_fast)) {
2574 // There are a few reasons to switch:
2575 // 1) We don't support !bang JNI anymore, it will turn to a hard error later.
2576 // 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI.
2577 // and switching is super easy, remove ! in C code, add annotation in .java code.
2578 // 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess
2579 // since that checks for presence of @FastNative and not for ! in the descriptor.
2580 LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod();
2581 is_fast = false;
2582 // TODO: make this a hard register error in the future.
2583 }
2584
2585 const void* final_function_ptr = m->RegisterNative(fnPtr);
2586 UNUSED(final_function_ptr);
2587 }
2588 return JNI_OK;
2589 }
ALWAYS_INLINE void SetNativePointer(MemberOffset offset, T new_value, PointerSize pointer_size) {
822 static_assert(std::is_pointer<T>::value, "T must be a pointer type");
823 const auto addr = reinterpret_cast<uintptr_t>(this) + offset.Uint32Value();
824 if (pointer_size == PointerSize::k32) {
825 uintptr_t ptr = reinterpret_cast<uintptr_t>(new_value);
826 *reinterpret_cast<uint32_t*>(addr) = dchecked_integral_cast<uint32_t>(ptr);
827 } else {
828 *reinterpret_cast<uint64_t*>(addr) = reinterpret_cast<uintptr_t>(new_value);
829 }
830 }
Java的类加载时,如何注册自己的函数地址
**
UnregisterNative
这个方法**SetEntryPointFromJni
GetJniDlsymLookupStub](http://aospxref.com/android-10.0.0_r47/s?defs=GetJniDlsymLookupStub&project=art)()
函数绑定的地址究竟在哪里,如何拿到对应的偏移?
偏移地址如何优雅的获取?
demo开发原理
利用自写的工具,拿到你当前手机的ArtMethod偏移
aosp10.0以下:
adb install --abi armeabi-v7a xxx.apk
如果你的手机系统在安卓10以上
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function () {
let ReadableNativeMap = Java.use("com.example.getoffsite.MainActivity");
console.log(getHandle(ReadableNativeMap["stringFromJNI"]))
});
var startAddress = ptr('0x754d267ed8'); // artmethod地址
var targetValue = ptr('0x74d82ebbb0'); // app界面上的值
var scanLength = 1024; // 扫描长度(字节数)
function scanMemory(address, target, length) {
for (var i = 0; i < length; i++) {
var currentAddress = address.add(i);
var currentValue = Memory.readPointer(currentAddress);
if (currentValue.equals(target)) {
console.log('Found match at address: ' + currentAddress);
console.log("offsite",currentAddress.sub(startAddress));
return;
}
}
console.log('No match found within the specified range.');
}
scanMemory(startAddress, targetValue, scanLength);
adb install --abi armeabi-v7a demo.apk
小试牛刀,获取一个demoapp的jni绑定地址
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function () {
let ReadableNativeMap = Java.use("com.example.test_1.MainActivity");
console.log(getHandle(ReadableNativeMap["stringFromJNI"]))
});
ptr(0x75480b3ed8).add(16).readPointer();
Process.getModuleByName("libtest_1.so")
样本1:
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function () {
let ReadableNativeMap = Java.use("com.facebook.react.bridge.ReadableNativeMap");
console.log(getHandle(ReadableNativeMap["importValues"]))
});
ptr(0x79b4c96368).add(32).readPointer(); //这里我使用的安卓8.0系统 32是4个指针乘8字节
样本2: 压轴戏(推荐观看)
function getHandle(object) {
var handle = null;
try {
handle = object.$handle;
} catch (e) {
}
if (handle == null) {
try {
handle = object.$h;
} catch (e) {
}
}
if (handle == null) {
try {
handle = object.handle;
} catch (e) {
}
}
return handle;
}
Java.perform(function() {
// 定位类
var targetClass = Java.use('com.cdel.encode.TSEncode');
console.log(getHandle(targetClass.de1))
});
ptr(0x7b3ff992c8).add(32).readPointer();
Java.perform(function() {
// 定位类
var targetClass = Java.use('com.cdel.encode.TSEncode');
// 定义要传递的参数
var param = "7ZvLaMCWJPFQmQX87ZvLaMCWJPEFUzIwJGPZwXlCunyRfQ8xqyCsSt1ADfx3xI3LZkeb.w__X8bMvisv";
// 调用目标方法并获取返回值
var result = targetClass.de1(param);
// 输出结果
console.log("Result: " + result);
});
[Pixel::com.cdel.accmobile ]-> console.log(hexdump(ptr(0x7b3ff992c8)))
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
7b3ff992c8 80 fd e4 14 09 01 00 00 00 00 00 00 2f 1e 00 00 ............/...
7b3ff992d8 03 00 00 00 00 00 00 00 00 b0 10 53 7b 00 00 00 ...........S{...
7b3ff992e8 **80 20 2c 3d 7b** 00 00 00 60 0b b1 68 7b 00 00 00 . ,={...`..h{...
7b3ff992f8 80 fd e4 14 09 01 00 00 00 00 00 00 30 1e 00 00 ............0...
7b3ff99308 04 00 00 00 00 00 00 00 00 b0 10 53 7b 00 00 00 ...........S{...
7b3ff99318 a0 6d b0 68 7b 00 00 00 60 0b b1 68 7b 00 00 00 .m.h{...`..h{...
7b3ff99328 80 fd e4 14 09 01 00 00 00 00 00 00 31 1e 00 00 ............1...
7b3ff99338 05 00 00 00 00 00 00 00 00 b0 10 53 7b 00 00 00 ...........S{...
7b3ff99348 a0 6d b0 68 7b 00 00 00 60 0b b1 68 7b 00 00 00 .m.h{...`..h{...
7b3ff99358 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
7b3ff99368 00 00 00 00 ff ff ff ff 00 00 00 00 00 00 00 00 ................
7b3ff99378 00 00 00 00 00 00 00 00 a0 93 f9 3f 7b 00 00 00 ...........?{...
7b3ff99388 70 08 b1 68 7b 00 00 00 00 00 00 00 00 00 00 00 p..h{...........
7b3ff99398 00 00 00 00 00 00 00 00 30 a3 68 70 00 00 00 00 ........0.hp....
7b3ff993a8 d8 2e 70 70 00 00 00 00 00 00 00 00 00 00 00 00 ..pp............
7b3ff993b8 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................
cat /proc/7129/maps >/data/local/tmp/map.txt
255|sailfish:/data/local/tmp # ./memdumper64 -m -s 7b3d228000 -e 7b3d453000 -n 123.bin -i 7129 -o /sdcard
尾言:
看雪ID:mb_qzwrkwda
https://bbs.kanxue.com/user-home-945390.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):告别RegisterNatives获取JNI函数绑定的地址,迎接最底层的方式获取(3个案例)