關於燒餅游戲修改器的分析


一、前言

  燒餅游戲修改器是一款元老級的游戲修改器,提供了精確搜索、模糊搜索、聯合搜索、數據過濾、存儲搜索與讀取搜索等功能。主要實現搜索手機進程的內存數據並做相應修改。

  本文中分析的版本為2.0.2(34),SDK:8,TargetSDK:17,代碼經過混淆,修改器基本使用方法為輸入需要修改的數據進行查詢,查詢結果少則可直接修改,查詢結果多則需要返回游戲,使想修改的數據發生變化后再次搜索,最后根據數據變化偏移找到需要修改的地址。輸入想要修改成的數據即可修改。

  本程序測試機為LG Nexus 5,版本為Android 4.4,運行程序前需先root手機。

  系統環境:Java 1.8.0_92 ,Android Debug Bridge version 1.0.36

  使用工具:AndroidKiller、JEB、Eclipse、IDA

二、詳細分析

2.1 關於smali注入代碼的說明

  為了便於分析,本文中涉及向工程中注入smali代碼並重新打包運行,對常用的注入代碼做如下說明。其中使用到的寄存器vn要根據實際代碼使用的寄存器個數來做相應修改。如圖2-1中的run()中“.locals 5”申明使用了5個寄存器,即v0~v4,如果我們需要插入一條日志信息,為了不影響原來代碼的執行,需要自己增加一個寄存器v5,先將“.locals 5”變成“.locals 6”,再用v5來保存日志信息。

 

圖2-1 Smali代碼中對寄存器的說明

2.1.1 log信息輸出


 

const-string vn, "提示語"

invoke-static {vn}, Lcom/android/killer/Log;->LogStr(Ljava/lang/String;)V


 

圖2-2 Log信息輸出

2.1.2 打印字符型數據


 

const-string vn, "string數據"

invoke-static {vn, v1}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I


圖2-3 打印字符數據

2.1.3 打印整型數據


 

const-string vn, "int數據"

invoke-static {v1}, Ljava/lang/Integer;->toString(I)Ljava/lang/String;

move-result-object v(n+1)

invoke-static {vn, v(n+1)},Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I


圖2-4 打印整型數據

2.1.4 打印boolean型數據


 

const-string vx, "boolean數據"

invoke-static {p1}, Ljava/lang/Boolean;->toString(Z)Ljava/lang/String;

move-result-object v(x+1)

invoke-static {vx, v(x+1)}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I


圖2-5 打印boolean型數據

2.1.5 棧追蹤


new-instance vn, Ljava/lang/Exception;

const-string v(n+1),"這里是 trace"

invoke-direct{vn,v(n+1)},Ljava/lang/Exception;-><init>(Ljava/lang/String;)V

invoke-virtual{v(n+1)},Ljava/lang/Exception;->printStackTrace()V

 


圖2-6 棧追蹤

2.1.6 toast輸出


const-string v0, "這里是Toast輸出"

const/4 v1, 0x1

invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object v0

invoke-virtual {v0}, Landroid/widget/Toast;->show()V


圖2-7 Toast輸出

2.1.7 加載so庫


const-string v0, "這里是Toast輸出"

const/4 v1, 0x1

invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

move-result-object v0

invoke-virtual {v0}, Landroid/widget/Toast;->show()V


圖2-8 加載so庫

2.2 Java層代碼分析

2.2.1 加載的so文件

  用AndroidKiller打開該apk,得到反編譯之后的工程,如圖2-9。

 

圖2-9 反編譯之后的apk工程目錄

 

  查看AndroidMainfest.xml,如圖2-10,可以看到應用程序包名為“org.sbtools.gamehack”,程序主活動為“MainActivity”,還可以看到服務名為“org.sbtools.gamehack.sesrvice.FlowServ”。

 

