在逆向中经常会遇到switch语句,在这里做一个总结,如果有错误,望师傅们可以指正。
switch要求
case后面必须是常量表达式
case后常量表达式的值不能一样
switch后面表达式必须为整数
case中必须是常量,常量的值不能一样,switch表达式的结果必须是整型
当分支条件比较小的时候,switch 和 if else 没有区别
1.添加case后面的值,一个一个增加,观察反汇编代码的变化(何时生成大表).
三个case情况下,生成的汇编码和if else 近似
0040F3E8 mov eax,dword ptr [ebp+8]
0040F3EB mov dword ptr [ebp-4],eax
0040F3EE cmp dword ptr [ebp-4],0
0040F3F2 je Function+32h (0040f402)
0040F3F4 cmp dword ptr [ebp-4],1
0040F3F8 je Function+41h (0040f411)
0040F3FA cmp dword ptr [ebp-4],2
0040F3FE je Function+50h (0040f420)
0040F400 jmp Function+5Fh (0040f42f)
0040F402 push offset string "A" (004241a4)
0040F407 call printf (00401610)
0040F40C add esp,4
0040F40F jmp Function+6Ch (0040f43c)
0040F411 push offset string "B" (00424194)
0040F416 call printf (00401610)
0040F41B add esp,4
0040F41E jmp Function+6Ch (0040f43c)
0040F420 push offset string "B" (00424194)
0040F425 call printf (00401610)
0040F42A add esp,4
0040F42D jmp Function+6Ch (0040f43c)
0040F42F push offset string "Error" (00424214)
0040F434 call printf (00401610)四个case的情况下,开始生成大表
switch(number)
{ case 0:printf("A");break; case 1:printf("B");break; case 2:printf("B");break; case 3:printf("B");break; default:printf("Error");break;
}
其中case从0开始,没有进行sub处理
尝试case从1开始,看是否有sub
switch(number)
{ case 1001:printf("A");break; case 1002:printf("B");break; case 1003:printf("B");break; case 1004:printf("B");break; default:printf("Error");break;
}
果然出现了sub ecx,1
当case是大数且连续时看sub的情况
switch(number)
{ case 1004:printf("A");break; case 1001:printf("B");break; case 1002:printf("B");break; case 1003:printf("B");break; default:printf("Error");break;
}
sub ecx,3E9h
其中3E9转换成十进制是1001
由此可以判断,如果case是大数值且连续,编译器会将其处理成0开始的索引,大表中保存各个分支语句函数的地址,配合edx*4+40D841h
直接定位到跳转的地方(40D841h是大表的首地址)
当分支语句case多余一定数量时(该数量由编译器界定,不同编译器数量可能不一致,vc6是4)switch 会生成一个大表,大表中保存各个分支语句函数的地址,通过edx*4+40D841h
直接定位到跳转的地方(40D841h是大表的首地址)
将case中的常量值的顺序打乱,观察反汇编代码(观察顺序是否会影响生成大表).
switch(number)
{ case 1004:printf("A");break; case 1002:printf("B");break; case 1001:printf("C");break; case 1003:printf("D");break; default:printf("Error");break;
}
大表生成顺序,按照case从小到大生成
将case后面的值改成从100开始到109,观察汇编变化(观察值较大时是否生成大表).
switch(number)
{ case 101:printf("A");break; case 102:printf("B");break; case 103:printf("C");break; case 104:printf("D");break; case 105:printf("E");break; case 106:printf("F");break; case 107:printf("I");break; case 108:printf("J");break; case 109:printf("K");break; default:printf("Error");break;
}
值较大时也同样生成大表
将连续的10项中抹去1项或者2项,观察反汇编有无变化(观察大表空缺位置的处理).
switch(number)
{ case 101:printf("A");break; case 103:printf("C");break; case 104:printf("D");break; case 105:printf("E");break; case 107:printf("I");break; case 108:printf("J");break; case 109:printf("K");break; default:printf("Error");break;
}
被抹去的位置,在大表中会用default的位置填充
在10项中连续抹去,不要抹去最大值和最小值(观察何时生成小表).
switch(number)
{ case 101:printf("A");break; case 108:printf("J");break; case 109:printf("K");break; case 110:printf("K");break; default:printf("Error");break;
}
连续删除6项时,出现小表
小表存储时,如果是case中的数值返回大表中对应的分支语句的索引,如果不是返回大表中default的索引
生成的小表最大256个,FF ,如果相差超过一个字节,小表也表示不了
将case后面常量表达式改成毫不连续的值,观察反汇编变化.
switch(number)
{ case 101:printf("A");break; case 999:printf("J");break; case 9999:printf("K");break; case 99999:printf("K");break; default:printf("Error");break;
}
表达式毫不相干时,是搜索二叉树(没有意义,实战中基本不会遇到)
总结
分支少于4的时候用switch没有意义,因为编译器会生成类似if..else之类的反汇编
case后面的常量可以是无序的,并不影响大表的生成
小表与大表在内存中是紧挨着的,小表在大表的下面
switch语句生成四种情况
分支少,按if else形式生成
分支间隔特别大,按if else形式生成
分支连续,生成大表(间隔小时用default生成大表)
分支间隔大,生成小表
1、写一个switch语句,不生产大表也不生产小表,贴出对应的反汇编.
2、写一个switch语句,只生成大表,贴出对应的反汇编.
3、写一个switch语句,生成大表和小表,贴出对应的反汇编.
4、为do..while语句生成的反汇编填写注释.
5、为while语句生成的反汇编填写注释.
6、为for语句生成的反汇编填写注释.
附上练习答案
1.
switch(number)
{ case 1:printf("1");break; case 2:printf("2");break; case 3:printf("3");break; default:printf("Error");break;
}0040F5D8 mov eax,dword ptr [ebp+8]0040F5DB mov dword ptr [ebp-4],eax0040F5DE cmp dword ptr [ebp-4],10040F5E2 je Function+32h (0040f5f2)0040F5E4 cmp dword ptr [ebp-4],20040F5E8 je Function+41h (0040f601)0040F5EA cmp dword ptr [ebp-4],30040F5EE je Function+50h (0040f610)0040F5F0 jmp Function+5Fh (0040f61f)0040F5F2 push offset string "1" (00424fbc)0040F5F7 call printf (00401610)0040F5FC add esp,40040F5FF jmp Function+6Ch (0040f62c)0040F601 push offset string "2" (004241a4)0040F606 call printf (00401610)0040F60B add esp,40040F60E jmp Function+6Ch (0040f62c)0040F610 push offset string "3" (00424194)0040F615 call printf (00401610)0040F61A add esp,40040F61D jmp Function+6Ch (0040f62c)0040F61F push offset string "Error" (00424214)0040F624 call printf (00401610)
2.
switch(number)
{ case 1:printf("1");break; case 2:printf("2");break; case 3:printf("3");break; case 4:printf("4");break; case 5:printf("5");break; case 6:printf("6");break; case 7:printf("7");break; case 8:printf("8");break; case 9:printf("9");break; case 10:printf("10");break; default:printf("Error");break;
}0040F5C0 push ebp0040F5C1 mov ebp,esp0040F5C3 sub esp,44h0040F5C6 push ebx0040F5C7 push esi0040F5C8 push edi0040F5C9 lea edi,[ebp-44h]0040F5CC mov ecx,11h0040F5D1 mov eax,0CCCCCCCCh0040F5D6 rep stos dword ptr [edi]0040F5D8 mov eax,dword ptr [ebp+8]0040F5DB mov dword ptr [ebp-4],eax0040F5DE mov ecx,dword ptr [ebp-4]0040F5E1 sub ecx,10040F5E4 mov dword ptr [ebp-4],ecx0040F5E7 cmp dword ptr [ebp-4],90040F5EB ja $L1232+0Fh (0040f697)0040F5F1 mov edx,dword ptr [ebp-4]0040F5F4 jmp dword ptr [edx*4+40F6B5h]
$L1214:0040F5FB push offset string "1" (00425054)0040F600 call printf (00401610)0040F605 add esp,40040F608 jmp $L1232+1Ch (0040f6a4)
$L1216:0040F60D push offset string "2" (00425050)0040F612 call printf (00401610)0040F617 add esp,40040F61A jmp $L1232+1Ch (0040f6a4)
$L1218:0040F61F push offset string "3" (0042504c)0040F624 call printf (00401610)0040F629 add esp,40040F62C jmp $L1232+1Ch (0040f6a4)
$L1220:0040F62E push offset string "4" (00425048)0040F633 call printf (00401610)0040F638 add esp,40040F63B jmp $L1232+1Ch (0040f6a4)
$L1222:0040F63D push offset string "5" (00425044)0040F642 call printf (00401610)0040F647 add esp,40040F64A jmp $L1232+1Ch (0040f6a4)
$L1224:0040F64C push offset string "6" (00425040)0040F651 call printf (00401610)0040F656 add esp,40040F659 jmp $L1232+1Ch (0040f6a4)
$L1226:0040F65B push offset string "E" (00424fcc)0040F660 call printf (00401610)0040F665 add esp,40040F668 jmp $L1232+1Ch (0040f6a4)
$L1228:0040F66A push offset string "A" (00424fbc)0040F66F call printf (00401610)0040F674 add esp,40040F677 jmp $L1232+1Ch (0040f6a4)
$L1230:0040F679 push offset string "9" (004241a4)0040F67E call printf (00401610)0040F683 add esp,40040F686 jmp $L1232+1Ch (0040f6a4)
$L1232:0040F688 push offset string "K" (00424194)0040F68D call printf (00401610)0040F692 add esp,40040F695 jmp $L1232+1Ch (0040f6a4)0040F697 push offset string "Error" (00424214)0040F69C call printf (00401610)
3.
switch(number)
{ case 1:printf("1");break; case 8:printf("8");break; case 9:printf("9");break; case 10:printf("10");break; default:printf("Error");break;
}040F5C0 push ebp0040F5C1 mov ebp,esp0040F5C3 sub esp,44h0040F5C6 push ebx0040F5C7 push esi0040F5C8 push edi0040F5C9 lea edi,[ebp-44h]0040F5CC mov ecx,11h0040F5D1 mov eax,0CCCCCCCCh0040F5D6 rep stos dword ptr [edi]0040F5D8 mov eax,dword ptr [ebp+8]0040F5DB mov dword ptr [ebp-4],eax0040F5DE mov ecx,dword ptr [ebp-4]0040F5E1 sub ecx,10040F5E4 mov dword ptr [ebp-4],ecx0040F5E7 cmp dword ptr [ebp-4],90040F5EB ja $L1220+0Fh (0040f63b)0040F5ED mov eax,dword ptr [ebp-4]0040F5F0 xor edx,edx0040F5F2 mov dl,byte ptr (0040f66d)[eax]0040F5F8 jmp dword ptr [edx*4+40F659h]
$L1214:0040F5FF push offset string "7" (00424fcc)0040F604 call printf (00401610)0040F609 add esp,40040F60C jmp $L1220+1Ch (0040f648)
$L1216:0040F60E push offset string "A" (00424fbc)0040F613 call printf (00401610)0040F618 add esp,40040F61B jmp $L1220+1Ch (0040f648)
$L1218:0040F61D push offset string "9" (004241a4)0040F622 call printf (00401610)0040F627 add esp,40040F62A jmp $L1220+1Ch (0040f648)
$L1220:0040F62C push offset string "K" (00424194)0040F631 call printf (00401610)0040F636 add esp,40040F639 jmp $L1220+1Ch (0040f648)0040F63B push offset string "Error" (00424214)0040F640 call printf (00401610)
4.
void Function()
{ int x = 0; int y = 3; do
{ printf("%dn",x);
x++;
} while(x<y);
}0040F5D8 mov dword ptr [ebp-4],00040F5DF mov dword ptr [ebp-8],30040F5E6 mov eax,dword ptr [ebp-4]//循环开始//调用printf0040F5E9 push eax0040F5EA push offset string "K" (00424194)0040F5EF call printf (00401610)0040F5F4 add esp,8//x++0040F5F7 mov ecx,dword ptr [ebp-4]0040F5FA add ecx,10040F5FD mov dword ptr [ebp-4],ecxwhile(x<y)0040F600 mov edx,dword ptr [ebp-4]0040F603 cmp edx,dword ptr [ebp-8]0040F606 jl Function+26h (0040f5e6)//进行比较如果不满足条件跳转
5.
void Function(int number)
{ int x = 0; int y = 3; while(x<y)
{ printf("%dn",x);
x++;
}
}0040F5D8 mov dword ptr [ebp-4],00040F5DF mov dword ptr [ebp-8],3//while(x < y)进行判断0040F5E6 mov eax,dword ptr [ebp-4]0040F5E9 cmp eax,dword ptr [ebp-8]0040F5EC jge Function+4Ah (0040f60a) //如果x>y就直接跳转到函数结尾//循环体内部,调用printf函数0040F5EE mov ecx,dword ptr [ebp-4]0040F5F1 push ecx0040F5F2 push offset string "K" (00424194)0040F5F7 call printf (00401610)0040F5FC add esp,8//x++0040F5FF mov edx,dword ptr [ebp-4]0040F602 add edx,10040F605 mov dword ptr [ebp-4],edx//此处跳到循环开始判断的位置,进行循环0040F608 jmp Function+26h (0040f5e6)0040F60A pop edi0040F60B pop esi0040F60C pop ebx
6.
void Function(int number)
{ int y = 3; for(int x=0;x < y;x++)
{ printf("%dn",x);
}
}0040F5D8 mov dword ptr [ebp-4],3//y = 30040F5DF mov dword ptr [ebp-8],0//x = 0//for语句开始,跳转到判断位置0040F5E6 jmp Function+31h (0040f5f1)//x++0040F5E8 mov eax,dword ptr [ebp-8]0040F5EB add eax,10040F5EE mov dword ptr [ebp-8],eax//x<y 进行判断,如果不满足条件直接跳到函数尾0040F5F1 mov ecx,dword ptr [ebp-8]0040F5F4 cmp ecx,dword ptr [ebp-4]0040F5F7 jge Function+4Ch (0040f60c)//for循环内部,调用printf0040F5F9 mov edx,dword ptr [ebp-8]0040F5FC push edx0040F5FD push offset string "K" (00424194)0040F602 call printf (00401610)0040F607 add esp,8//跳转到进行x++0040F60A jmp Function+28h (0040f5e8)0040F60C pop edi0040F60D pop esi0040F60E pop ebx
本文作者:星盟安全团队
本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/126376.html