最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Android逆向工程簡單分析

2019-05-08 13:50 作者:啵嘰玉桂狗勾  | 我要投稿

Android逆向工程


在Root前提下,我們可以使用Hooker方式綁定so庫,通過逆向方式篡改數(shù)值,從而達(dá)到所謂破解目的。然而,目前無論是軟件加固方式,或是數(shù)據(jù)處理能力后臺(tái)化,還是客戶端數(shù)據(jù)真實(shí)性驗(yàn)證,都有了一定積累和發(fā)展,讓此“懶技術(shù)”不再是破解修改的萬金油。再者,閱讀匯編指令,函數(shù)指針替換,壓棧出棧等技術(shù)需要一定技術(shù)沉淀,不利于開發(fā)同學(xué)上手。

兩年前,也是因?yàn)閼?,很懶,非常懶,堆積了足夠的動(dòng)力,寫了一個(gè)基于人工模擬方式,對(duì)一個(gè)特定規(guī)則的游戲進(jìn)行暴力破解。我們都知道,人工模擬方式,繞過了大量防破解技術(shù),只要還是人機(jī)交互模式,并且滿足一定的游戲規(guī)則,基本是無法防御的。


技術(shù)實(shí)現(xiàn)原理

因涉及到安全方面的考量,本文主要圍繞技術(shù)實(shí)現(xiàn)原理和關(guān)鍵技術(shù)點(diǎn)進(jìn)行闡述。

技術(shù)要求:

  1. 支持多分辨率

  2. 支持多點(diǎn)觸摸

  3. 支持輸入速率動(dòng)態(tài)變更

  4. 處理能力峰值需要達(dá)到30fps

實(shí)現(xiàn)方式分三步:

  1. 劫持屏幕

  2. 分析數(shù)據(jù)

  3. 模擬輸出


1.劫持屏幕

先說說劫持屏幕,做過截屏功能的同學(xué)應(yīng)該清楚,Root了之后能訪問設(shè)備“dev/graphic”文件夾,里面有fb0, fb1, fb2三個(gè)screen buffer文件。這里用到的是fb0文件。

拋出一個(gè)問題,當(dāng)前主流屏幕分辨率都在1920*1080區(qū)間,一張圖片的緩存能去到2M左右,要達(dá)到30fps的性能指標(biāo),光是屏幕數(shù)據(jù)的讀寫耗時(shí),就滿足不了要求。怎么做呢?

一般在做圖像處理的時(shí)候都會(huì)想到parallel programming。然而,這里的圖片是時(shí)間相關(guān)的,不適宜采用多線程任務(wù)派發(fā)。

懶人一番思量后,發(fā)現(xiàn)一條捷徑,共享內(nèi)存讀取,請(qǐng)看以下代碼。

mapbase = mmap(0, **mapsize, PROT_READ, MAP_SHARED, fd, offset);

這行代碼廣泛存在于各個(gè)截屏代碼片段中,精髓在于PROT_READ 和 MAP_SHARED上。先科普一下mmap參數(shù)中這兩個(gè)參數(shù)吧。

prot : 映射區(qū)域的保護(hù)方式??梢詾橐韵聨追N方式的組合:

  • PROT_EXEC 映射區(qū)域可被執(zhí)行

  • PROT_READ 映射區(qū)域可被讀取

  • PROT_WRITE 映射區(qū)域可被寫入

  • PROT_NONE 映射區(qū)域不能存取

flags : 影響映射區(qū)域的各種特性。在調(diào)用mmap()時(shí)必須要指定MAP_SHARED 或MAP_PRIVATE。

  • MAP_FIXED 如果參數(shù)start所指的地址無法成功建立映射時(shí),則放棄映射,不對(duì)地址做修正。通常不鼓勵(lì)用此旗標(biāo)。

  • MAP_SHARED 對(duì)映射區(qū)域的寫入數(shù)據(jù)會(huì)復(fù)制回文件內(nèi),而且允許其他映射該文件的進(jìn)程共享。

  • MAP_PRIVATE 對(duì)映射區(qū)域的寫入操作會(huì)產(chǎn)生一個(gè)映射文件的復(fù)制,即私人的“寫入時(shí)復(fù)制”(copy on write)對(duì)此區(qū)域作的任何修改都不會(huì)寫回原來的文件內(nèi)容。

  • MAP_ANONYMOUS建立匿名映射。此時(shí)會(huì)忽略參數(shù)fd,不涉及文件,而且映射區(qū)域無法和其他進(jìn)程共享。

  • MAP_DENYWRITE只允許對(duì)映射區(qū)域的寫入操作,其他對(duì)文件直接寫入的操作將會(huì)被拒絕。

  • MAP_LOCKED 將映射區(qū)域鎖定住,這表示該區(qū)域不會(huì)被置換(swap)。



