arm c/c++ 基本结构逆向

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

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

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

 


if {} 语句 

if (i < 9) {
  printf("i < 9\n");
}
; 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\n" 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\n");
} else {
   printf("i >= 9\n");
}
; debug
CMP R0, #8 BGT loc_99E B loc_992 ;这步与单if语句的区别,C里面就是“跳else” ; --------------------------------------------------------------------------- loc_992 LDR R0, =(aI9 - 0x998) ADD R0, PC ; "i < 9\n" BL sub_A04 STR R0, [SP,#0x30+var_28] B loc_9AA ; --------------------------------------------------------------------------- loc_99E LDR R0, =(aI9_0 - 0x9A4) ADD R0, PC ; "i >= 9\n" 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\n");
} else if ( i == 2) {
  printf("i = 2\n");
} else {
  printf("i != 1 && i != 2\n");
} 
; debug
                CMP             R0, #1
                BNE             loc_99E   ; 不等于1,检查下一条 else if 
                B               loc_992
; ---------------------------------------------------------------------------
loc_992
                LDR             R0, =(aI1 - 0x998)
                ADD             R0, PC    ; "i = 1\n"
                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\n"
                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\n"
                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\n", 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\n"
  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\n"
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\n", 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\n"
  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\n"
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\n", 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\n"
  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\n"
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\n"
  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\n"
  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\n"
  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\n"
  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

 

posted @ 2019-01-28 16:40  Russinovich`s Blog  阅读(1160)  评论(0编辑  收藏  举报