安卓動態調試七種武器之離別鉤 – Hooking(上)

来源:http://www.cnblogs.com/alisecurity/archive/2016/06/16/5590940.html
-Advertisement-
Play Games

隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。因此,筆者將會在這一系列文章中分享一些自己經常用或原創的調試工具以及手段,希望能對國內移動安全的研究起到一些催化劑的作用。 ...


安卓動態調試七種武器之離別鉤 – Hooking(上)

作者:蒸米@阿裡聚安全

 

0x00 序

隨著移動安全越來越火,各種調試工具也都層出不窮,但因為環境和需求的不同,並沒有工具是萬能的。另外工具是死的,人是活的,如果能搞懂工具的原理再結合上自身的經驗,你也可以創造出屬於自己的調試武器。因此,筆者將會在這一系列文章中分享一些自己經常用或原創的調試工具以及手段,希望能對國內移動安全的研究起到一些催化劑的作用。

 

目錄如下:

安卓動態調試七種武器之長生劍 - Smali Instrumentation 

安卓動態調試七種武器之孔雀翎 – Ida Pro 

安卓動態調試七種武器之離別鉤 – Hooking (上) 

安卓動態調試七種武器之離別鉤 – Hooking (下) 

安卓動態調試七種武器之碧玉刀- Customized DVM 

安卓動態調試七種武器之多情環- Customized Kernel 

安卓動態調試七種武器之霸王槍 - Anti Anti-debugging 

安卓動態調試七種武器之拳頭 - Tricks & Summary

 

文章中所有提到的代碼和工具都可以在我的github下載到,地址是:https://github.com/zhengmin1989/TheSevenWeapons

 

0x01 離別鉤

Hooking翻譯成中文就是鉤子的意思,所以正好配合這一章的名字《離別鉤》。

“我知道鉤是種武器,在十八般兵器中名列第七,離別鉤呢?” 

“離別鉤也是種武器,也是鉤。”

 “既然是鉤,為什麼要叫做離別?”

 “因為這柄鉤,無論鉤住什麼都會造成離別。如果它鉤住你的手,你的手就要和腕離別;如果它鉤住你的腳,你的腳就要和腿離別。” 

“如果它鉤住我的咽喉,我就和這個世界離別了?” 

“是的,”

 “你為什麼要用如此殘酷的武器?”

 “因為我不願被人強迫與我所愛的人離別。”

 “我明白你的意思了。”

 “你真的明白?”

 “你用離別鉤,只不過為了要相聚。”

 “是的。”

 

一提到hooking,讓我又回想起了2011年的時候。當時android才出來沒多久,各大安全公司都在忙著研發自己的手機助手。當時手機上最泛濫的病毒就是簡訊扣費類的病毒,但僅僅是靠雲端的病毒庫掃描是遠遠不夠的。而這時候”LBE安全大師”橫空出世,提供了主動防禦的技術,可以在病毒發送簡訊之前攔截下來,並讓用戶選擇是否發送。 其實這個主動防禦技術就是hooking。雖然在PC上hooking的技術已經很成熟了,但是在android上的資料卻非常稀少,只有少數人掌握著android上hooking的技術,因此這些人也變成了各大公司爭相搶奪的對象。

 

但是沒有什麼東西是能夠永久保密的,這些技術早晚會被大家研究出來並對外公開的。因此,到了2015年,android上的hook資料已經遍地都是了,各種開源的hook框架也層出不窮,使用這些hook工具就可以輕鬆的hook native,jni和java層的函數。但這同樣也帶來了一些問題,新手想研究hook的時候因為資料和工具太多往往不知道如何下手,並且就算使用了工具成功的hook,也根本不知道原理是什麼。因此筆者準備從hook的原理開始,配合開源工具循序漸進的介紹native,jni和java層的hook,方便大家對hook進行系統的學習。

 

0x02 Playing with Ptrace on Android

其實無論是hook還是調試都離不開ptrace這個system call,利用ptrace,我們可以跟蹤目標進程,並且在目標進程暫停的時候對目標進程的記憶體進行讀寫。在linux上有一篇經典的文章叫《Playing with Ptrace》,簡單介紹瞭如何玩轉ptrace。在這裡我們照貓畫虎,來試一下playing with Ptrace on Android