因?yàn)槲覀儾恍枰獙懫?,所以prot只需要采用PORT_READ;而我們期望避免屏幕數(shù)據(jù)的多次創(chuàng)建,flags就需要用到MAP_SHARED,這樣文件句柄fd指向的內(nèi)存塊數(shù)據(jù)就會(huì)實(shí)時(shí)變更,無需多次創(chuàng)建,拷貝,釋放數(shù)據(jù)。


2.分析數(shù)據(jù)

截取到屏幕數(shù)據(jù)就好辦了,對(duì)每一幀進(jìn)行數(shù)據(jù)處理,這里完全就是算法問題了。懶人都用搓算法,大概的思路就是:7*7宮格,對(duì)于所有相連的兩個(gè)同色item做了橫向映射表和縱向映射表,然后輪尋處理5連,4連和3連。里面還有一些涉及到實(shí)現(xiàn)細(xì)節(jié)的映射表重置與預(yù)判,因?yàn)椴皇潜疚闹攸c(diǎn),就帶過了。

  1. void Handle_X_Combination() {


  2. LOGE("Handle_X_Combination");


  3. gen_Horizontal_Matrix(6);


  4. get_Horizontal_X_Match();


  5. gen_Vertical_Matrix(0, 6);


  6. get_Vertical_X_Match();

  7. }


下面是程序運(yùn)行時(shí)的Log信息片段,以供大家參考。


3. 模擬輸出

算法會(huì)輸出當(dāng)前屏幕的一個(gè)模擬手勢(shì)操作隊(duì)列,最精彩的當(dāng)然放到最后,也是此工程的技術(shù)點(diǎn),怎么模擬輸出手勢(shì)的問題。

Android所給予的截屏和模擬操作分別為 adb screenshot 和 adb shell sendevent (根據(jù)android版本,有些機(jī)型用的是input event,記得沒錯(cuò)的話~)
所有需要adb處理的指令,都不能采用高并發(fā)方式調(diào)用,要不然要么機(jī)器重啟,要么指令堵塞。所以adb這條路不通。
怎么辦呢?

懶人又一番思量后,linux系統(tǒng)大都采用文件buffer,直接將指令寫文件吧。其實(shí)adb也是寫文件,不過adb做了一層轉(zhuǎn)譯,這里涉及到設(shè)備層指令代碼,不同機(jī)型定義的指令代碼不盡相同。

要完成此任務(wù),首先要弄清楚幾件事情:

  1. 一個(gè)點(diǎn)擊事件的構(gòu)成是怎樣的

  2. 一個(gè)滑動(dòng)事件的構(gòu)成多了什么

  3. 事件的指令代碼分別代表什么



萬能的adb給了我一些思路,adb shell getevent,會(huì)打印出當(dāng)前event的指令。再科普一下,event有很多,包括compass_sensor,light_sensor,pressure_sensor,accelerometer_sensor等等。
我們這里監(jiān)聽的是,touchscreen_sensor。


有了上面的指導(dǎo)信息,要構(gòu)建一個(gè)模擬操作函數(shù)就很容易了。操作屏幕打印出想要的模擬的手勢(shì),然后寫下來就好了。一共會(huì)有這么幾個(gè)模擬操作函數(shù)需要?jiǎng)?chuàng)建:


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);