圖2-10 AndroidMainfest.xml

 

  找到MainActivity.smali,其中onCreate方法如圖2-11,在做一些布局工作后調用了a(),a()方法如圖2-12,其主要工作為啟用Instrumentation,通過Instrumentation框架,可以在主程序啟動之前,創建模擬的系統對象,如Context;控制應用程序的多個生命周期;發送UI事件給應用程序;在執行期間檢查程序狀態等。從AndroidMainfest.xml中可以知道Instrumentation對應的方法為org.sbtools.gamehack.GameInstrumention。在Instrumentation中啟用了服務,如圖2-13。

圖2-11 MainActivity.onCreate()

 

圖2-12 MainActivity.a()

  圖2-13 啟動服務
 

  在FlowServ中,發現該方法加載了Libencode.so,而在該方法中我們可以找到decode被聲明為native(圖2-14),想必能用到這個decode的地方一定是關鍵之處,於是在工程里搜索decode,發現在/d/i.smali中用到了decode,而decode上面出現了兩個可疑文件(圖2-15)。在工程中注入代碼打印出string v1,打開eclipse, 在DDMS中打印出如圖2-16的信息,可知將路徑“/data/data/org.sbtools.gamehack/files/hack”和“/data/data/

org.sbtools.gamehack/files/gamehack”作為參數傳給了decode(),隨后在該類的a方法中運行了hack文件(圖2-17),運行之后將該文件刪除(圖2-15)。

圖2-14 Native程序加載

 

圖2-15 調用decode的地方

 

圖2-16 獲取的path

 

圖2-17 給hack文件賦予root權限

  這里的gamehack文件筆者未找到,IDA調試程序安裝時顯示Libdecode.so加載失敗,因此按邏輯圖2-7中的decode()執行失敗,后面的hack文件不能執行,但以附加方式調試代碼時候hack文件確實已經執行了,觀察smali代碼反應JEB翻譯的java邏輯沒有問題,猜測程序采用了一些反調試的手段,具體原因還待考究。

2.2.2 Socket消息傳送

  打開該程序,搜索界面和搜索結果界面如圖2-18、圖2-19,我們有多種方法來了解執行搜索時候java層的執行邏輯,比如在AndroidKiller中搜索字符串“搜索”,找到其ID,逐層分析其流程,或從搜索結果入手,搜索找到“共搜索到%ld個數值,請選擇修改”出現的地方,然后利用再smali代碼中添加棧回溯語句來查看棧調用順序。

圖2-18 搜索界面

 

圖2-19 搜索結果頁面

 

  最后我們在ah類中找到程序對搜索消息的的設置(圖2-20),最后發送的數據為“s [要搜索的數據] 標志位 0”,其中s表示正在進行搜索操作,當輸入的搜索數據合法時,標志位為1。我們在這個方法中注入代碼打印出發送的消息,發送的消息如圖2-21所示。

 

圖2-20 設置搜索消息

 

圖2-21 發送的消息

 

  那么現在問題來了,這些消息發送給了誰?又是誰來接收這些搜索指令呢?分析到這里,之前提到的hack文件的功能就可想而知了。

2.3 Hack文件分析

2.3.1 獲取hack文件

  那么這個hack文件具體做了些什么呢?為了進一步分析我們需要先提取該hack文件。打開adb shell,進入該文件所在目錄,但是ls命令並未查看到,文件顯然已經被刪掉了。(圖2-22)

 

圖2-22 adb shell未查找到文件

 

  此時我們有兩種方法來找到該hack文件:

  方法一:找到刪除文件的smali代碼,注釋掉刪除文件的代碼,重新打包運行。之后用adb pull /data/data/org.sbtools.gamehack/files/hack D://命令將文件拷貝到本地計算機。注釋掉的代碼如圖2-23、圖2-24所示。

 

圖2-23 注釋掉的代碼1

 

