新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > android平臺arm指令學習和調試

        android平臺arm指令學習和調試

        作者: 時間:2016-11-09 來源:網絡 收藏
        */

        一、Ndk下內聯匯編

        跟vc下一樣,ndk編譯環境下也能使用內聯匯編,如下:

        本文引用地址:http://www.104case.com/article/201611/317575.htm

        include

        intmy_thumb(intdummy)

        {

        __asm__(

        “movr0,#1tn”

        “movr1,#2tn”

        “addr0,r0,r1tn”

        “bxlr”

        );

        }

        intmain()

        {

        intn=my_thumb(12);

        printf(“result:%08x”,n);

        return0;

        }

        再看看使用標簽的例子:

        #include

        intmy_thumb(intdummy)

        {

        __asm(

        “movr0,#0x1tn”

        “ldrr0,__starttn”

        “adrr0,__starttn”

        “ldrr0,=__starttn”

        “__start:tn”

        “noptn”

        );

        }

        intmain()

        {

        intn=my_thumb(12);

        printf(“result:%08x”,n);

        return0;

        }

        二、編譯

        創建android.mk文件,然后使用ndk-build進行編譯

        1)編譯一個可執行程序,Android.mk文件的內容如下:

        LOCAL_PATH:=$(callmy-dir)

        include$(CLEAR_VARS)

        LOCAL_MODULE:=helloa

        LOCAL_SRC_FILES:=testHello.c

        include$(BUILD_EXECUTABLE)

        2)編譯一個so,android.mk文件內容如下:

        LOCAL_PATH:=$(callmy-dir)

        include$(CLEAR_VARS)

        LOCAL_MODULE:=testJniSo

        LOCAL_SRC_FILES:=testJniSo.c

        include$(BUILD_SHARED_LIBRARY)

        3)必須有一個jni目錄,目錄結構:

        4)這樣之后,就可以在cmd窗口中,切換到代碼目錄,使用ndk-build命令進行編譯。

        三、調試可執行程序

        1)Push編譯好的elf到樣機中

        2)使用gdbserver啟動該文件

        “/data/local/gdbserver:12345/data/local/helloa”

        3)adbforwardtcp:12345tcp:12345

        4)啟動gdb

        5)Targetremote127.0.0.1:12345

        6)Ni是單步,setdisassemble-nexton下一句指令顯示反匯編,使用setarmforce-modearm或者setarmforce-modethumb讓gdb切換thumb和arm代碼顯示。

        7)Display/i$pc顯示當前的代碼

        8)Continue就是windbg的f5,od的f9

        9)Infobreakpoints顯示斷點,而delete刪除斷點disable禁用斷點

        10)Disas0xAAAA,+20(20字節的數據)顯示反匯編

        11)調試比較



        Gdbarm和ida反匯編比較:

        四、調試so

        以愛加密為例,進行調試,范例是看雪求助帖給出的,鏈接如下:

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

        調試方法

        1)So中實現的方法,程序跑起來再附加的話,該方法可能已經執行完。看雪上給出方法步驟較多,應用和底層都調試,有點復雜如下:

        我試出來的方法,對so文件進行修改,讓其在入口處直接調用它本身。需要注意的是,必須在so的入口函數處修改,如果在內部調用函數修改,app會自動退出。

        2)gdb的調試,使用ndk下自帶的gdbserver和gdb進行調試

        3)將修改的so導入到手機該應用的lib目錄下,覆蓋之前的so文件。然后運行app,出現只有邊界的黑框。Gdbserver附加,使用set命令,將bl調用自身的命令改回去,則可以進行正常調試了。

        4)模塊基地址的獲取cat/proc/pid/maps找到

        5)Dump內存的命令式dumpbinarymemoryc:xxxstartAdendAd

        6)小細節要注意的是:.init_proc的地址是0x31cb9,ida直接點過去,是一堆數據,摁c無法轉成代碼

        因為arm指令和thumb指令是2字節或者4字節對齊的,所以,改地址加1應該是真正的地址。將該地址修改為調用自身,然后gdb附加,成功斷下來,然后輸入“ir$lr”查看返回值,如下

        該地址恰巧是在linker模塊中,

        此處的在linker中的代碼如下:

        7)至于為什么系統的linker會調用.init_proc,不得而知。該so的oep為空,沒有section表,但是有dynamicsection表。自己寫一個so,并沒有函數“init_proc”的導出,需要再研究下elf文件格式。

        自解碼

        本身的so是經過加密的,解密的代碼是在.init_proc+1的位置,字節碼的代碼較長,部分代碼如下:

        1)windows下逃避斷點的方法,就是利用“codeshadow”的方式,在申請內存,然后將代碼拷貝到指定的申請的空間上執行,這樣,直接斷原代碼是斷不到的。但是,直接搜索二進制就可以找到這樣的鏡像代碼。

        如下,之前應該運行的代碼:

        拷貝到其它地方執行的代碼:

        2)計算指定函數的匯編指令長度:代碼要進行拷貝執行,必須計算拷貝代碼的長度。愛加密計算拷貝代碼的長度方式是通過call來計算的。函數實現的排布如下:

        __asmcalltargetFunc

        xxxxxx

        xxxxxx

        xxxxxx

        xxxxxx

        xxxxxx

        xxxxxx

        VoidtargetFunc

        而call的指令是:

        00f0是指令opcode,c0與跳轉的距離有關,計算公式是(targetAddr–curAddr)/2–2=opdataLen。那么知道c0就知道中間xxx代碼的長度了。

        3)愛加密處理的數據有三塊,第一塊未知,第二塊是壓縮過的指令數據,第三塊是需要拷貝到堆上的代碼,這三塊數據總長度為0x29e3c,使用mmap2進行申請映射內存,如下:

        然后將三塊數據拷貝到映射的內存上,

        在此之后,數據內存排布:

        AAAAAAAAA

        BBBBBBBBBB

        CCCCCCCCCC

        愛加密還原代碼前,處理了三塊數據,如上。其中,“AAAAAAAAAA”數據是一組未使用過的數據,具體功能未知,長度為0x6b0;“BBBBBBBBBB”代表的是lzma壓縮過的數據;“CCCCCCCCCC”代表的是拷貝函數的數據(一共拷貝三個函數的代碼)。

        4)修改lr,然后刷新cache,返回到mmap2得到的堆上的函數進行執行

        OAD:510A4010 01 1C MOVS R1, R0 ; r0 0x4fb83e3c
        LOAD:510A4012 10 1C MOVS R0, R2 ; r2 0x4fb83bd8
        LOAD:510A4014 BE 46 MOV LR, R7 ; r7 0x4fb83d91
        LOAD:510A4014 ; 這里對lr寄存器進行修改
        LOAD:510A4016 C3 E7 B libexec___ARM_NR_cacheflush

        5)再次調用mmap2,抹掉最初的“AA”、“BB”和“CC”三塊數據,這樣,之前執行的代碼和數據將都被清零。這三塊數據就被搬運到第一次調用mmap2得到的地址上,數據在內存重新做了排布。

        6)解壓數據:將數據解壓到第二次調用mmap2清零的地址上,偏移為0x6b0。數據的解壓使用的是lzma算法,解壓的代碼:

        上圖中,r4指向的lzma的解壓函數,r0是待解壓的數據,r1是待解壓的長度,r2是解壓到的地址,r3上是保存解壓到的地址空間的長度的地址。Lzma的具體實現:

        7)修正指定數據:解壓完的數據并不是最終的代碼,還需要進行修正。修正代碼如下:

        修正的方法是:解壓后的數據從末端開始往前,每四字節為單位,如果這四字的最高個字節是低四位是0xb,則進行移位修復。(這種方法很像pe殼中解密代碼后,對0xe8、0xe9、0xff25等的修復)修復代碼如下:

        8)修改內存屬性,代碼就完全解密,并可以運行了。

        解密前的導出函數代碼如下:

        顯然上圖中的代碼沒法運行,解碼后的代碼如下:

        解碼后的代碼分析

        解密后的代碼上邊已經給出,r0傳遞的就是jnienv虛表指針,gdb中顯示相關api在libicuil8n.so模塊中,如下:

        1)動態注冊接口:解密后的代碼主要是動態注冊用于java調用的接口,如下:

        2)load的實現

        3)run方法的實現

        gdb調試的問題:

        1)Gdb在遇到bxpc;nop指令,從thumb指令切換到arm指令時,會出錯,調試無法繼續進行。解決方法:

        http://sourceware-org.1504.n7.nabble.com/Fix-ARM-stepping-over-Thumb-mode-quot-bx-pc-quot-or-quot-blx-pc-quot-td69213.html)

        2)gdb遇到如下的拷貝指令也會退出調試,解決方法是跳過這段代碼的位置下斷點。

        LOAD:510A3F88 loc_510A3F88 ; CODE XREF: libexec_memcopy+1Ej
        LOAD:510A3F88 DC 13 B1 E8 LDMIA R1!, {R2-R4,R6-R9,R12}
        LOAD:510A3F8C 01 50 55 E2 SUBS R5, R5, #1 ; 這里r5不斷的減少
        LOAD:510A3F90 DC 13 A0 E8 STMIA R0!, {R2-R4,R6-R9,R12}
        LOAD:510A3F94 FB FF FF 1A BNE loc_510A3F88
        LOAD:510A3F98 D4 03 BD E8 LDMFD SP!, {R2,R4,R6-R9}



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 南汇区| 哈巴河县| 长治市| 青河县| 积石山| 开鲁县| 城口县| 鄂托克旗| 岑溪市| 汽车| 称多县| 三都| 客服| 汪清县| 澜沧| 贵南县| 边坝县| 台东县| 宜宾市| 南充市| 长乐市| 东丰县| 呼图壁县| 古蔺县| 胶州市| 峨边| 萨嘎县| 顺昌县| 白河县| 上虞市| 洛川县| 东港市| 黎平县| 尼勒克县| 佛坪县| 伊金霍洛旗| 明光市| 漳平市| 东兰县| 翁源县| 荔波县|