zoukankan      html  css  js  c++  java
  • arm c/c++ 基本结构逆向

    任何平台的逆向都是从基本语句开始的,我们需要知道if, for, while, do()while, switch等这些基本语句对应 arm 汇编是什么样子。
    这样我们的知识就能形成一个闭环,使我们更加深刻的掌握一门语言。

    下面就区分两种状态,即debug和release,对它们一一描述,重点是优化后不太一样。

    以后看汇编时扫一眼就大概知道是什么语句,方便快速的阅读汇编代码。

     


    if {} 语句 

    if (i < 9) {
      printf("i < 9
    ");
    }
    ; debug
    CMP R0, #8 BGT loc_99E ;BGT的含义是大于8跳到loc_99E,反过来if进入的条件就是 if (i <=8); B loc_992 ; --------------------------------------------------------------------------- loc_992 LDR R0, =(aI9 - 0x998) ADD R0, PC ; "i < 9 " BL sub_9F4 STR R0, [SP,#0x30+var_28] B loc_99E ; debug模式 这儿都要跳一下,算是一个特点吧 ; --------------------------------------------------------------------------- loc_99E
    ; relese
    CMP R0, #8 BGT loc_9AC ;BGT的含义是大于8跳到loc_9AC,反过来if进入的条件就是 if (i <=8); LDR R0, =(aI9 - 0x9AA) ADD R0, PC ; "i < 9" BLX puts loc_9AC

    重点是这两行汇编

    CMP  R0, #8

    BGT  loc_99E ;Z清零且(N等于V) 有符号数大于

    意思是大于8 跳走, if内部条件就是反过的,if (i <= 8)

    debug和release 共同点都是将if (i < 9) 编译成 if (i <=8) 虽然逻辑上是一样的。但是,我们在逆向的时候尽可能的向原始的代码上靠

    翻译汇编时建议写成 if (i < 9)


    if {} else{} 语句 

    if (i < 9) {
       printf("i < 9
    ");
    } else {
       printf("i >= 9
    ");
    }
    ; debug
    CMP R0, #8 BGT loc_99E B loc_992 ;这步与单if语句的区别,C里面就是“跳else” ; --------------------------------------------------------------------------- loc_992 LDR R0, =(aI9 - 0x998) ADD R0, PC ; "i < 9 " BL sub_A04 STR R0, [SP,#0x30+var_28] B loc_9AA ; --------------------------------------------------------------------------- loc_99E LDR R0, =(aI9_0 - 0x9A4) ADD R0, PC ; "i >= 9 " BL sub_A04 STR R0, [SP,#0x30+var_2C] B loc_9AA ; debug模式这儿要跳一下,算是个特点吧 ; --------------------------------------------------------------------------- loc_9AA
    ; release
    CMP R0, #8 BGT loc_9AA LDR R0, =(aI9 - 0x9AA) ADD R0, PC ; "i < 9" B loc_9AE ;这步与单if语句的区别,C里面就是“跳else” ; --------------------------------------------------------------------------- loc_9AA ; CODE XREF: main+1A↑j LDR R0, =(aI9_0 - 0x9B0) ADD R0, PC ; "i >= 9" loc_9AE ; CODE XREF: main+20↑j BLX puts

    if 与 if else 的区别并不大, 只是多了一个else的强制跳转


    if {} else if{} else{} 语句 

    if (i == 1) {
      printf("i = 1
    ");
    } else if ( i == 2) {
      printf("i = 2
    ");
    } else {
      printf("i != 1 && i != 2
    ");
    } 
    ; debug
                    CMP             R0, #1
                    BNE             loc_99E   ; 不等于1,检查下一条 else if 
                    B               loc_992
    ; ---------------------------------------------------------------------------
    loc_992
                    LDR             R0, =(aI1 - 0x998)
                    ADD             R0, PC    ; "i = 1
    "
                    BL              sub_A1C
                    STR             R0, [SP,#0x38+var_28]
                    B               loc_9BE   ; 直接跳到if语句的结尾
    ; ---------------------------------------------------------------------------
    loc_99E
                    LDR             R0, [SP,#0x38+var_18]
                    CMP             R0, #2    ; 检查else if (i == 2)
                    BNE             loc_9B2   ; 不等于2 跳到 else语句
                    B               loc_9A6   ; 等于2
    ; ---------------------------------------------------------------------------
    loc_9A6
                    LDR             R0, =(aI2 - 0x9AC)
                    ADD             R0, PC  ; "i = 2
    "
                    BL              sub_A1C
                    STR             R0, [SP,#0x38+var_2C]
                    B               loc_9BE   ; 跳到整个if语句的结尾
    ; ---------------------------------------------------------------------------
    loc_9B2                                   ; 此处是最后的 else
                    LDR             R0, =(aI1I2 - 0x9B8)
                    ADD             R0, PC  ; "i != 1 && i != 2
    "
                    BL              sub_A1C
                    STR             R0, [SP,#0x38+var_30]
                    B               loc_9BE
    ; ---------------------------------------------------------------------------
    loc_9BE 
    ; release
                    CMP             R0, #2
                    BEQ             loc_72E  ; 等于2   跳转
                    CMP             R0, #1
                    BNE             loc_734  ; 不等于1 跳转
                    LDR             R0, =(aI1 - 0x72E)  ; 此处是 if (i == 1) 基本块
                    ADD             R0, PC  ; "i = 1"
                    B               endif_738
    ; ---------------------------------------------------------------------------
    
    loc_72E                                 ; CODE XREF: main+A↑j
                    LDR             R0, =(aI2 - 0x734)  ; 此处是 if (i == 2) 基本块
                    ADD             R0, PC  ; "i = 2"
                    B               endif_738
    ; ---------------------------------------------------------------------------
    
    loc_734                                 ; CODE XREF: main+E↑j
                    LDR             R0, =(aI1I2 - 0x73A) ; 此处是 最后 else 基本块
                    ADD             R0, PC  ; "i != 1 && i != 2"
    
    endif_738                               ; CODE XREF: main+14↑j
                                            ; main+1A↑j
                    BLX             puts

     if {} else if{} else{} 语句 debug 与 release差别比较大, 但是共同点是每个块结尾都强制跳转到if语句的结尾

    如果看到这样的特点,基本上可以确定是if {} else if{} else{}语句,这点与x86上基本一样。

    同时,我发现另一个特点,debug在每个块完成后都要跳一下,release没有,这样在debug下会非常影响性能,所以建议发布时不要用debug编译。


    for 语句 

    for (int j = 0; j < i; j++) {
      printf("j = %d
    ", j);
    }
    ; debug
    begin_for_9C6
      LDR   R0, [SP,#0x30+var_1C] ; var_1C 就是变量 j
      LDR   R1, [SP,#0x30+var_18] ; var_18 就是变量 i
      CMP   R0, R1
      BGE   end_for_9E6           ; var_1C >= var_18就跳出for
      B     loc_9D0
    ; ---------------------------------------------------------------------------
    loc_9D0
      LDR   R1, [SP,#0x30+var_1C]
      LDR   R0, =(aJD - 0x9D8)
      ADD   R0, PC  ; "j = %d
    "
      BL    sub_9F0
      STR   R0, [SP,#0x30+var_2C]
      B     loc_9DE
    ; ---------------------------------------------------------------------------
    loc_9DE
      LDR   R0, [SP,#0x30+var_1C] ; var_1C +1
      ADDS  R0, #1
      STR   R0, [SP,#0x30+var_1C]
      B     begin_for_9C6         ; for 循环最典型的特征,从下往上跳
    ; ---------------------------------------------------------------------------
    end_for_9E6
    ; release
      MOV   R4, R0   ; R4就是变量 i
      CMP   R4, #1   
      BLT   endfor_99C ; i < 1 跳出循环
      LDR   R6, =(aJD - 0x990)
      MOVS  R5, #0   ; R5就是变量 j
      ADD   R6, PC  ; "j = %d
    "
    loc_98E
      MOV   R0, R6  ; 此处是循环体,相比debug优化很多
      MOV   R1, R5
      BL    sub_9A8 ; R0 R1 传递参数调用printf 
      ADDS  R5, #1
      CMP   R4, R5
      BNE   loc_98E ; for 循环的特征 +1向上跳
    endfor_99C

    可以看出for循环的特点,在循环的尾部 +1向上跳,同时也能看出debug和release在性能的上的差异太大了。
    debug版本需要不停的访问内存,速度上明显要慢不少!
    大多数的for循环都是从0开始的,release版本的for循环,刚开始就判断i是不是<1,这也算是一个release的特点吧。


    while 语句

    int j = 0;
    while(j < i) {
      printf("j = %d
    ", j);
      j++;
    } 
    ; debug
    begin_while_9C6
      LDR    R0, [SP,#0x30+var_1C] ; var_1C 就是变量 j
      LDR    R1, [SP,#0x30+var_18] ; var_18 就是变量 i
      CMP    R0, R1
      BGE    end_while_9E4         ; j >= i 跳出循环
      B      loc_9D0               ; debug模式的特点,跳到下一行
    ; ---------------------------------------------------------------------------
    loc_9D0
      LDR    R1, [SP,#0x30+var_1C]
      LDR    R0, =(aJD - 0x9D8)
      ADD    R0, PC  ; "j = %d
    "
      BL     sub_9F0
      LDR    R1, [SP,#0x30+var_1C]
      ADDS   R1, #1
      STR    R1, [SP,#0x30+var_1C]
      STR    R0, [SP,#0x30+var_2C]
      B      begin_while_9C6       ; 强制向上跳, 与for循环很像
    ; ---------------------------------------------------------------------------
    end_while_9E4 
    ; release
      MOV    R4, R0
      CMP    R4, #1 ; R4是变量 i
      BLT    end_while_99C ; R4 < 1 跳出while循环
      LDR    R6, =(aJD - 0x990)
      MOVS   R5, #0
      ADD    R6, PC  ; "j = %d
    "
    loc_98E
      MOV    R0, R6   ; while循环体    
      MOV    R1, R5
      BL     sub_9A8
      ADDS   R5, #1
      CMP    R4, R5
      BNE    loc_98E  ; R4 != R5 
    end_while_99C

     while循环与for循环非常像,区别不大。逻辑上while, do while,都可以写成for循环。 


     do while 语句 

    int j = 0;
    do {
      printf("j = %d
    ", j);
      j++;
    }while(j < i);
    ; debug
    begin_dowhile_9C6
      LDR   R1, [SP,#0x30+var_1C]  ; do while体,没有判断直接运行块中pritnf
      LDR   R0, =(aJD - 0x9CE)
      ADD   R0, PC  ; "j = %d
    "
      BL    sub_9F0
      LDR   R1, [SP,#0x30+var_1C]
      ADDS  R1, #1
      STR   R1, [SP,#0x30+var_1C]
      STR   R0, [SP,#0x30+var_2C]
      B     loc_9DA
    ; ---------------------------------------------------------------------------
    loc_9DA
      LDR   R0, [SP,#0x30+var_1C]
      LDR   R1, [SP,#0x30+var_18]
      CMP   R0, R1                ; 在此判断条件
      BLT   begin_dowhile_9C6
      B     end_dowhile_9E4
    ; ---------------------------------------------------------------------------
    end_dowhile_9E4
    ; release
      LDR             R6, =(aJD - 0x98C)
      MOV             R4, R0
      MOVS            R5, #0
      ADD             R6, PC  ; "j = %d
    "
    loc_98A
      MOV             R0, R6  ; 直接进入do while基本块中
      MOV             R1, R5
      BL              sub_9A4
      ADDS            R5, #1  
      CMP             R5, R4  ; 条件判断
      BLT             loc_98A ; do while结尾判断条件

    最大的特点是直接进入执行体没有判断,与C代码完全一致。


     switch 语句 

    switch (i) {
    case 1:
      printf("1");
      break;
    case 2:
      printf("2");
      break;
    case 3:
      printf("3");
      break;
    case 4:
      printf("4");
      break;
    default:
      printf("unknow");
    }
    ; debug
      STR      R0, [SP,#0x38+var_18]
      SUBS     R0, #1  ; switch 4 cases
      MOV      R1, R0
      CMP      R0, #3
      STR      R1, [SP,#0x38+var_24]
      BHI      def_9C8 ; jumptable 000009C8 default case
      LDR      R1, [SP,#0x38+var_24]
      TBB.W    [PC,R1] ; switch jump   ; switch最典型的特征,有一个跳转表
    ; ---------------------------------------------------------------------------
    jpt_9C8    DCB 2                   ; jump table for switch statement
               DCB 8
               DCB 0xE
               DCB 0x14
    ; ---------------------------------------------------------------------------
    loc_9D0
      LDR      R0, =(a1 - 0x9D6) ; jumptable 000009C8 case 1
      ADD      R0, PC  ; "1
    "
      BL       sub_A28
      STR      R0, [SP,#0x38+var_28]
      B        end_switch_A0C
    ; ---------------------------------------------------------------------------
    loc_9DC
      LDR      R0, =(a2 - 0x9E2) ; jumptable 000009C8 case 2
      ADD      R0, PC  ; "2
    "
      BL       sub_A28
      STR      R0, [SP,#0x38+var_2C]
      B        end_switch_A0C
    ; ---------------------------------------------------------------------------
    loc_9E8
      LDR      R0, =(a3 - 0x9EE) ; jumptable 000009C8 case 3
      ADD      R0, PC  ; "3
    "
      BL       sub_A28
      STR      R0, [SP,#0x38+var_30]
      B        end_switch_A0C
    ; ---------------------------------------------------------------------------
    loc_9F4                          ; CODE XREF: main+2C↑j
      LDR      R0, =(a4 - 0x9FA) ; jumptable 000009C8 case 4
      ADD      R0, PC  ; "4
    "
      BL       sub_A28
      STR      R0, [SP,#0x38+var_34]
      B        end_switch_A0C
    ; ---------------------------------------------------------------------------
    def_9C8
      LDR      R0, =(aUnknow - 0xA06) ; jumptable 000009C8 default case
      ADD      R0, PC  ; "unknow"
      BL       sub_A28
      STR      R0, [SP,#0x38+var_38]
      B        end_switch_A0C
    ; ---------------------------------------------------------------------------
    end_switch_A0C
    ; release
      SUBS            R0, #1  ; switch 4 cases
      CMP             R0, #3
      BHI             def_9AE ; jumptable 000009AE default case
      TBB.W           [PC,R0] ; switch jump  ; release版本也一样存在 TBB, 后面就是跳转表,这是最重要的特征
    ; ---------------------------------------------------------------------------
    jpt_9AE         DCB 2                   ; jump table for switch statement
      DCB 0xA
      DCB 0xD
      DCB 0x10
    ; ---------------------------------------------------------------------------
    loc_9B6
      LDR             R0, =(a1 - 0x9BC) ; jumptable 000009AE case 1
      ADD             R0, PC  ; "1"
      B               end_switch_9D6
    ; ---------------------------------------------------------------------------
    def_9AE
      LDR             R0, =(aUnknow - 0x9C2) ; jumptable 000009AE default case
      ADD             R0, PC  ; "unknow"
      BL              sub_9F4
      B               loc_9DA
    ; ---------------------------------------------------------------------------
    
    loc_9C6                                 ; CODE XREF: main+E↑j
      LDR             R0, =(a2 - 0x9CC) ; jumptable 000009AE case 2
      ADD             R0, PC  ; "2"
      B               end_switch_9D6
    ; ---------------------------------------------------------------------------
    
    loc_9CC                                 ; CODE XREF: main+E↑j
      LDR             R0, =(a3 - 0x9D2) ; jumptable 000009AE case 3
      ADD             R0, PC  ; "3"
      B               end_switch_9D6
    ; ---------------------------------------------------------------------------
    loc_9D2
      LDR             R0, =(a4 - 0x9D8) ; jumptable 000009AE case 4
      ADD             R0, PC  ; "4"
    end_switch_9D6

    arm的汇编基本上都一样,以后介绍类 class的各种实现。
    class中继承,派生,虚函数等在arm中是如何实现的。

    说明:目前的编译器都是ndk的编译器 armeabi-v7a

  • 相关阅读:
    关于BufferefReader.readLine()方法的理解
    web.xml配置
    第一章 网络编程入门
    浅谈c++中map插入数据的用法
    c++中new的用法
    线程
    设计模式(九)Bridge模式
    设计模式(八)Abstract Factory模式
    牛逼的人,都不太要面子
    质量运营在美团点评智能支付业务测试中的初步实践
  • 原文地址:https://www.cnblogs.com/russinovich/p/12236300.html
Copyright © 2011-2022 走看看