PS:這裡的一部分內容借鑒了harry大牛在百度Hi寫的一篇文章,可惜後來百度Hi關了,就失傳了。不過不用擔心,我這篇比他寫的還詳細。:)

 

首先來看我們要ptrace的目標程式,用來一直迴圈輸出一句話”Hello,LiBieGou!”:

 

想要編譯它非常簡單,首先建立一個Android.mk文件,然後填入如下內容,讓ndk將文件編譯為elf可執行文件:

 

接下來我們寫出hook1.c程式來hook target程式的system call,main函數如下:

 

首先要知道hook的目標的pid,這個用ps命令就能獲取到。然後我們使用ptrace(PTRACE_ATTACH, pid, NULL, NULL)這個函數對目標進程進行載入。載入成功後我們可以使用ptrace(PTRACE_SYSCALL, pid, NULL, NULL)這個函數來對目標程式下斷點,每當目標程式調用system call前的時候,就會暫停下載。然後我們就可以讀取寄存器的值來獲取system call的各項信息。然後我們再一次使用ptrace(PTRACE_SYSCALL, pid, NULL, NULL)這個函數就可以讓system call在調用完後再一次暫停下來,並獲取system call的返回值。

獲取system call編號的函數如下:

 

ARM架構上,所有的系統調用都是通過SWI來實現的。並且在ARM 架構中有兩個SWI指令,分別針對EABI和OABI:

[EABI] 機器碼:

1110 1111 0000 0000 -- SWI 0 

具體的調用號存放在寄存器r7中. 

 

[OABI] 機器碼:

1101 1111 vvvv vvvv -- SWI immed_8 

調用號進行轉換以後得到指令中的立即數。立即數=調用號 | 0x900000

 

既然需要相容兩種方式的調用,我們在代碼上就要分開處理。首先要獲取SWI指令判斷是EABI還是OABI,如果是EABI,可從r7中獲取調用號。如果是OABI,則從SWI指令中獲取立即數,反向計算出調用號。

我們接著看hook system call前的函數,和hook system call後的函數:

 

在獲取了system call的number以後,我們可以進一步獲取個個參數的值,比如說write這個system call。在arm上,如果形參個數少於或等於4,則形參由R0,R1,R2,R3四個寄存器進行傳遞。若形參個數大於4,大於4的部分必須通過堆棧進行傳遞。而執行完函數後,函數的返回值會保存在R0這個寄存器里。

下麵我們就來實際運行一下看看效果。我們先把target和hook1 push到 /data/local/tmp目錄下,再chmod 777一下。接著運行target。

 

我們隨後再開一個shell,然後ps獲取target的pid,然後使用hook1程式對target進行hook操作:

 

我們可以看到第一個SysCallNo是162,也就是sleep函數。第二個SysCallNo是4,也就是write函數,因為printf本質就是調用write這個系統調用來完成的。關於system call number對應的具體system call可以參考我在github上的reference文件夾中的systemcalllist.txt文件,裡面有對應的列表。我們的hook1程式還對write的參數做瞭解析,比如1表示stdout,0xadf020表示字元串的地址,19代表字元串的長度。而返回值19表示write成功寫入的長度,也就是字元串的長度。

 

整個過程用圖表達如下:

 

0x03 利用Ptrace動態修改記憶體

僅僅是用ptrace來獲取system call的參數和返回值還不能體現出ptrace的強大,下麵我們就來演示用ptrace讀寫記憶體。我們在hook1.c的基礎上繼續進行修改,在write被調用之前對要輸出string進行翻轉操作。

 

我們在hookSysCallBefore()函數中加入modifyString(pid, regs.ARM_r1, regs.ARM_r2)這個函數:

 

因為write的第二個參數是字元串的地址,第三個參數是字元串的長度,所以我們把R1和R2的值傳給modifyString()這個函數:

 

modifyString()首先獲取在記憶體中的字元串,然後進行翻轉操作,最後再把翻轉後的字元串寫入原來的地址。這些操作用到了getdata()和putdata()函數:

 

getdata()和putdata()分別使用PTRACE_PEEKDATA和PTRACE_POKEDATA對記憶體進行讀寫操作。因為ptrace的記憶體操作一次只能控制4個位元組,所以如果修改比較長的內容需要進行多次操作。

