Andriod 破解之道(一)
# 前言
在Root前提下,我們可以使用Hooker方式綁定so庫,通過逆向方式篡改數值,從而達到所謂破解目的。然而,目前無論是軟件加固方式,或是數據處理能力后臺化,還是客戶端數據真實性驗證,都有了一定積累和發展,讓此“懶技術”不再是破解修改的萬金油。再者,閱讀匯編指令,函數指針替換,壓棧出棧等技術需要一定技術沉淀,不利于開發同學上手。
兩年前,也是因為懶,很懶,非常懶,堆積了足夠的動力,寫了一個基于人工模擬方式,對一個特定規則的游戲進行暴力破解。我們都知道,人工模擬方式,繞過了大量防破解技術,只要還是人機交互模式,并且滿足一定的游戲規則,基本是無法防御的。
以下是一段技術應用視頻,以便大家對文章有個初步認識:
大家能夠看到,這段一分鐘的視頻,在50秒的時候就刷爆了游戲分數上限,足見此技術能力并不亞于傳統的Hooker篡改內存地址方式。
技術實現原理
因涉及到安全方面的考量,本文主要圍繞技術實現原理和關鍵技術點進行闡述。
技術要求:
實現方式分三步:
1.劫持屏幕
先說說劫持屏幕,做過截屏功能的同學應該清楚,Root了之后能訪問設備“dev/graphic”文件夾,里面有fb0, fb1, fb2三個screen buffer文件。這里用到的是fb0文件。
拋出一個問題,當前主流屏幕分辨率都在1920*1080區間,一張圖片的緩存能去到2M左右,要達到30fps的性能指標,光是屏幕數據的讀寫耗時,就滿足不了要求。怎么做呢?
一般在做圖像處理的時候都會想到parallel programming。然而,這里的圖片是時間相關的,不適宜采用多線程任務派發。
懶人一番思量后,發現一條捷徑,共享內存讀取,請看以下代碼。
mapbase = mmap(0, **mapsize, PROT_READ, MAP_SHARED, fd, offset);
這行代碼廣泛存在于各個截屏代碼片段中,精髓在于PROT_READ 和 MAP_SHARED上。先科普一下mmap參數中這兩個參數吧。
prot : 映射區域的保護方式。可以為以下幾種方式的組合:
- PROT_EXEC 映射區域可被執行
- PROT_READ 映射區域可被讀取
- PROT_WRITE 映射區域可被寫入
- PROT_NONE 映射區域不能存取
flags : 影響映射區域的各種特性。在調用mmap()時必須要指定MAP_SHARED 或MAP_PRIVATE。
- MAP_FIXED 如果參數start所指的地址無法成功建立映射時,則放棄映射,不對地址做修正。通常不鼓勵用此旗標。
- MAP_SHARED 對映射區域的寫入數據會復制回文件內,而且允許其他映射該文件的進程共享。
- MAP_PRIVATE 對映射區域的寫入操作會產生一個映射文件的復制,即私人的“寫入時復制”(copy on write)對此區域作的任何修改都不會寫回原來的文件內容。
- MAP_ANONYMOUS建立匿名映射。此時會忽略參數fd,不涉及文件,而且映射區域無法和其他進程共享。
- MAP_DENYWRITE只允許對映射區域的寫入操作,其他對文件直接寫入的操作將會被拒絕。
- MAP_LOCKED 將映射區域鎖定住,這表示該區域不會被置換(swap)。
因為我們不需要寫屏,所以prot只需要采用PORT_READ;而我們期望避免屏幕數據的多次創建,flags就需要用到MAP_SHARED,這樣文件句柄fd指向的內存塊數據就會實時變更,無需多次創建,拷貝,釋放數據。
2.分析數據
截取到屏幕數據就好辦了,對每一幀進行數據處理,這里完全就是算法問題了。懶人都用搓算法,大概的思路就是:7*7宮格,對于所有相連的兩個同色item做了橫向映射表和縱向映射表,然后輪尋處理5連,4連和3連。里面還有一些涉及到實現細節的映射表重置與預判,因為不是本文重點,就帶過了。
void Handle_X_Combination() {LOGE("Handle_X_Combination");gen_Horizontal_Matrix(6);get_Horizontal_X_Match();gen_Vertical_Matrix(0, 6);get_Vertical_X_Match(); }
下面是程序運行時的Log信息片段,以供大家參考。
3. 模擬輸出
算法會輸出當前屏幕的一個模擬手勢操作隊列,最精彩的當然放到最后,也是此工程的技術點,怎么模擬輸出手勢的問題。
Android所給予的截屏和模擬操作分別為 adb screenshot 和 adb shell sendevent (根據android版本,有些機型用的是input event,記得沒錯的話~)
所有需要adb處理的指令,都不能采用高并發方式調用,要不然要么機器重啟,要么指令堵塞。所以adb這條路不通。
怎么辦呢?
懶人又一番思量后,linux系統大都采用文件buffer,直接將指令寫文件吧。其實adb也是寫文件,不過adb做了一層轉譯,這里涉及到設備層指令代碼,不同機型定義的指令代碼不盡相同。
要完成此任務,首先要弄清楚幾件事情:
萬能的adb給了我一些思路,adb shell getevent,會打印出當前event的指令。再科普一下,event有很多,包括compass_sensor,light_sensor,pressure_sensor,accelerometer_sensor等等。
我們這里監聽的是,touchscreen_sensor。
有了上面的指導信息,要構建一個模擬操作函數就很容易了。操作屏幕打印出想要的模擬的手勢,然后寫下來就好了。一共會有這么幾個模擬操作函數需要創建:
void simulate_long_press_start_event(int touch, int fromX, int fromY);
void simulate_long_press_hold_event(int touch, int fromX, int fromY);
void simulate_long_press_end_event(int touch);
void simulate_press_event(int touch, int fromX, int fromY);
void simulate_move_event(int touch, int fromX, int fromY, int toX, int toY);
下面給出一個我寫好的范例出來,大家可以依葫蘆畫瓢,把剩下的寫好。
建議大家在動手寫模擬輸入模塊之前,先仔細研讀Android input事件機制,對于個人程序修為大有裨益。有時間我會單獨寫一個技術文檔供大家參考,目前就此帶過了。
調試
因為我是個懶人,能偷懶的地方都會花時間深研。以下,是個人針對本項目調試的一些懶技巧,以供各位參考。
下面是在Debug模式下生成的8張連續圖片。能看到每個小宮格的右上方都打印出了當前識別的顏色。如果當前宮格需要被移動,則采用雙色繪制表明移動的方向,上下雙色表示需要上下移動,左右雙色表示需要左右移動。
此外,調測程序的時候必不可少的就是單步回歸了,以下是設計的Dummy模式,以驗證Bug修復效果。
至此,主要關鍵技術已經簡述完畢,謝謝大家。
# 后記
大家如果仔細看了這篇文章,會發現視頻中的游戲版本,和截屏圖片的版本是不一致的。視頻是我在13年的時候錄的,截屏是因為KPI考核要求寫文章,臨時又生成一遍的,所以會有版本差異。做這個技術的初衷就是因為懶,才想到虐爆Android的。從開始到第一個demo出來,大概花了一周的時間,因為思路都比較清晰,后續的優化反而花了一個多月,包括防破解這塊(總不能出個破解然后被人爆菊吧,太侮辱智商了)還是需要仔細走讀一下底層實現。其中也請教了當時公司安全部的哥們,知道了更多關于軟件實現機制,也深知安全的重要性,所以這段代碼一直只存留在我的代碼實驗室,以前不會,現在不會,以后也不會開源發布,所以請各位海涵了。有興趣的同學可以通過自己的努力實現一遍,對個人技術的提高會有很大幫助。
這篇技術文檔的確只覆蓋了一些關鍵技術節點,還有較多和當前程序不相關的技術并沒有被涵蓋,例如底層加固技術,動態底層Binary Dex加載技術(在Art下需要有一定的修改,懶,沒有去深挖了),so庫混淆,屏幕同步,模擬輸入同步等,往后有時間再行一一簡述吧。
原文地址:?https://yq.aliyun.com/articles/3032?spm=5176.100239.blogcont3033.31.a7Z6Y1
總結
以上是生活随笔為你收集整理的Andriod 破解之道(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android6.0 Log的工作机制
- 下一篇: Android 破解之道 (二)