Android免Root權限Hook系統函數修改程序運行時內存指令邏輯

2021-02-13 編碼美麗

在之前一篇文章中,已經介紹了Android中如何修改內存指令改變方法執行邏輯,當時那篇文章的大致流程很簡單,在程序運行起來,dex文件被加載到內存中之後,通過讀取maps文件,獲取到dex文件的內存起始地址,然後通過文件頭信息找到指定dex在內存中的數據結構,這裡還需要詳細了解dex文件的格式,不了解的同學可以點擊看這篇文章:Android中Dex文件格式解析,然後使用系統函數修改內存讀寫屬性,在通過指定方法名找到該方法在內存的指令地址,然後替換即可。我們可以簡單看一下dex文件被映射到內存之後的地址:


上面的這種方案有一點不好就是,需要熟悉dex文件格式,然後通過方法名通過地址轉化獲取其內存對應的指令,操作有點繁瑣,而本文將介紹一種簡便的方式,修改起來非常簡單。就是通過hook系統函數來做到這一點。而這種hook功能是免root的,所以只能hook應用內部邏輯。對其他應用程式沒有任何效果,不過這個就已經滿足本文操作的需求了,關於hook系統函數網上已經有現成的框架:Android-Inline-Hook;這個框架用法也非常簡單。自己下載之後導入工程即可。下面來看看它的具體用法:


新建一個NDK工程,這個不多說了,然後把這幾個框架中的文件拷貝到jni目錄下,hook代碼主要在InlineHook.cpp中:


這裡看到我們會用到兩個函數進行hook:

第一個是註冊函數:registerInlineHook

參數:

    1、原始函數地址

    2、hook的新函數地址

    3、原始函數的二級指針

第二個是hook函數:inlineHook

參數:

    1、原始函數地址

這裡為了演示效果,我們先Hook系統函數puts,需要在hook之前定義新函數以及舊函數的函數指針類型,這裡一定要注意,新函數定義類型要和原始函數保持一致。不然hook失敗的:


接下來,我們需要出發這個hook操作,我們可以在java層定義一個native方法,然後加載出發即可:


這裡定義一個native方法了,然後用javah命令生成指定的頭文件即可:


在native方法中開始進行hook操作,運行程序看日誌信息即可:


看到了我們成功的hook了系統函數puts。接下來我們開始進入本文的正題了,如何hook系統函數來修改程序運行時態指令。

在上面的hook操作中可以看到,如果想hook一個函數,需要先找到這個函數的聲明,所以第一步需要想好hook哪個系統函數,如何獲取這個函數的聲明?這個不難,因為我們想修改程序運行時態指令,那麼肯定和dex加載解析過程分不開,這個就簡單了,直接去 [Android源碼/dalvik/libdex/] 下找到DexFile.h頭文件,查看他的內部函數聲明和一些數據結構定義信息:


我們發現了這個函數,為什麼呢?因為我們知道一個方法執行之前肯定需要解析類信息加載到內存,而這個函數就是加載類必定運行的函數,在看看這個函數的聲明:

返回值是DexClassDef結構體指針,看看DexClassDef定義結構體:


這個結構體描述了一個類的詳細信息,每個欄位在這裡不多解釋了,不了解的同學可以去看看之前介紹dex文件格式的那篇文章。這裡關心的就是類代碼數據的偏移地址,這個值在後面會用到,用它獲取類代碼結構體信息,後面會介紹。

兩個參數是:

第一個參數:DexFile結構體指針


這個結構體包括了整個類的全局信息。後面再獲取其他結構體信息都會用到這個值。

第二個參數:是加載類的名稱

這個參數在這裡也非常重要,因為我們想修改一個方法的指令,肯定需要通過類去查找的,這個類名就非常重要了。

分析完了這個函數聲明之後,下面就開始操作了hook了,不過還需要做兩件事:

第一件事:因為我們看到上面涉及到很多dex的結構體定義,所以我們需要手動的把這個系統頭文件DexFile.h拷貝到我們的工程中,我們可以只保留一些有用的結構體定義和函數即可。

第二件事:因為hook的時候需要原始函數地址的,所以這裡我們需要利用系統函數dlopen和dlsym來獲取指定函數的地址,關於這兩個函數用法網上介紹的知識非常多了,這裡不在詳細介紹了,他們大致的功能就是可以通過函數名獲取so文件中的函數地址。

這裡要注意,為了獲取正確的函數名稱,我們需要導出設備中的libdvm.so文件,在設備的[/system/lib/libdvm.so]下,然後用IDA打開libdvm.so文件:


搜索dexFindClass函數名,然後查看他的代碼位置,獲取導出的函數名。

上面兩件事完成之後,下面就可以開始hook操作了,操作過程和上面hook系統函數puts方式完全一樣:


