使用IDA进行静态逆向分析,很重要的一点是对高级语言程序结构的解析,比如Switch-Case结构。偏偏IDA的F5功能很多时候会无法生成C语言代码,此时就需要我们自己分析汇编代码,理解switch-case结构等。
从简单入手, 测试使用的C++代码如下:
int switch_case(char a,int b,int *c,int d,int e)
{
switch(a)
{
case 'a':
*c = d + 3;
break;
case 'b':
*c = d + 4;
break;
case 'c':
*c = d - 8;
break;
case 'd':
*c = d + e;
break;
default:
e -= 10;
break;
}
switch(b)
{
case 1235:
b += d;
break;
case 1236:
b += e;
break;
case 1237:
b += *c;
break;
case 1238:
b -= 100;
break;
default:
e += 100;
break;
}
printf("a=%d\r\n,b=%d\r\n,*c=%d",a,b,*c);
return e;
}
int main(int argc,char *argv[])
{
int number = 0;
int i = 0;
int j = 0;
int k = 0;
scanf("%d",&number);
scanf("%d",&i);
scanf("%d",&j);
scanf("%d",&k);
char c = '\0';
scanf("%c",&c);
int m = switch_case(c,i,&number,j,k);
printf("m = %d\r\n",m);
return 0;
}
编译生成release版本的EXE执行文件,删除pdb文件后,使用IDA加载EXE进行静态分析。首先看main函数调用swcitch_case函数处如何传递参数:
.text:004010A0 var_14 = dword ptr -14h
.text:004010A0 var_10 = dword ptr -10h
.text:004010A0 var_C = dword ptr -0Ch
.text:004010A0 var_8 = dword ptr -8
.text:004010A0 var_4 = dword ptr -4
.text:004010A0 argc = dword ptr 4
.text:004010A0 argv = dword ptr 8
.text:004010A0 envp = dword ptr 0Ch
.text:004010A0
.text:004010A0 sub esp, 14h
.text:004010A3 push ebx
.text:004010A4 push esi
.text:004010A5 mov esi, ds:scanf
.text:004010AB push edi
.text:004010AC lea eax, [esp+20h+var_8]
.text:004010B0 xor ebx, ebx
.text:004010B2 push eax
.text:004010B3 push offset aD ; "%d"
.text:004010B8 mov [esp+28h+var_8], ebx
.text:004010BC mov [esp+28h+var_4], ebx
.text:004010C0 mov [esp+28h+var_C], ebx
.text:004010C4 mov [esp+28h+var_10], ebx
.text:004010C8 call esi ; scanf
.text:004010CA lea ecx, [esp+28h+var_4]
.text:004010CE push ecx
.text:004010CF push offset aD ; "%d"
.text:004010D4 call esi ; scanf
.text:004010D6 lea edx, [esp+30h+var_C]
.text:004010DA push edx
.text:004010DB push offset aD ; "%d"
.text:004010E0 call esi ; scanf
.text:004010E2 lea eax, [esp+38h+var_10]
.text:004010E6 push eax
.text:004010E7 push offset aD ; "%d"
.text:004010EC call esi ; scanf
.text:004010EE lea ecx, [esp+40h+var_14]
.text:004010F2 push ecx
.text:004010F3 push offset aC ; "%c"
.text:004010F8 mov byte ptr [esp+48h+var_14], bl
.text:004010FC call esi ; scanf
.text:004010FE mov edx, [esp+48h+var_14] ; char c
.text:00401102 mov eax, [esp+48h+var_10] ; int k 传入值
.text:00401106 mov edi, [esp+48h+var_C] ; int j 传入值
.text:0040110A mov ecx, [esp+48h+var_4] ; int i 传入值
.text:0040110E push edx ; char c 通过入栈传递参数
.text:0040110F lea edx, [esp+4Ch+var_8] ; int number 传入指针
.text:00401113 call sub_401000
Go on。再看看switch_case函数的汇编解析:
.text:00401000 sub_401000 proc near ; CODE XREF: _main+73p
.text:00401000
.text:00401000 arg_0 = byte ptr 4 ; char c 通过入栈传递参数
.text:00401000
.text:00401000 push ebx
.text:00401001 push esi
.text:00401002 movsx esi, [esp+8+arg_0] ; arg_0 即为 char c
.text:00401007 mov ebx, eax ; eax 为 int k 传入值
.text:00401009 lea eax, [esi-61h] ; switch 4 cases 61h = 'a'
.text:0040100C cmp eax, 3
.text:0040100F ja short loc_401034 ; default
.text:00401011 jmp ds:off_401078[eax*4] ; switch jump off_401078 为第一个switch case的跳转表首地址
.text:00401018
.text:00401018 loc_401018: ; DATA XREF: .text:off_401078o
.text:00401018 lea eax, [edi+3] ; jumptable 00401011 case 97 edi 为 int j 传入值
.text:0040101B mov [edx], eax
.text:0040101D jmp short loc_401037
.text:0040101F ; ---------------------------------------------------------------------------
.text:0040101F
.text:0040101F loc_40101F: ; CODE XREF: sub_401000+11j
.text:0040101F ; DATA XREF: .text:off_401078o
.text:0040101F lea eax, [edi+4] ; jumptable 00401011 case 98
.text:00401022 mov [edx], eax
.text:00401024 jmp short loc_401037
.text:00401026 ; ---------------------------------------------------------------------------
.text:00401026
.text:00401026 loc_401026: ; CODE XREF: sub_401000+11j
.text:00401026 ; DATA XREF: .text:off_401078o
.text:00401026 lea eax, [edi-8] ; jumptable 00401011 case 99
.text:00401029 mov [edx], eax
.text:0040102B jmp short loc_401037
.text:0040102D ; ---------------------------------------------------------------------------
.text:0040102D
.text:0040102D loc_40102D: ; CODE XREF: sub_401000+11j
.text:0040102D ; DATA XREF: .text:off_401078o
.text:0040102D lea eax, [edi+ebx] ; jumptable 00401011 case 100
.text:00401030 mov [edx], eax
.text:00401032 jmp short loc_401037
.text:00401034 ; ---------------------------------------------------------------------------
.text:00401034
.text:00401034 loc_401034: ; CODE XREF: sub_401000+Fj
.text:00401034 sub ebx, 0Ah ; default
.text:00401037
.text:00401037 loc_401037: ; CODE XREF: sub_401000+1Dj
.text:00401037 ; sub_401000+24j ...
.text:00401037 lea eax, [ecx-4D3h] ; switch 4 cases ; ecx 为 int i 传入值
.text:0040103D cmp eax, 3
.text:00401040 ja short loc_40105A ; default
.text:00401042 jmp ds:off_401088[eax*4] ; switch jump off_401088 为第二个switch case的跳转表首地址
.text:00401049
.text:00401049 loc_401049: ; DATA XREF: .text:off_401088o
.text:00401049 add ecx, edi ; jumptable 00401042 case 1235
.text:0040104B jmp short loc_40105D
.text:0040104D ; ---------------------------------------------------------------------------
.text:0040104D
.text:0040104D loc_40104D: ; CODE XREF: sub_401000+42j
.text:0040104D ; DATA XREF: .text:off_401088o
.text:0040104D add ecx, ebx ; jumptable 00401042 case 1236
.text:0040104F jmp short loc_40105D
.text:00401051 ; ---------------------------------------------------------------------------
.text:00401051
.text:00401051 loc_401051: ; CODE XREF: sub_401000+42j
.text:00401051 ; DATA XREF: .text:off_401088o
.text:00401051 add ecx, [edx] ; jumptable 00401042 case 1237
.text:00401053 jmp short loc_40105D
.text:00401055 ; ---------------------------------------------------------------------------
.text:00401055
.text:00401055 loc_401055: ; CODE XREF: sub_401000+42j
.text:00401055 ; DATA XREF: .text:off_401088o
.text:00401055 sub ecx, 64h ; jumptable 00401042 case 1238
.text:00401058 jmp short loc_40105D
.text:0040105A ; ---------------------------------------------------------------------------
.text:0040105A
.text:0040105A loc_40105A: ; CODE XREF: sub_401000+40j
.text:0040105A add ebx, 64h ; default
.text:0040105D
.text:0040105D loc_40105D: ; CODE XREF: sub_401000+4Bj
.text:0040105D ; sub_401000+4Fj ...
.text:0040105D mov edx, [edx]
.text:0040105F push edx
.text:00401060 push ecx
.text:00401061 push esi
.text:00401062 push offset Format ; "a=%d\r\n,b=%d\r\n,*c=%d"
.text:00401067 call ds:printf
.text:0040106D add esp, 10h
.text:00401070 pop esi
.text:00401071 mov eax, ebx
.text:00401073 pop ebx
.text:00401074 retn
.text:00401074 sub_401000 endp
.text:00401074
.text:00401074 ; ---------------------------------------------------------------------------
.text:00401075 align 4
.text:00401078 off_401078 dd offset loc_401018 ; DATA XREF: sub_401000+11r
.text:00401078 dd offset loc_40101F ; jump table for switch statement
.text:00401078 dd offset loc_401026
.text:00401078 dd offset loc_40102D
.text:00401088 off_401088 dd offset loc_401049 ; DATA XREF: sub_401000+42r
.text:00401088 dd offset loc_40104D ; jump table for switch statement
.text:00401088 dd offset loc_401051
.text:00401088 dd offset loc_401055
.text:00401098 align 10h
.text:004010A0
如上,2个switch_case函数里面包括2个Switch-Case结构,汇编代码中就包含有2个跳转表,每个跳转表都包括4个case的跳转地址,可以理解为跳转数组,然后程序根据跳转数组的索引值,通过jmp指令跳转到各个分支即可。