最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Radare2靜態(tài)分析apk

2020-12-01 11:59 作者:無情劍客Burning  | 我要投稿

在Radare2靜態(tài)分析apk(1)?對Radare靜態(tài)分析apk進(jìn)行了簡單的介紹。 補(bǔ)充一下: 通過r2 apk://URI可以直接對apk中的dex進(jìn)行分析。

Android的so文件

在前面的文章中,對so文件進(jìn)行了基本的介紹。Android的so有點(diǎn)不一樣。

Android平臺pic(位置無關(guān)代碼)編譯的原因,所有全局變量的引用都是通過got(全局偏移表)完成的,加載器會根據(jù)加載基址來修正,并向got填入正確的全局變量的地址。如某重定位數(shù)據(jù)a=S,app運(yùn)行時(shí)的基址是A,pBuf的地址是B,則重定位a的值為S-A+B,這樣便相當(dāng)于從pBuf處加載so。

通過readelf -d來獲取數(shù)據(jù)重定位的信息。后面會對android的so文件進(jìn)行專門的分析。

JNIEnv在什么時(shí)候創(chuàng)建

開機(jī)的時(shí)候。

JNIEnv是什么

參考鏈接: http://androidxref.com/9.0.0_r3/xref/libnativehelper/include_jni/jni.h和https://www.androidos.net.cn/android/10.0.0_r6/xref/libnativehelper/include_jni/jni.h

#if defined(__cplusplus) typedef _JNIEnv JNIEnv; typedef _JavaVM JavaVM; #else typedef const struct JNINativeInterface* JNIEnv; typedef const struct JNIInvokeInterface* JavaVM; #endif

?如果用C++編譯器,那么JNIEnv就是_JNIEnv?如果用C編譯器,那么JNIEnv就是JNINativeInterface

那_JNIEnv又是什么? 通過下面的代碼可知*最終還是還是JNINativeInterface**。

struct _JNIEnv { ? ? /* do not rename this; it does not seem to be entirely opaque */ ? ? const struct JNINativeInterface* functions; #if defined(__cplusplus) ? ? jint GetVersion() ? ? { return functions->GetVersion(this); } ? ? jclass DefineClass(const char *name, jobject loader, const jbyte* buf, ? ? ? ? jsize bufLen) ? ? { return functions->DefineClass(this, name, loader, buf, bufLen); } ? ? . . . #endif /*__cplusplus*/ };

JNINativeInterface又是什么?通過下面的代碼可知定義了一系列的函數(shù)指針。

struct JNINativeInterface { ? ? void* ? ? ? reserved0; ? ? void* ? ? ? reserved1; ? ? void* ? ? ? reserved2; ? ? void* ? ? ? reserved3; ? ? jint ? ? ? ?(*GetVersion)(JNIEnv *); ? ? jclass ? ? ?(*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, ? ? ? ? ? ? ? ? ? ? ? ? jsize); ? ? jclass ? ? ?(*FindClass)(JNIEnv*, const char*); ? ? jmethodID ? (*FromReflectedMethod)(JNIEnv*, jobject); ? ? jfieldID ? ?(*FromReflectedField)(JNIEnv*, jobject); ? ? /* spec doesn't show jboolean parameter */ ? ? jobject ? ? (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean); ? ? jclass ? ? ?(*GetSuperclass)(JNIEnv*, jclass); ? ? . . . ?};

創(chuàng)建JNIEnv

參考鏈接: https://www.androidos.net.cn/android/10.0.0_r6/xref/frameworks/base/cmds/app_process/app_main.cpp

Zygote進(jìn)程是在Init進(jìn)程啟動的時(shí)候創(chuàng)建的。至于Init進(jìn)程是怎么來的,可參考Android啟動流程。

int main(int argC, char* const argv[]) { ? ? if (!LOG_NDEBUG) { ? ? ? String8 argv_String; ? ? ? for (int i = 0; i < argC; ++i) { ? ? ? ? argv_String.append("\""); ? ? ? ? argv_String.append(argv[i]); ? ? ? ? argv_String.append("\" "); ? ? ? } ? ? ? ALOGV("app_process main with argv: %s", argv_String.string()); ? ? } ? ? AppRuntime runtime(argv[0], computeArgBlockSize(argC, argv)); ? ? . . . ? ? // Parse runtime arguments. ?Stop at first unrecognized option. ? ? bool zygote = false; ? ? bool startSystemServer = false; ? ? bool application = false; ? ? String8 niceName; ? ? String8 className; ? ? ++i; ?// Skip unused "parent dir" argument. ? ? while (i < argC) { ? ? ? ? const char* arg = argv[i++]; ? ? ? ? if (strcmp(arg, "--zygote") == 0) { ? ? ? ? ? ? zygote = true; ? ? ? ? ? ? niceName = ZYGOTE_NICE_NAME; ? ? ? ? ? ? . . . ? ? } ? ? . . . ? ? if (zygote) { ? ? ? ? runtime.start("com.android.internal.os.ZygoteInit", args, zygote); ? ? } else if (className) { ? ? ? ? runtime.start("com.android.internal.os.RuntimeInit", args, zygote); ? ? } else { ? ? ? ? fprintf(stderr, "Error: no class name or --zygote supplied.\n"); ? ? ? ? app_usage(); ? ? ? ? LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); ? ? } }

如果zygote是true,則說明當(dāng)前運(yùn)行在Zygote進(jìn)程。走runtime.start("com.android.internal.os.ZygoteInit", args, zygote),start方法的實(shí)現(xiàn)如下:

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ? ? ALOGD(">>>>>> START %s uid %d <<<<<<\n", ? ? ? ? ? ? className != NULL ? className : "(unknown)", getuid()); ? ? . . . ? ? ?/* start the virtual machine */ ? ? JniInvocation jni_invocation; ? ? jni_invocation.Init(NULL); ? ? JNIEnv* env; ? ? //啟動Java虛擬機(jī) 001 ? ? if (startVm(&mJavaVM, &env, zygote) != 0) { ? ? ? ? return; ? ? } ? ? onVmCreated(env); ? ? /* ? ? ?* Register android functions. ? ? ?*/ ? ? ?//為Java虛擬機(jī)注冊JNI方法 ? ? if (startReg(env) < 0) { ? ? ? ? ALOGE("Unable to register all android natives\n"); ? ? ? ? return; ? ? } ? ? /* ? ? ?* We want to call main() with a String array with arguments in it. ? ? ?* At present we have two arguments, the class name and an option string. ? ? ?* Create an array to hold them. ? ? ?*/ ? ? jclass stringClass; ? ? jobjectArray strArray; ? ? jstring classNameStr; ? ? stringClass = env->FindClass("java/lang/String"); ? ? assert(stringClass != NULL); ? ? strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL); ? ? assert(strArray != NULL); ? ? classNameStr = env->NewStringUTF(className); ? ? assert(classNameStr != NULL); ? ? env->SetObjectArrayElement(strArray, 0, classNameStr); ? ? for (size_t i = 0; i < options.size(); ++i) { ? ? ? ? jstring optionsStr = env->NewStringUTF(options.itemAt(i).string()); ? ? ? ? assert(optionsStr != NULL); ? ? ? ? env->SetObjectArrayElement(strArray, i + 1, optionsStr); ? ? } ? ? /* ? ? ?* Start VM. ?This thread becomes the main thread of the VM, and will ? ? ?* not return until the VM exits. ? ? ?*/ ? ? char* slashClassName = toSlashClassName(className != NULL ? className : ""); ? ? jclass startClass = env->FindClass(slashClassName); ? ? if (startClass == NULL) { ? ? ? ? ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); ? ? ? ? /* keep going */ ? ? } else { ? ? ? ? //調(diào)ZygoteInit的main方法 ? ? ? ? jmethodID startMeth = env->GetStaticMethodID(startClass, "main", ? ? ? ? ? ? "([Ljava/lang/String;)V");//002 ? ? ? ? if (startMeth == NULL) { ? ? ? ? ? ? ALOGE("JavaVM unable to find main() in '%s'\n", className); ? ? ? ? ? ? /* keep going */ ? ? ? ? } else { ? ? ? ? ? ? env->CallStaticVoidMethod(startClass, startMeth, strArray); #if 0 ? ? ? ? ? ? if (env->ExceptionCheck()) ? ? ? ? ? ? ? ? threadExitUncaughtException(env); #endif ? ? ? ? } ? ? } ? ? free(slashClassName); ? ? ALOGD("Shutting down VM\n"); ? ? if (mJavaVM->DetachCurrentThread() != JNI_OK) ? ? ? ? ALOGW("Warning: unable to detach main thread\n"); ? ? if (mJavaVM->DestroyJavaVM() != 0) ? ? ? ? ALOGW("Warning: VM did not shut down cleanly\n"); }

在上文的注釋002處,進(jìn)入ygoteInit的main方法。Zygote便進(jìn)入了Java框架層,也就是說Zygote開創(chuàng)了Java框架層。

在注釋001處,調(diào)用startVM啟動Java虛擬機(jī)。

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) { ? ? . . . ? ? /* ? ? ?* Initialize the VM. ? ? ?* ? ? ?* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. ? ? ?* If this call succeeds, the VM is ready, and we can start issuing ? ? ?* JNI calls. ? ? ?*/ ? ? if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ? ? ? ? ALOGE("JNI_CreateJavaVM failed\n"); ? ? ? ? return -1; ? ? } ? ? return 0; }