圖2-24 注釋掉的代碼2

 

  方法二:linux系統中每一個進程都有一個/proc/[pid]目錄,該目錄下保存着有關該進程的各類信息,其中有個文件名為exe,這個文件是一個鏈接指向該進程的實際可執行文件。用命令ps |grep gamehack找到該進程ID為18912(圖2-25),執行cd /proc/18912 進入到該進程信息目錄,用ls –al會看到我們要找的文件。linux系統刪除文件是使用了unlink這個函數,這個函數只會減少某個文件的硬連接數,當某個文件被打開后,並調用unlink刪除該文件時,使用ls命令是不能查看到該文件了的,但這個文件實際上還是存在在磁盤上的,當使用該文件的進程結束時或者調用close關閉該文件描述符時,這個文件才真正的從磁盤上消失。因此,只要程序還在運行,該hack文件就不會被真正刪除,將該文件拷貝到sdcard,再pull到計算機上(圖2-26)。

 

圖2-25 查看進程ID

 

圖2-26 進入進程目錄並提取文件

 

  接下來就可以對該文件進行分析了。

2.3.2 hack文件分析

  IDA加載提取出來的hack文件,查看導出表,只有一個start(),雙擊進入start(),F5之后如圖2-27所示。

 

圖2-27 start()啟動函數

 

  在我重命名為“_HackWhat”的函數中,實現了該程序中最核心的功能。首先建立起socket連接,接收來自遠端的數據,並根據第一個數據來決定后續要執行的操作(圖2-28)。前面我們已經得出,當執行數據搜索的時候用字母“s”來表示,直接分析case s的代碼(圖2-29),可以發現在_DataMemory()中對接收到的數據做了一些處理,提取出需要搜索的數值,接着在_SearchData中讀取進程內存信息進行搜索。

 

圖2-28 接收socket數據

 

圖2-29 搜索數據

 

  靜態分析搜索部分的代碼,可以看到程序首先用ptrace來attatch進程(圖2-30),接着打開了進程內存映射信息所在的文件“/proc/[PID]/maps”並讀取相關信息進行保存(圖2-30)。

 

圖2-30 Ptrace進程

 

圖2-31 讀取內存映射信息

 

  接下來程序讀取進程內存信息“/proc/[PID]/mem”並保存。此處用於監測程序的內存映射信息是否發生變化。當在執行搜索操作時,程序實際上執行的為圖所示的_SearchData函數,該函數在IDA中的偏移地址為0000A900。

 

圖2-32 讀取進程內存

 

圖2-33 執行搜索

 

  在該函數中,同樣先ptrace進程,讀取內存信息並和之前保存的做一個比較,校驗內存是否發生變化,接着從內存信息中搜索我們要修改的數據(圖2-35、圖2-36),而此處的具體邏輯還待深究,此處未作進一步描述。

 

圖2-34 讀取內存信息

 

圖2-35 搜索數據a

 

圖2-36 搜索數據b

 

  搜索完數據之后,執行圖2-29中的_SendFindResult將搜索結果發送到遠端,交給java層代碼做接下來的處理。

  如果搜索結果太多,需要我們返回游戲,手動使想要修改的數據發生變化,然后重新搜索(圖2-37)。此時發送的消息為“f = [Data]” (圖2-38)。Hack程序收到socket信息后會提取變化之后的數字,再次執行圖2-33中的_SearchData函數,不過此時會讀取剛才搜索數字8之后的內存,檢測哪一個內存保存的數值變為了12,據此來鎖定想要修改的數據所在的內存。接着執行修改操作。

 

圖2-37 變化數據

 

圖2-38 第二次搜索時向socket發送的數據

 

  當點擊修改之后,hack進程識別標志修改的字母“m”並執行如圖2-39所示的操作流程。在對收到搜索命令做一系列提取等操作之后,ptrace注入進程並對相應內存地址的數據進行修改(圖2-40),最后將修改結果發送給java層進行處理。

 

圖2-39 修改數據a

圖2-40 修改數據b

 