我們現在運行一下target,並且在運行中用hook2程式進行hook:

 

哈哈,是不是看到字元串都被翻轉了。如果我們退出hook2程式,字元串又會回到原來的樣子。

 

0x04 利用Ptrace動態執行sleep()函數

上一節中我們介紹瞭如何使用ptrace來修改記憶體,現在繼續介紹如何用ptrace來執行libc .so中的sleep()函數。主要邏輯都在inject()這個函數中:

 

首先我們用ptrace(PTRACE_GETREGS, pid, NULL, &old_regs)來保存一下寄存器的值,然後獲取sleep()函數在目標進程中的地址,接著利用ptrace執行sleep()函數,最後在執行完sleep()函數後再用ptrace(PTRACE_SETREGS, pid, NULL, &old_regs)恢復寄存器原來值。

 

下麵是獲取sleep()函數在目標進程中地址的代碼:

 

因為libc.so在記憶體中的地址是隨機的,所以我們需要先獲取目標進程的libc.so的載入地址,再獲取自己進程的libc.so的載入地址和sleep()在記憶體中的地址。然後我們就能計算出sleep()函數在目標進程中的地址了。要註意的是獲取目標進程和自己進程的libc.so的載入地址是通過解析/proc/[pid]/maps得到的。

 

接下來是執行sleep()函數的代碼:

 

首先是將參數賦值給R0-R3,如果參數大於四個的話,再使用putdata()將參數存放在棧上。然後我們將PC的值設置為函數地址。接著再根據是否是thumb指令設置ARM_cpsr寄存器的值。隨後我們使用ptrace_setregs()將目標進程寄存器的值進行修改。最後使用waitpid()等待函數被執行。

 

編譯完後,我們使用hook3對target程式進行hook:

 

正常的情況是target程式每秒輸出一句話,但是用hook3程式hook後,就會暫停10秒鐘的時間,因為我們利用ptrace運行了sleep(10)在目標程式中。

 

0x05 利用Ptrace動態載入so並執行自定義函數

僅僅是執行現有的libc函數是不能滿足我們的需求的,接下來我們繼續介紹如何動態的載入自定義so文件並且運行so文件中的函數。邏輯大概如下:

 

保存當前寄存器的狀態 -> 獲取目標程式的mmap, dlopen, dlsym, dlclose 地址 -> 調用mmap分配一段記憶體空間用來保存參數信息 –> 調用dlopen載入so文件 -> 調用dlsym找到目標函數地址 -> 使用ptrace_call執行目標函數 -> 調用 dlclose 卸載so文件 -> 恢復寄存器的狀態。

 

實現整個邏輯的函數 injectSo()的代碼如下:

mmap()可以用來將一個文件或者其它對象映射進記憶體,如果我們把flag設置為MAP_ANONYMOUS並且把參數fd設置為0的話就相當於直接映射一段內容為空的記憶體。mmap()的函數聲明和參數如下:

  • start:映射區的開始地址,設置為0時表示由系統決定映射區的起始地址。 length:映射區的長度。 
  • prot:期望的記憶體保護標誌,不能與文件的打開模式衝突。我們這裡設置為RWX。 
  • flags:指定映射對象的類型,映射選項和映射頁是否可以共用。我們這裡設置為:MAP_ANONYMOUS(匿名映射,映射區不與任何文件關聯),MAP_PRIVATE(建立一個寫入時拷貝的私有映射。記憶體區域的寫入不會影響到原文件)。 
  • fd:有效的文件描述詞。匿名映射設置為0。 
  • off_toffset:被映射對象內容的起點。設置為0。

 

在我們使用ptrace_call(pid, mmap_addr, parameters, 6, &regs)調用完mmap()函數之後,要記得使用ptrace(PTRACE_GETREGS, pid, NULL, &regs); 用來獲取保存返回值的regs.ARM_r0,這個返回值也就是映射的記憶體的起始地址。

 

mmap()映射的記憶體主要用來保存我們傳給其他函數的參數。比如接下來我們需要用dlopen()去載入”/data/local/tmp/libinject.so”這個文件,所以我們需要先用putdata()將”/data/local/tmp/libinject.so”這個字元串放置在mmap()所映射的記憶體中,然後就可以將這個映射的地址作為參數傳遞給dlopen()了。接下來的dlsym(),so中的目標函數,dlclose()都是相同調用的方式,這裡就不一一贅述了。

 