下面給出一個(gè)我寫好的范例出來,大家可以依葫蘆畫瓢,把剩下的寫好。

  1. void simulate_press_event(int touch, int fromX, int fromY) {


  2. pthread_mutex_lock(&global.writeEventLock);


  3. LOGE("simulate_press_event");


  4. INPUT_EVENT event;


  5. // 0. Multi-Touch

  6. // 此項(xiàng)目非必要,因?yàn)闆]有用到多點(diǎn)觸摸,是另一個(gè)項(xiàng)目使用到了

  7. event.type = 0x3;

  8. event.code = 0x2f;

  9. event.value = touch;

  10. write(global.fd_event, &event, sizeof(event));


  11. // 1. ABS_MT_TRACKING_ID:

  12. // 理論上必要,因?yàn)锳ndroid事件輸入是批量處理的,需要用到輸入id,

  13. // 但是這里偷懶使用了同步鎖,并且沒有多點(diǎn)觸摸需求,所以不會(huì)有Tracking_ID串?dāng)_問題,也就不需要記數(shù)了

  14. event.type = 0x3;

  15. event.code = 0x39;

  16. event.value = global.event_id > 60000 ? 10 : global.event_id++;

  17. write(global.fd_event, &event, sizeof(event));


  18. // 2. At screen coordinates:

  19. // 觸摸點(diǎn)x,y坐標(biāo)

  20. event.type = 0x3;

  21. event.code = 0x35;

  22. event.value = fromX;

  23. write(global.fd_event, &event, sizeof(event));

  24. event.type = 0x3;

  25. event.code = 0x36;

  26. event.value = fromY;

  27. write(global.fd_event, &event, sizeof(event));


  28. // 4. Sync

  29. // 數(shù)據(jù)同步到設(shè)備

  30. event.type = 0x0;

  31. event.code = 0x0;

  32. event.value = 0x0;

  33. write(global.fd_event, &event, sizeof(event));


  34. event.type = 0x3;

  35. event.code = 0x39;

  36. event.value = 0xffffffff;

  37. write(global.fd_event, &event, sizeof(event));


  38. // 4. Pure event separator:

  39. // 結(jié)束符

  40. event.type = 0x0;

  41. event.code = 0x0;

  42. event.value = 0x0;

  43. write(global.fd_event, &event, sizeof(event));


  44. pthread_mutex_unlock(&global.writeEventLock);

  45. }

為了大家對(duì)Android逆向有一個(gè)簡單的理解,我們看下面幾個(gè)問題。


首先,請(qǐng)大家查閱源碼:
frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp

截取其中關(guān)鍵的兩段:

渲染方式聲明:

  1. #ifdef EGL_ANDROID_swap_rectangle ?

  2. if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) {

  3. if (eglSetSwapRectangleANDROID(display, surface,

  4. 0, 0, mWidth, mHeight) == EGL_TRUE) {

  5. // This could fail if this extension is not supported by this ?

  6. // specific surface (of config) ?

  7. mFlags |= SWAP_RECTANGLE;

  8. }

  9. }

  10. // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE ?

  11. // choose PARTIAL_UPDATES, which should be more efficient ?

  12. if (mFlags & PARTIAL_UPDATES)

  13. mFlags &= ~SWAP_RECTANGLE;

  14. #endif ?

具體渲染操作:

  1. void DisplayHardware::flip(const Region& dirty) const

  2. {

  3. checkGLErrors();


  4. EGLDisplay dpy = mDisplay;

  5. EGLSurface surface = mSurface;


  6. #ifdef EGL_ANDROID_swap_rectangle ? ? ?

  7. if (mFlags & SWAP_RECTANGLE) {

  8. const Region newDirty(dirty.intersect(bounds()));

  9. const Rect b(newDirty.getBounds());

  10. eglSetSwapRectangleANDROID(dpy, surface,

  11. b.left, b.top, b.width(), b.height());

  12. }

  13. #endif ?


  14. if (mFlags & PARTIAL_UPDATES) {

  15. mNativeWindow->setUpdateRectangle(dirty.getBounds());

  16. }


  17. mPageFlipCount++;

  18. eglSwapBuffers(dpy, surface);

  19. checkEGLErrors("eglSwapBuffers");


  20. // for debugging ?

  21. //glClearColor(1,0,0,0); ?

  22. //glClear(GL_COLOR_BUFFER_BIT); ?

  23. }