然後我們使用dlopen和dlsym函數獲取正確的函數地址即可。


hook觸發邏輯,依然是之前定義的native方法:


到這裡,我們還需要做一個操作,就是手動利用DexClassLoader來加載一個我們自己編寫的dex文件,來看看hook是否成功了。所以我們還需要在構建一個工程:


這個工程非常簡單,有一個核心的工具類,類中有一個計算方法:


我們的目的就是把這個方法的乘法改成加法操作。運行這個工程,獲取dex文件,這裡為了加載簡單,直接把這個dex文件放到SD卡目錄下,然後在回到上面的hook工程,需要在Java成編寫一個加載dex文件的方法:


我們利用DexClassLoader加載之前將CoreDex工程編譯獲取的dex文件,然後加載類利用反射執行計算方法,傳入的參數是2和3,正常結果是乘法也就是6,我們就要把乘法變成加法,讓結果輸出的是5。加載邏輯我們用一個點擊事件來觸發:


在回到native層中的hook代碼:


這裡主要看hook的新函數功能,過程有點複雜,這裡一步一步來詳細分析。首先我們需要過濾處理的類,不能把所有的類都做處理,然後通過原始函數,獲取類的DexClassDef結構信息,然後利用系統函數dlsym調用函數dexReadAndVerifyClassData獲取類對應的數據結構信息,這裡依然需要用IDA打開libdvm.so文件查看這個函數的導出名稱:


獲取到類對應的數據結構DexClassData信息,之後就可以獲取類中的方法個數和具體信息了,這裡再來看一下DexClassData數據結構信息,這個結構體在DexClass.h中,我們依然把結構信息拷貝到我們的工程中即可:


有了這個結構體,下面就來獲取方法的個數,這裡的方法分為類方法和對象方法,在DexDataClassHeader結構體信息中。

這裡利用系統函數dexGetClassData獲取類的代碼數據結構:

接下來,繼續看如何獲取類中的方法信息:


因為我們知道那個calculateMoney方法是對象方法,所以這裡直接獲取對象方法結構體信息,然後依次遍歷獲取每個方法,通過系統函數dexGetMethodId獲取DexMethodIds結構體信息:


這裡需要注意的是每個方法都是在內存中依次挨著的,所以直接利用指針操作即可獲取每個方法的結構體信息。然後在利用系統函數dexStringById獲取方法名稱,這個也是系統函數,一樣方式拷貝到工程中來即可:


有了方法名就需要進行過濾了,只處理我們的那個calculateMoney方法,然後在獲取方法對應的數據結構信息DexCode了,依然如此,我們需要把DexCode結構體信息從系統中拷貝到工程中:


然後利用系統函數dexGetCode通過DexMethod結構體獲取DexCode結構體信息


有了DexCode結構體信息之後,我們可以列印方法的原始指令數據:


然後我們因為需要修改內存指令,所以還需要把內存修改為可讀屬性:


這裡需要注意的是,修改的起始地址一定是系統內存頁的整數倍,所以需要做一次轉化。修改完內存屬性之後。

接下來就可以構造指令,然後替換內存指令即可。那麼如何獲取原始指令,怎麼把乘法改成加法呢?這裡就需要利用010Editor軟體了,直接查看這個方法的指令數據:


這裡看到,這個方法有三條指令,但是一條指令是兩個字節,所以一共是6個字節,這裡看到的是十進位的數據了,我們可以把這三個十進位數據轉化成6個十六進位數據:


然後我們現在只需要把乘法指令碼改成加法指令碼即可,這個需要參考Bytecode of Dalvik了:


這裡也看到加法指令就是十六進位的90,也就是十進位的144,所以咋們替換指令就簡單了:


替換指令之後,在此列印指令數據即可,好了,到這裡我們就完成了所有的操作了,下面就來運行看看日誌信息了:


看到日誌,我們成功的把指令146變成了144了,在往下看日誌,就可以看到計算結果是加法了,也就是5:


就這樣我們在內存中修改了這個方法的指令邏輯,把乘法邏輯變成了加法邏輯了。神奇吧。到這裡我們也算介紹完了本文的大致內容了。不過有的同學可以看得沒太明白,沒關係,下面在來總結一下流程:

首先明確我們的目的就是想能修改內存中指定方法的運行指令邏輯,那麼不用之前介紹的那個讀取內存中的dex數據然後靠地址來檢索到指令地址,而是採用hook系統函數來實現:

第一步:就需要找到hook點,每個方法要想運行肯定是先將方法所屬的類加載到內存中,那麼就需要調用系統的函數:dexFindClass,而這個函數的返回值是一個DexClassDef結構體信息。

第二步:通過DexClassDef結構體信息獲取類的數據結構體信息DexClassData,然後獲取類的所有方法信息。

第三步:遍歷方法結構體信息DexMethod,找到我們想要處理的方法信息,然後在獲取其DexCode方法數據結構體信息。

第四步:有了方法數據結構體信息之後,就可以獲取到方法的指令個數和具體指令數據,在修改之前必須修改內存屬性為可讀寫的。

第五步:通過查閱虛擬機指令集,找到加法指令碼替換原來的乘法指令碼,然後覆蓋內存中的原始指令即可。

所以在這個過程中發現,會涉及到很多數據結構體,不過好在這些結構體信息都定義在DexFile.h和DexClass.h這兩個頭文件中,他們存放在[Android源碼/dalvik/libdex/]目錄下。而這些結構體信息也是相互包含的,下面就來整理一下:


本文涉及到的代碼工具可以去小密圈自取,為了安全考慮代碼不進行公開,希望大家利用本文技術作為研發目的,絕不可以用於非法目的,否則後果自負與本文作者無任何關係!點擊立即進入小密圈

當然本文介紹完了之後,其實有一個很大的用途就是hook虛擬機的一些函數來做一些事情,其實說到這裡本文並不是本次研究的重點,重點是下一篇文章,只是為了需要hook系統的函數,本文就先做個鋪墊而已。聰明的人應該知道我下一篇文章要介紹啥了,而下一篇文章才是重點。篇幅原因,不得不將其內容拆分了。看文本文之後,一定要記得一點,不僅在java層可以免root進行hook操作,在native也是可以的。這個知識點對未來安全防護,逆向分析,應用開發都非常重要。

手機查看文章不方便,可以網頁看

http://www.wjdiankong.cn


《Android應用安全防護和逆向分析》

點擊查看圖書詳情


長按下面👇👇二維碼,關注編碼美麗


天若有情天亦老,我為逆向續一秒!,猛戳下方"閱讀原文",購買安全逆向圖書!