2.3.3 用IDA進行動態分析

  以上分析結合了靜態分析和動態分析,用IDA對進程進行動態分析的步驟如下:

  1)    把IDA\dbgsrv目錄下的android_server push到android中,命令:

    adb push  android_server /data/data/sv

  2)    adb shell進入該目錄,為android_server賦755權限並以管理員權限運行。

cd /data/data/sv

chmod 755 android_server

su

./android_server

    成功運行之后可以看到監聽端口為23946。.

  3)    在windows控制台下轉發window到模擬器或手機端口。

    adb forward tcp:23946 tcp:23946

  4)    在IDA中選擇android調試,在Debuggger 中的process options 的hostname 填上localhost。

  5)    在Debugger中的attach上選擇對應的android程序即可。

  這里由於hack文件運行之后被刪除了  因此需要注釋掉刪除文件的代碼,重新編譯運行之后才能attach上該進程。附加上該進程之后在IDA右側的Modules窗格可以看到該文件基址為00008000,從剛才IDA加載的我們導出的hack文件中可以看到start偏移地址為00009010。兩個地址相加即為該代碼在elf文件中的實際地址,即00011010,我們在IDA中找到這個地址並下斷。接着運行程序點擊搜索,可以發現程序執行了如圖所示的流程,在寄存器窗格中可以查看到存放接收數據的地址,Hex窗格中可以看到該地址下存放的數據。(圖2-41)

 

圖2-41 IDA動態加載hack程序——數據處理

 

  調試到圖2-42所示的過程中發現程序在讀取內存信息。

 

圖2-42 讀取內存

  搜索完數據之后對搜索結果進行處理,將處理之后的數據發送給遠端(圖2-43)。發送的信息如圖2-44,其格式為“[內存地址]=[查找的數據]=[數據類型]”。

 

圖2-43 處理搜索結果

 

圖2-44 發送的數據

三、總結

  本文中分析的燒餅游戲修改器是一款經典的游戲修改器,之后時常出現的很多修改器和外掛都在其基礎上衍生,對該游戲修改器進行分析能幫助學者素數掌握修改器和外掛的思路。通過這段時間的學習,收獲如下:

  1)    掌握了逆向分析的大致流程和相關工具的使用;

  2)    對java語言有進一步了解;

  3)    熟悉了android框架及一些基礎控件的使用;

  4)    熟悉了smali代碼語法,能讀懂smali代碼,通過注入samli代碼來調試程序;

  5)    掌握了IDA動態調試進程的方法;

  6)    對Native程序有了初步的認識。

  該apk是筆者分析的第一個android程序,由於沒有相關分析經驗,以及對工具和相關編程語言不熟悉,導致前期花費了大量時間在android編程學習和java層代碼的分析上,沒有抓住程序的關鍵點。針對本文中的游戲修改器的分析,尚有許多待完善之處:

  1)    程序讀取完內存之后執行搜索操作的時候用的什么方法或函數來搜索尚不確定;

  2)    本文只對綜合搜索進行了分析,對於修改器中提供的其他功能,如模糊搜索、深度搜索、聯合搜索、反加密等還未做進一步分析。

  3)    對JNI還需深入學習。

  初次分析,難免有錯誤、不當和疏漏的地方,如有發現,還請指正,謝謝!

 

@Vivi

@qq:3320163319

@email:viwilla@outlook.com

四、參考

《Android 軟件安全與逆向分析》

《游戲安全》

Android killer中的一些使用技巧: http://www.pd521.com/thread-509-1-1.html

Android killer方便搜蘇哦資源ID的一個插件http://www.pd521.com/thread-472-1-1.html

關於ptrace:http://www.cnblogs.com/catch/p/3476280.html

xx助手之天天酷跑外掛詳細分析:

http://bbs.pediy.com/showthread.php?t=187948

ARM指令STMFD和LDMFD:

http://blog.csdn.net/kickxxx/article/details/9192265

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM