라이브러리 분석 시 (
.init_array
,.init_proc
과 같은 친구들이 있지만) 일반적으로JNI_OnLoad
함수가 가장 먼저 호출되기 때문에 해당 함수를 먼저 분석해야 한다.
이때JNI_OnLoad
함수가 숨겨져 있을 경우 찾는 방법에 대한 포스팅이다.
먼저 JNI_OnLoad
함수 후킹을 통한 방법이다.
라이브러리 로드 시 JNI_OnLoad
후킹을 통해 주소 값을 쉽게 알아낼 수 있다.
Interceptor.attach(Module.findExportByName(null, 'android_dlopen_ext'), {
onEnter: function(args) {
this.path = args[0].readUtf8String();
},
onLeave: function(retval) {
if(this.path.indexOf('libname.so') !== -1) {
var JNI_OnLoad = Module.findExportByName('libname.so', 'JNI_OnLoad');
console.log('[*] JNI_OnLoad : ' + JNI_OnLoad);
Interceptor.attach(JNI_OnLoad, {
onEnter: function(args) {
// hooking ...
}
});
}
}
});
정적 분석 시 JNI_OnLoad
함수가 암호화 되어있다면 JNI_OnLoad
호출 전에 복호화를 진행하기 때문에 호출 시점에 메모리를 덤프하면 정적 분석이 가능하다.
다음은 디버깅을 통한 방법이다. 참고로 나는 Android 11
, arm64-v8a
환경에서 진행했다.
System.loadLibrary
함수가 실행될 때 라이브러리는 대략 아래와 같은 과정으로 로드된다. (자세한 로드 및 링킹 과정은 Reference
참고)
libart.so!art::JavaVMExt::LoadNativeLibrary
(0x397230) > libnativeloader.so!dlopen_ext
> libdl.so!android_dlopen_ext
> linker64!dlopen_ext
> linker64!do_dlopen
(0x35280) > libname.so!.init_func
do_dlopen
함수가 실행될 때 X0
레지스터에서 라이브러리명을 확인할 수 있다.
LoadNativeLibrary
함수가 실행되며 라이브러리가 정상적으로 로드되어 .init_func
함수가 호출된 이후 art::SharedLibrary::FindSymbolWithoutNativeBridge
(0x26BE94) 함수의 리턴 값으로 JNI_OnLoad
함수의 주소 값을 얻을 수 있다.
이후 JNI_OnLoad
주소로 분기(BR X24)
하기 때문에 JNI_OnLoad
주소 값에 BP를 걸어 분석하면 된다.
2023년의 첫 포스팅이다.
1~2년 전에는 JNI_OnLoad 후킹도 제대로 못했는데, 지금 보니 분석 능력도 과거에 비해 많이 개선된 것 같다. 뭘 했는지 잘 모르겠는데 암튼 발전했구나 하는 걸 느낀다.
역시 기록을 해야 하나 보다.
하지만 아직 갈 길이 멀다... 올해도 열심히 하자!
https://cloud.tencent.com/developer/article/1736997
https://android.googlesource.com/platform/bionic.git/+/master/linker/linker.cpp
https://android.googlesource.com/platform/art/+/e34fa1d/runtime/java_vm_ext.cc