這段代碼主要用來檢查系統(tǒng)的主繪圖表面是否支持EGL_ANDROID_swap_rectangle擴(kuò)展屬性。如果支持的話,那么每次在調(diào)用函數(shù)eglSwapBuffers來渲染UI時(shí),都會(huì)使用軟件的方式來支持部分更新區(qū)域功能,即:先得到不在新臟區(qū)域里面的那部分舊臟區(qū)域的內(nèi)容,然后再將得到的這部分舊臟區(qū)域的內(nèi)容拷貝回到要渲染的新圖形緩沖區(qū)中去,這要求每次在渲染UI時(shí),都要將被渲染的圖形緩沖區(qū)以及對(duì)應(yīng)的臟區(qū)域保存下來。注意,如果系統(tǒng)的主繪圖表面同時(shí)支持EGL_ANDROID_swap_rectangle擴(kuò)展屬性以及部分更新屬性,那么將會(huì)優(yōu)先使用部分更新屬性,因?yàn)楹笳呤侵苯釉谟布现С植糠指?,因而性能?huì)更好。

在Android源碼中有以下對(duì)framebuffer的結(jié)構(gòu)定義:
hardware/libhardware/include/hardware/gralloc.h

  1. typedef struct framebuffer_device_t {

  2. struct hw_device_t common;


  3. /* flags describing some attributes of the framebuffer */

  4. const uint32_t ?flags;


  5. /* dimensions of the framebuffer in pixels */

  6. const uint32_t ?width;

  7. const uint32_t ?height;


  8. /* frambuffer stride in pixels */

  9. const int ? ? ? stride;


  10. /* framebuffer pixel format */

  11. const int ? ? ? format;


  12. /* resolution of the framebuffer's display panel in pixel per inch*/

  13. const float ? ? xdpi;

  14. const float ? ? ydpi;


  15. /* framebuffer's display panel refresh rate in frames per second */

  16. const float ? ? fps;


  17. /* min swap interval supported by this framebuffer */

  18. const int ? ? ? minSwapInterval;


  19. /* max swap interval supported by this framebuffer */

  20. const int ? ? ? maxSwapInterval;


  21. int reserved[8];


  22. int (*setSwapInterval)(struct framebuffer_device_t* window,

  23. int interval);


  24. int (*setUpdateRect)(struct framebuffer_device_t* window,

  25. int left, int top, int width, int height);


  26. int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);


  27. int (*compositionComplete)(struct framebuffer_device_t* dev);


  28. void* reserved_proc[8];


  29. } framebuffer_device_t;

以上聲明中,成員函數(shù)compositionComplete用來通知fb設(shè)備device,圖形緩沖區(qū)的組合工作已經(jīng)完成。引用參考[2]的文章說明,此函數(shù)指針并沒有被使用到。那么,我們就要找到在哪里能夠獲取得到屏幕渲染完成的信號(hào)量了。

這個(gè)問題建議大家先行閱讀所有引用參考文章。然后因?yàn)閼?,這里就直接給出大家結(jié)論,過程需參考surfaceflinger的所有源碼。

我們都知道Android在渲染屏幕的時(shí)候,一開始用到了double buffer技術(shù),而后的4.0以上版本升級(jí)到triple buffer。buffer的緩存是以文件內(nèi)存映射的方式存儲(chǔ)在dev\graphics\fb0路徑。每塊buffer置換的時(shí)候,會(huì)有唯一的,一個(gè),信號(hào)量(注意修飾語)拋給應(yīng)用層,接收方是我們經(jīng)常用到的SurfaceView控件。SurfaceView內(nèi)的OnSurfaceChanged() API 即是當(dāng)前屏幕更新的信號(hào)量,除此之外,程序無從通過任何其他官方API形式獲取屏幕切換的時(shí)間點(diǎn)。這也是Android應(yīng)用商場(chǎng)為何沒有顯示當(dāng)前任意屏幕的FPS數(shù)值的軟件(補(bǔ)充一下,有,需要Root,用到的就是本文后續(xù)介紹的技術(shù)。準(zhǔn)確來說,是本文實(shí)現(xiàn)了一遍他們的技術(shù))。

本文將在稍后的獨(dú)立章節(jié)說明如何實(shí)現(xiàn)強(qiáng)行暴力獲取埋在系統(tǒng)底層surfaceflinger service內(nèi)的信號(hào)量。