我們再來看一下被載入的so文件,裡面的內容為:

這裡我們不光使用printf()還使用了android debug的函數LOGD()用來輸出調試結果。所以在編譯時我們需要加上LOCAL_LDLIBS := -llog。

 

編譯完後,我們使用hook4對target程式進行hook:

可以看到無論是stdout還是logcat都成功的輸出了我們的調試信息。這意味著我們可以通過註入讓目標進程載入so文件並執行任意代碼了。

 

0x06 小節

現在我們已經可以做到hook system call以及動態的載入自定義so文件並且運行so文件中的函數了,但離執行以及hook java層的函數還有一定距離。因為篇幅原因,我們的hook之旅就先進行到這裡,敬請期待一下篇《離別鉤 - Hooking》

文章中所有提到的代碼和工具都可以在我的github下載到,地址是:https://github.com/zhengmin1989/TheSevenWeapons

 

0x07 參考資料

  • Playing with Ptrace http://www.linuxjournal.com/article/6100
  • System Call Tracing using ptrace
  • 古河的Libinject http://bbs.pediy.com/showthread.php?t=141355

 

作者:蒸米@阿裡聚安全,更多技術文章,請訪問阿裡聚安全博客


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 今天要跟大家談的是事件冒泡,這個事件呢,也是兩面性的,有時候給我們帶來bug,有時候優點也很明顯。我們就一起來看看它的真面目。 首先看看事件冒泡是什麼? 事件冒泡 :當一個元素接收到事件的時候 會把他接收到的事件傳給自己的父級,一直到window 。(註意這裡傳遞的僅僅是事件 並不傳遞所綁定的事件函 ...
  • 1.Class類的介紹 在ES6中新增了Class類的概念,讓語法看起來更像是面向對象編程,其實這可以說是一個語法糖,ES5可以做到Class絕大部分功能,但也有一些不同。在ES6以前,可以通過構造函數來模擬類的概念,如下所示 在引入了Class關鍵字後,可以這樣做,如下所示 如上所示,便是ES6中 ...
  • 彈出層在網頁開發中經常用到,以前都是百度一下複製粘貼,從來沒有仔細看過也沒有理解原理. 直到有一天沒有網路,,,然後突然就想到了.這麼簡單為什麼會複製粘貼這麼久.總結如下: 1、有個大的div背景設個透明度opacity:0.5;filter:alpha(opacity=57),來個absolute ...
  • 常用選擇符的介紹 通配選擇符指選定文檔對象模型(DOM)中的所有類型的單個對象。 *{css代碼規則},其中*表示所有類型,包含body,div,td等 類型選擇符指以文檔對象模型(DOM)作為選擇符,即選擇某個html標記為對象,設置樣式規則。 P{ } Div{ } 包含選擇符 對某個容器層的子 ...
  • 寫在前面 本文翻譯自 Android Studio Tips by Philippe Breault,一共收集了62個 Android Studio 使用小技巧和快捷鍵。 根據這些小技巧的使用場景,本文將這62個小技巧分為常用技巧(1 – 28)、編碼技巧(29 – 49)和調試技巧(50 – 62 ...
  • url支持26個英文字母、數字和少數幾個特殊字元,因此,對於url中包含非標準url的字元時,就需要對其進行編碼。iOS中提供了函數stringByAddingPercentEscapesUsingEncoding對中文和一些特殊字元(下麵已證實包含"%")進行編碼,但是stringByAdding ...
  • 有數值按此方式構成: 5=2^0+2^2; 40=2^3+2^5; 現在需要獲取到冪值0和2或者3和5。int main(int argc, char * argv[]) int main(int argc, char * argv[]) ...
  • 介紹: 在WWDC 2015會議上,蘋果官方公佈了iOS9。除開許多新的特性和增強功能,這次升級也給了開發者們一個機會讓他們的app里的內容能通過Spotlight 搜索功能被髮現和使用。在iOS9中可用的新APIs允許你去索引APP裡面的內容或者界面狀態,通過Spotlight來讓用戶使用。 這些 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...