參考鏈接: https://www.androidos.net.cn/android/10.0.0_r6/xref/libnativehelper/JniInvocation.cpp

jint JniInvocationImpl::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { ? //調(diào)用前面從libart.so中找到的創(chuàng)建虛擬機(jī)的函數(shù) ? return JNI_CreateJavaVM_(p_vm, p_env, vm_args); }

NIEnv是在Zygote初始化的時(shí)候調(diào)用libart.so中的"JNI_CreateJavaVM"方法創(chuàng)建的。JNIEnv來的竟然如此復(fù)雜!

JNIEnv基礎(chǔ)

1.線程相關(guān) : JNIEnv 是線程相關(guān)的, 即 在 每個(gè)線程中 都有一個(gè) JNIEnv 指針, 每個(gè)JNIEnv 都是線程專有的, 其它線程不能使用本線程中的 JNIEnv, 線程 A 不能調(diào)用 線程 B 的 JNIEnv。JNIEnv 不能跨線程 :

?當(dāng)前線程有效 : JNIEnv 只在當(dāng)前線程有效, JNIEnv 不能在 線程之間進(jìn)行傳遞, 在同一個(gè)線程中, 多次調(diào)用 JNI層方法, 傳入的 JNIEnv 是相同的;?本地方法匹配多JNIEnv : 在 Java 層定義的本地方法, 可以在不同的線程調(diào)用, 因此 可以接受不同的 JNIEnv;


1.?所有的JNI調(diào)用都使用了JNIEnv*類型的指針,習(xí)慣上在CPP文件中將這個(gè)變量定義為evn,它是任意一個(gè)本地方法的第一個(gè)參數(shù)。env指針指向一個(gè)函數(shù)指針表,在VC中可以直接用"->"操作符訪問其中的函數(shù)。2.jobject 指向在此 Java 代碼中實(shí)例化的 Java 對象 LocalFunction的一個(gè)句柄,相當(dāng)于this指針。后續(xù)的參數(shù)就是本地調(diào)用中有Java程序傳進(jìn)的參數(shù)。以下是我們經(jīng)常會用到的字符串類型處理的函數(shù):

?const char* GetStringUTFChars (jstring string,jboolean* isCopy) 返回指向字符串UTF編碼的指針,如果不能創(chuàng)建這個(gè)字符數(shù)組,返回null。這個(gè)指針在調(diào)用ReleaseStringUTFChar()函數(shù)之前一直有效。 參數(shù): string Java字符串對象 isCopy 如果進(jìn)行拷貝,指向以JNI_TRUE填充的jboolean,否則指向以JNI_FALSE填充的jboolean。?void ReleaseStringUTFChars(jstring str, const char* chars) 通知虛擬機(jī)本地代碼不再需要通過chars訪問Java字符串。 參數(shù): string Java字符串對象 chars 由GetStringChars返回的指針


?jstring NewStringUTF(const char *utf) 返回一個(gè)新的Java字符串并將utf內(nèi)容拷貝入新串,如果不能創(chuàng)建字符串對象,返回null。通常在反值類型為string型時(shí)用到。 參數(shù): utf UTF編碼的字符串指針,對于數(shù)值型參數(shù),在C/C++中可直接使用

JNIEnv編程

通過ANdroid studio 新建c++項(xiàng)目,需要下載ndk和CMakelist。 核心代碼:

MainActivity:

? ?static { ? ? ? ? System.loadLibrary("native-lib"); ? ? } ? ? . . . ? ? tv.setText(stringFromJNI());

native-lib.cpp

#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI( ? ? ? ? JNIEnv* env, ? ? ? ? jobject /* this */) { ? ? std::string hello = "Hello from C++"; ? ? return env->NewStringUTF(hello.c_str()); }

CMakeList.txt

add_library( # Sets the name of the library. ? ? ? ? ? ? ?native-lib ? ? ? ? ? ? ?# Sets the library as a shared library. ? ? ? ? ? ? ?SHARED ? ? ? ? ? ? ?# Provides a relative path to your source file(s). ? ? ? ? ? ? ?native-lib.cpp )

使用Radare2靜態(tài)分析

使用pdf看一下,只能看出有字符串"Hello from c++"。至于怎么處理的看不出來。

decompiler看一下效果:

還是不能直接看出來怎么處理的“hello from c++”。 如果能把int64_t轉(zhuǎn)換為JNIEnv *,同時(shí)相關(guān)的函數(shù)也可以帶出來。我查詢了一些資料,通過ESIL能夠模擬出JNIEnv的地址,不過我沒有成功,然后通過tl命令進(jìn)行鏈接。類型處理是Radare2做得不太好的地方。不過問題總有解決辦法,既然靜態(tài)分析看不出來什么,那就使用動態(tài)分析。動態(tài)分析的內(nèi)容會在后續(xù)文章中介紹。

這個(gè)使用Frida走一波,看看最終的返回值是什么。

function frida_Java() { ? ? Java.perform(function () { ? ? ? ? ? var targetClass = Java.use("com.example.myapplication.MainActivity"); ? ? ? ? ? targetClass.stringFromJNI.implementation = function(){ ? ? ? ? ? ? var result = this.stringFromJNI(); ? ? ? ? ? ? console.log("burning result "+result); ? ? ? ? ? ? return result; ? ? ? ? ? } ? ? ? ? ? var targetFunc = Module.getExportByName("libnative-lib.so", 'Java_com_example_myapplication_MainActivity_stringFromJNI'); ? ? ? ? ? console.log("burning"+targetFunc); ? ? ? ? ? Interceptor.attach(targetFunc, { ? ? ? ? ? ? onEnter: function (args) { ? ? ? ? ? ? ? ? console.log(args[0]); ? ? ? ? ? ? ? ? console.log("burning Env"+JSON.stringify(Java.vm.getEnv())); ? ? ? ? ? ? }, ? ? ? ? ? ? onLeave: function (retval) { ? ? ? ? ? ? ? console.log("retval "+retval.toString()); ? ? ? ? ? ? } ? ? ? ? ? }); ? ? }); ? } ? ? ? ? ?setImmediate(frida_Java,0);

通過如下的命令運(yùn)行,這樣才能hook onCreate方法。

frida -U ?-f ?com.example.myapplication -l answner.js ?--no-pause

運(yùn)行結(jié)果如下:

如果我想改返回值,

? ? ? ? ?targetClass.stringFromJNI.implementation = function(){ ? ? ? ? ? ? var result = this.stringFromJNI(); ? ? ? ? ? ? console.log("burning result "+result); ? ? ? ? ? ? result = "歡迎關(guān)注我的微信公眾號:無情劍客"; ? ? ? ? ? ? return result; ? ? ? ? ? }

寫在最后

通過Radare2靜態(tài)分析so文件,不能看出直觀的邏輯,即使使用decompiler也不是非常友好,使用ESIL模擬定位JNIEnv沒能成功定位。希望Radare2在類型處理方面加強(qiáng)一些。動態(tài)調(diào)式在后續(xù)文章中會更新。

公眾號

更多內(nèi)容,歡迎關(guān)注我的微信公眾號:無情劍客。


Radare2靜態(tài)分析apk的評論 (共 條)

分享到微博請遵守國家法律
茌平县| 当雄县| 抚远县| 柏乡县| 吉林省| 洮南市| 交城县| 台东市| 宝鸡市| 泸西县| 来宾市| 开封市| 安徽省| 渭南市| 陆丰市| 南雄市| 寿宁县| 顺平县| 措勤县| 兴山县| 白城市| 南阳市| 肥乡县| 望谟县| 柘荣县| 琼中| 嘉鱼县| 呼伦贝尔市| 娱乐| 东安县| 沽源县| 阿拉善盟| 乡城县| 和林格尔县| 岳阳市| 丰台区| 社旗县| 玛曲县| 东宁县| 文水县| 宜丰县|