Hooker 代碼注入

系統(tǒng)屏幕切換所用到的函數(shù)是在surfaceflinger內(nèi)的elfswapbuffer()函數(shù),要獲取得系統(tǒng)屏幕切換的信號(hào)量,需要劫持surfaceflinger service內(nèi)的elfswapbuffer()函數(shù),替換成我們自己的newelfswapbuffer()函數(shù),并在系統(tǒng)每次調(diào)用newelfswapbuffer()函數(shù)時(shí),此向JNI層拋出一個(gè)信號(hào)量,這樣就能強(qiáng)行獲得屏幕切換狀態(tài)量。

而,這樣做,需要用到hooker技能,向系統(tǒng)服務(wù)注入一段代碼,勾住elfswapbuffer()函數(shù)的ELF表地址,然后把自己的newelfswapbuffer()函數(shù)地址替換入ELF表內(nèi)。在程序結(jié)束后,需要逆向?qū)崿F(xiàn)一遍以上操作,還原ELF表。

程序用到了以下兩個(gè)核心文件:

一個(gè)文件負(fù)責(zé)注入系統(tǒng)服務(wù),另一個(gè)負(fù)責(zé)感染系統(tǒng)程序。

Inject surfaceflinger

  1. int main(int argc, char** argv) {


  2. pid_t target_pid;

  3. target_pid = find_pid_of("/system/bin/surfaceflinger");

  4. if (-1 == target_pid) {

  5. printf("Can't find the process\n");

  6. return -1;

  7. }


  8. //target_pid = find_pid_of("/data/test");

  9. inject_remote_process(target_pid, argv[1], "hook_entry", ?argv[2], strlen(argv[2]));


  10. return 0;

  11. }

Infect surfaceflinger

  1. int hook_entry(char * argv) {


  2. LOGD("Hook success\n");


  3. LOGD("pipe path:%s", argv);


  4. if(mkfifo(argv, 0777) != 0 && errno != EEXIST) {

  5. LOGD("pipe create failed:%d",errno);

  6. return -1;

  7. } else {

  8. LOGD("pipe create successfully");

  9. }


  10. LOGD("Start injecting\n");


  11. elfHook(LIB_PATH, "eglSwapBuffers", (void *)new_eglSwapBuffers, (void **)&old_eglSwapBuffers);


  12. while(1){


  13. int fPipe = open(argv, O_TRUNC, O_RDWR);

  14. if (fPipe == -1) {

  15. LOGD("pipe open failed");

  16. break;

  17. } else {

  18. LOGD("pipe open successfully");

  19. }


  20. char command[10];

  21. memset(command, 0x0, 10);


  22. int ret = read(fPipe, &command, 10);

  23. if(ret > 0 && strcmp(command, "done") == 0) {

  24. LOGD("ptrace detach successfully with %s", command);

  25. break;

  26. } else {

  27. LOGD("ret:%d received command: %s", ret, command);

  28. }


  29. // close the pipe

  30. close(fPipe);


  31. usleep(100);


  32. }


  33. elfHook(LIB_PATH, "eglSwapBuffers", (void *)old_eglSwapBuffers, (void **)&new_eglSwapBuffers);


  34. }

我們能看到以上代碼還用到了pipe管道通訊,那是因?yàn)樽⑷氲氖且欢味M(jìn)制可執(zhí)行代碼,而我們?cè)谕顺龀绦驎r(shí)需要與此二進(jìn)制代碼通訊,以便正常退出。




Android逆向工程簡單分析的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國家法律
呼和浩特市| 红河县| 南木林县| 昌宁县| 绵阳市| 会理县| 明水县| 惠来县| 吴桥县| 昌乐县| 尉氏县| 揭东县| 连南| 平安县| 疏勒县| 博客| 新巴尔虎左旗| 吉水县| 务川| 巨野县| 班玛县| 萝北县| 靖远县| 赫章县| 商城县| 攀枝花市| 宁晋县| 莒南县| 沧源| 中宁县| 盐源县| 深州市| 西吉县| 阜南县| 抚远县| 绍兴市| 凤阳县| 西盟| 石泉县| 通化市| 清丰县|