相關焦點

  • Hook Android C代碼(Cydia Substrate)
    參考學習的英文博文https://koz.io/android-substrate-c-hooking/創建一個目標apk編寫目標項目,用於本次實操過程的hook對象1.創建項目android create project--target android-23 --path targetapp --package
  • Android上玩玩Hook:Cydia Substrate實戰
    我們知道,在Android作業系統中系統維護著自己的一套事件分發機制。應用程式,包括應用觸發事件和後臺邏輯處理,也是根據事件流程一步步的向下執行。而「鉤子」的意思,就是在事件傳送到終點前截獲並監控事件的傳輸,像個鉤子勾上事件一樣。並且能夠在勾上事件時,處理一些自己特定的事件。如下圖所示:
  • 免root,免刷機,甚至免安裝 xposed 框架,也能用 xposed 框架及插件...
    大家都知道,用 xposed 框架必須要 root,但現在 root 成功率越來越低,大大提高了使用門檻。看似無解的問題,其實可以通過沙箱技術解決。  市面上典型利用沙箱技術的應用是雙開類應用,今天介紹的這塊應用,不僅穩定支持雙能開,更支持運行 xposed 框架+插件,真正做到免 root,甚至不用安裝 xposed 框架,就能玩轉各種插件了。
  • 【Android 原創】實戰分析一個Crackme的過程(超級詳細)
    _server /data/local/tmp/進入手機端命令:adb shell切換獲取手機的root權限:su查找push的文件是否在手機中:cd /data/local/tmp/查看路徑下的文件以及權限:ls -l擁有root權限更改文件的權限為777:chmod 777 android_x86_server在手機中啟動運行該文件
  • xposed 入門之修改手機 IMEI
    背景xposed 對於很多安卓安全和逆向人員來說並不陌生,在對 app 的函數功能分析和攔截時經常會用到。本文以 xposed 修改某個應用的 IMEI 為例,作為一個簡單入門介紹。供新手了解如何使用 xposed 編寫自己所需的 hook 模塊,以及簡單介紹 xposed 在其他方面的應用場景。2.
  • HOOK技術的分析
    Windows HookWindows的話呢主要分為用戶層hook和驅動層hookAPI鉤子其實準確的說不是通過SetWindowsHookEx這個函數進行設置鉤子,因為這個函數似乎實現不了接管函數的效果,那麼該如何才能做到接管其他程序的函數是一個比較關鍵的一點。
  • android平臺arm指令學習和調試
    我試出來的方法,對so文件進行修改,讓其在入口處直接調用它本身。需要注意的是,必須在so的入口函數處修改,如果在內部調用函數修改,app會自動退出。將該地址修改為調用自身,然後gdb附加,成功斷下來,然後輸入「ir$lr」查看返回值,如下該地址恰巧是在linker模塊中,此處的在linker中的代碼如下:7)至於為什麼系統的linker會調用.init_proc,不得而知。該so的oep為空,沒有section表,但是有dynamicsection表。
  • 詳解Android 6.0運行時權限
    那麼今天不給大家帶來什麼高端複雜的技術,而是一篇簡單易懂的6.0運行時權限講解。我知道目前國內大多數的項目都還沒有去兼容6.0系統,但現在6.0的設備已經慢慢多起來了,足以引起我們的重視,希望這篇文章可以給大家帶來幫助,提前祝大家周末愉快。本篇文章由 yuyue 投稿,在此表示感謝。
  • 想讓Android手機開掛,安裝Xposed框架就行了!
    Xposed(也被稱作Xposed框架、XP框架、Xposed framework),是運行於Android作業系統的一個著名的免費開源Hook框架。其通過替換Android系統的關鍵文件,可以攔截幾乎所有Java函數的調用,並允許通過模塊擴展方式來實現各種功能,模塊中的自定義代碼可以更改調用函數時的行為,常被用來修改Android系統和應用程式的功能。
  • 【Android 原創】APK加固之類抽取分析與修復
    修改dex結構:抽取DexCode中的字節碼指令後用零去填充,或者修改方法屬性等操作,運行時在內存中做修正、修復等處理工作。通過動態分析libedog.so中的dl函數主要功能是: 獲得系統版本號->驗證加固前後的籤名是否一致->反調試->將抽走的指令映射到內存中還原指令時用到->HOOK函數dvmResolveClass->結束。
  • 【Android 原創】從OWASP CrackMe學Android逆向(二)
    能感覺出來這個應用和之前level1不同的是, check輸入的關鍵代碼應該是在libfoo.so中, 所以不能使用Xposed去處理這個CrackMe了, 但是root和debug的檢測還是Java層調用System.exit(0)退出。所以上次的cracker.js, 重載系統exit函數部分保留。
  • Android Hook神器:XPosed入門與登陸劫持演示
    Xposed Xposed框架是一款可以在不修改APK的情況下影響程序運行(修改系統)的框架服務,通過替換/system/bin/app_process程序控制zygote進程,使得app_process在啟動過程中會加載XposedBridge.jar這個jar包,從而完成對Zygote進程及其創建的Dalvik虛擬機的劫持。
  • 電視盒和智能電視如何通過ADB獲取ROOT權限?
    如何通過adb獲取root權限(安卓電視盒和智能電視通用)
  • HOOK工具原理系列之Xposed
    zygote進程複製新的App進程在生成後,就會加載本App的程序代碼(apk中的dex文件)Xposed介紹Xposed是安卓系統上能夠修改系統或三方應用信息的框架。(1)hook 住系統資源相關的方法;(2)hook 住zygote 的相關方法;(3)加載系統中已經安裝的xposed 模塊。
  • Android權限機制與適配經驗
    Android6.0以前,Android的權限機制比較簡單,開發者在AndroidManifest文件中聲明需要的權限,APP安裝時,系統提示用戶APP將獲取的權限,需要用戶同意授權才能繼續安裝,從此APP便永久的獲得了授權。
  • Android逆向之旅--「最右」籤名算法解析(ARM指令學習噁心篇)
    我們先用Frida去hook這個函數,把加密的信息列印出來然後在分析arm指令,最後用代碼實現這個算法來看看結果是否正確:,然後在加上需要hook的未導出的函數相對地址就是這個函數的絕對地址了:這裡看到JNI_OnLoad是一個導出函數,然後減去他的相對地址就是so在內存中的基地址了,最後加上我們需要hook的函數地址即可。
  • 免安裝xposed 也能用xposed框架及插件
    免root免刷機,甚至不用安裝xposed模塊,直接在分身大師內部運行xposed插件!2. 首批支持的功能有:自動搶紅包,本地修改餘額,偽裝地理位置,偽裝設備信息等;筆者親測了幾個功能,本地修改餘額和自動搶紅包都實現了,免去了root的煩惱,確實方便太多。
  • 【Linux】Linux系統中的權限詳解
    w:具有修改目錄結構的權限,比如新建文件和目錄,刪除此目錄下文件和目錄,重命名此目錄下文件和目錄,剪切和複製(比如命令cp mv touch rm)。        x:目錄有執行權限但是不能運行,可以進入目錄(cd命令)。
  • 使用 mtrace 分析 「內存洩露」
    1 內存洩漏導論在工作中,特別是採用 C 語言編寫程序時,動態內存分配是常有的事,而伴隨動態內存分配而來的最大的問題就是所謂 「內存洩漏」。mtrace 工具的主要思路是在我們的調用內存分配和釋放的函數中裝載 「鉤子(hook)」 函數,通過 「鉤子(hook)」 函數列印的日誌來幫助我們分析對內存的使用是否存在問題。