摘要:如何使用呢,可以見我的的上一篇文章點我逆向之路脫殼加固與注意事項寫在最后偶爾聊聊技術(shù),偶爾聊聊逆向,偶爾聊聊生活不能總聊技術(shù)呀,下次一起聊點輕松的。
前言
眾所周知,上次說到了如何脫殼360加固,大致意思就是安裝一個xposed插件,然后自動就會脫殼了,那么這個插件是如何工作的呢,本次重點說說這個。
上次說道了dumpDex脫殼360加固,其實先說個大概,就是從ndk層和java層,適配不同的系統(tǒng),hook關(guān)鍵函數(shù),然后在運行時將dex文件dump出來。
如果僅僅想知道如何使用,可以參見上一篇
點我:Android逆向之路---脫殼360加固、與xposed hook注意事項
需要的環(huán)境無,看文章就可以了解大致了
(當(dāng)然你要是想編譯下dumpDex項目,需要如下工具)
Android Studio
sdk ndk
入口所有的程序執(zhí)行的時候都是有個入口的,dumpDex工程也不例外。
由于是個xposed插件,所以我們先看com.wrbug.dumpdex.XposedInit類。
public class XposedInit implements IXposedHookLoadPackage { //--------略--------- @Override public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) { PackerInfo.Type type = PackerInfo.find(lpparam); if (type == null) { return; } final String packageName = lpparam.packageName; //這里主要是各個app只管解析各個app自己的進(jìn)程的程序 if (lpparam.packageName.equals(packageName)) { //首先在當(dāng)前app的指定目錄,創(chuàng)建好目錄,以便于一會兒脫殼存放dex文件 String path = "/data/data/" + packageName + "/dump"; File parent = new File(path); if (!parent.exists() || !parent.isDirectory()) { parent.mkdirs(); } log("sdk version:" + Build.VERSION.SDK_INT); if (DeviceUtils.isOreo()) { //api為27或27版本的執(zhí)行下面一行,進(jìn)行脫殼 OreoDump.init(lpparam); } else { //低版本api執(zhí)行下面一行進(jìn)行脫殼 LowSdkDump.init(lpparam,type); } } } }
已經(jīng)加好注釋,值得注意的就是,此處程序有分叉了,分別是 OreoDump.init()和LowSdkDump.init()
我們先看OreoDump.init方法
public class OreoDump { //--------略--------- public static void init(final XC_LoadPackage.LoadPackageParam lpparam) { Native.dump(lpparam.packageName); } }
跟著進(jìn)入Native.dump(),
注:不會android ndk也沒關(guān)系,可以繼續(xù)往下看
Native hook以下可以先粗略的說一下,主要就是進(jìn)入了ndk層進(jìn)行hook,然后進(jìn)行dump
進(jìn)入native.cpp文件里面找到JNICALL Java_com_wrbug_dumpdex_Native_dump方法。
由于切換到了c語言,所以我會幫大家刪除一些代碼的細(xì)枝末節(jié),只看主干。
再次聲明下,以下方法實在當(dāng)android版本為26或27的時候,會默認(rèn)進(jìn)行Native層脫殼
JNIEXPORT void JNICALL Java_com_wrbug_dumpdex_Native_dump (JNIEnv *env, jclass obj, jstring packageName) { //在這里作者考慮到了防止每次app啟動的時候都會dump,因此保存了一個變量is_hook來記錄,如果hook過了的話就會退出程序 static bool is_hook = false; char *p = (char *) env->GetStringUTFChars(packageName, 0); if (is_hook) { __android_log_print(ANDROID_LOG_INFO, TAG, "hooked ignore"); return; } init_package_name(p); env->ReleaseStringChars(packageName, (const jchar *) p); //這里由于使用了第三方庫,所以先執(zhí)行第三方庫的初始化操作,具體第三方庫,見下文 ndk_init(env); //下面就是重點了,首先以RTLD_NOW模式打開動態(tài)庫libart.so,拿到句柄 void *handle = ndk_dlopen("libart.so", RTLD_NOW); if (handle == NULL) { __android_log_print(ANDROID_LOG_ERROR, TAG, "Error: unable to find the SO : libart.so"); return; } //根據(jù)不同的版本,拿到不同的對應(yīng)的加載的符號 void *open_common_addr = ndk_dlsym(handle, get_open_function_flag()); //--------略--------- //略掉很多分支,多帶帶說一個,見下文 if (registerInlineHook((uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(), (uint32_t **) get_old_open_function_addr()) != ELE7EN_OK) { __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook failed!"); return; } else { __android_log_print(ANDROID_LOG_ERROR, TAG, "register1 hook success!"); } //設(shè)置hook標(biāo)記為true is_hook = true; }
registerInlineHook( (uint32_t) open_common_addr, (uint32_t) get_new_open_function_addr(), (uint32_t **) get_old_open_function_addr())
已經(jīng)定位到函數(shù)的地址,接下來就是Hook替換以前的函數(shù),替換成我們自己定義的函數(shù),例如下面的函數(shù)
static void *new_arm64_open_common(uint8_t *base, size_t size, void *location, uint32_t location_checksum, void *oat_dex_file, bool verify, bool verify_checksum, void *error_meessage, void *verify_result) { //--------略--------- //首先在程序運行時,保存dex,完成脫殼 save_dex_file(base, size); //調(diào)用以前的函數(shù),保證程序正確執(zhí)行, void *result = old_arm64_open_common(base, size, location, location_checksum, oat_dex_file, verify, verify_checksum, error_meessage, verify_result); return result; }NDK層hook完畢
到此為止NDK層hook分析完畢,分別用了第三方庫hook了android指定版本的加載dex函數(shù)的方法,然后在hook的新函數(shù)里面添加到保存到dump目錄的函數(shù),達(dá)到脫殼
的目的。
ndk hook主要用到的庫
https://github.com/rrrfff/ndk_dlopen
https://github.com/ele7enxxh/Android-Inline-Hook
SDK層hook回到入口的那個章節(jié),還記得嗎,還有一個LowSdkDump.init
這個是低版本的時候的邏輯
public static void init(final XC_LoadPackage.LoadPackageParam lpparam, PackerInfo.Type type) { //如果sdk是23,24,25,26,27之一,那么繼續(xù)使用native層hook if (DeviceUtils.supportNativeHook()) { Native.dump(lpparam.packageName); } //額。。。。。。。。可能百度充錢了 if (type == PackerInfo.Type.BAI_DU) { return; } //見下文說明 XposedHelpers.findAndHookMethod("android.app.Instrumentation", lpparam.classLoader, "newApplication", ClassLoader.class, String.class, Context.class, new XC_MethodHook() { @Override protected void afterHookedMethod(MethodHookParam param) throws Throwable { //執(zhí)行真正的dump方法,然后保存 dump(lpparam.packageName, param.getResult().getClass()); attachBaseContextHook(lpparam, ((Application) param.getResult())); } }); }
上面的主要就是先檢測可不可以natvie層hook,可以的話優(yōu)先native層hook,
然后就是百度可能充錢了,當(dāng)然開個玩笑,這個可以大家自己嘗試。
接下來就是java層hook了,hook了加載類Instrumentation類,的newApplication方法,然后進(jìn)行dump.
還有attachBaseContextHook里面也是主要Hook ClassLoader的loadClass方法,
主要看java層的dump函數(shù)
private static void dump(String packageName, Class> aClass) { Object dexCache = XposedHelpers.getObjectField(aClass, "dexCache"); log("decCache=" + dexCache); Object o = XposedHelpers.callMethod(dexCache, "getDex"); byte[] bytes = (byte[]) XposedHelpers.callMethod(o, "getBytes"); String path = "/data/data/" + packageName + "/dump"; File file = new File(path, "source-" + bytes.length + ".dex"); if (file.exists()) { log(file.getName() + " exists"); return; } FileUtils.writeByteToFile(bytes, file.getAbsolutePath()); }大功告成
代碼陸陸續(xù)續(xù)的看了一遍,可以嘗試畫一個流程圖了
其實文中涉及到的具體的hook的函數(shù),需要我們具體的去看,去研讀android源碼。
這樣才能融匯貫通。
了解了系統(tǒng)是如何加載一個dex的,才能真真正正的理解如何攔截,如何從內(nèi)存dump出來。
dump的時候用的別人的庫,的工具,都還好,主要是思路。如何找到關(guān)鍵點,進(jìn)行dump。
如何使用呢,可以見我的的上一篇文章
點我:Android逆向之路---脫殼360加固、與xposed hook注意事項
寫在最后偶爾聊聊技術(shù),偶爾聊聊逆向,偶爾聊聊生活
不能總聊技術(shù)呀,下次一起聊點輕松的。
博主還是一個懶散的博主。
關(guān)于我個人博客:MartinHan的小站
博客網(wǎng)站:hanhan12312的專欄
知乎:MartinHan01
我的公眾號:
程序技術(shù)指北
(剛開不久,最近在琢磨新東西,謹(jǐn)慎關(guān)注!)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/72761.html
摘要:前言眾所周知,現(xiàn)在軟件在防止逆向采取了混淆,加殼等措施。這兩天在逆向一款的時候找到了一個不錯的插件推薦給大家,下載地址點我下載前提環(huán)境過的手機文件下載地址在上方自動脫殼安裝完成之后,在里面軟重啟,激活。 前言 眾所周知,現(xiàn)在軟件在防止逆向采取了混淆,加殼等措施。比如360加固,騰訊加固,梆梆加固等等。這兩天在逆向一款app的時候找到了一個不錯的xposed插件推薦給大家, 下載地址:點...
摘要:為了防止這種現(xiàn)象,我們可以對字節(jié)碼進(jìn)行混淆。動態(tài)鏈接庫是目標(biāo)文件的集合,目標(biāo)文件在動態(tài)鏈接庫中的組織方式是按照特殊方式形成的。 一、已知防護策略 1.不可或缺的混淆 Java 是一種跨平臺、解釋型語言,Java 源代碼編譯成的class文件中有大量包含語義的變量名、方法名的信息,很容易被反編譯為Java 源代碼。為了防止這種現(xiàn)象,我們可以對Java字節(jié)碼進(jìn)行混淆?;煜粌H能將代碼中的類...
閱讀 1953·2019-08-30 15:53
閱讀 3286·2019-08-30 15:44
閱讀 2873·2019-08-26 13:31
閱讀 2029·2019-08-26 12:10
閱讀 875·2019-08-26 11:01
閱讀 2199·2019-08-23 15:32
閱讀 1673·2019-08-23 13:43
閱讀 2614·2019-08-23 11:58