设为首页 加入收藏

TOP

5.4 switch的真相(4)
2013-10-07 14:30:19 来源: 作者: 【 】 浏览:55
Tags:5.4 switch 真相

5.4 switch的真相(4)

Release版与Debug版的反汇编代码基本一致,下面将在Release版中对这种结构进行实际分析,如代码清单5-14所示。

代码清单5-14 case语句的有序线性结构—Release版

  1. ; 取出switch语句的参数值并放入ecx中  
  2. 00401018    mov     ecx,dword ptr [esp+8]  
  3. 0040101C    add     esp,8   ; 平衡scanf函数的参数  
  4.  
  5. ; 将ecx减1后放入eax中,因为最小的case 1存放在case地址表中下标为0处,需要调整对齐到0下标,便于直接查表  
  6. 0040101F    lea     eax,[ecx-1]  
  7. ; 与6进行比较,有了这两步操作可以初步假设这里的代码是一个switch结构  
  8. ; 无符号比较,大于6时跳转到地址0x00401086处  
  9. 00401022    cmp     eax,6  
  10. 00401025    ja      00401086  
  11. ; 下面的指令体现了switch的第二个特性:查表(case地址表)  
  12. ; 可以确认这是一个switch结构  
  13. ; 上一步的跳转地址00401086就是switch结尾或者是default语句块的首地址  
  14. ; 下面指令中的地址0x00401088便是case线性地址表的首地址  
  15. ; 可参考图5-5  
  16. 00401027    jmp     dword ptr [eax*4+401088h]  
  17. ; 地址0x0040706C为字符串"nIndex == 1"的首地址  
  18. ; 此处为第一个case语句块的地址  
  19. 0040102E    push        40706Ch  
  20. ; 此处调用printf函数  
  21. 00401033    call        004012F0  
  22. ; 平衡printf函数破坏的栈空间  
  23. 00401038    add     esp,4  
  24. ; 还原esp  
  25. 0040103B        pop     ecx  
  26. ; 返回,在Release版中,编译器发现switch后什么也没做就直接返回,所以  
  27. ; 将每句break优化为了return  
  28. ; 到此处,第一个case语句块结束,顺序向下为第二个case语句块  
  29. 0040103C        ret  
  30. ; 第二个case语句块  
  31. ; 以下代码相似,除地址外,不再进行注释,请读者自行分析  
  32. ; 地址0x00407060为字符串"nIndex == 2"的首地址  
  33. 0040103D    push        407060h  
  34. 00401042    call        004012F0  
  35. 00401047    add     esp,4  
  36. 0040104A    pop     ecx  
  37. 0040104B    ret  
  38. ; 第三个case语句块  
  39. ; 地址0x00407054为字符串"nIndex == 3"的首地址  
  40. 0040104C    push        407054h  
  41. 00401051    call        004012F0  
  42. 00401056    add     esp,4  
  43. 00401059    pop     ecx  
  44. 0040105A    ret  
  45. ; 第四个case语句块  
  46.  
  47. ; 地址0x00407048为字符串"nIndex == 5"的首地址  
  48. 0040105B    push        407048h  
  49. 00401060    call        004012F0  
  50. 00401065    add     esp,4  
  51. 00401068    pop     ecx  
  52. 00401069    ret  
  53. ; 第五个case语句块  
  54. ; 地址0x0040703C为字符串"nIndex == 6"的首地址  
  55. 0040106A    push        40703Ch  
  56. 0040106F    call        004012F0  
  57. 00401074    add     esp,4  
  58. 00401077    pop     ecx  
  59. 00401078    ret  
  60. ; 第六个case语句块  
  61. ; 地址0x00407030为字符串"nIndex == 7"的首地址  
  62. 00401079    push        407030h  
  63. 0040107E    call        004012F0  
  64. 00401083    add     esp,4  
  65. 00401086    pop     ecx  
  66. 00401087    ret 

所有的case语句块都已找到,接下来将每个case的标号值进行还原。如何得到这个标号值呢?很简单,只要找到case线性地址表即可。此示例的case线性地址表的首地址为0x00401088,如图5-5所示。

 
图5-5  switch的有序线性case地址表

case线性地址表是一个有序表,在switch语句块中有减1操作,地址表是以0为下标开始,那么表中的第0项对应的case标号值应为(0+1=1)1,地址0x0040102E处为“case 1”。后续case语句块按此方案,请读者进行依次还原。

总结:

  1. mov         reg, mem    ; 取变量  
  2. ; 对变量进行运算,对齐case地址表的0下标,非必要  
  3. ; 上例中的eax也可用其他寄存器替换,这里也可以是其他类型的运算  
  4. lea         eax, [reg+xxxx]  
  5. ; 影响标志位的指令,进行范围检查  
  6. jxx         DEFAULT_ADDR  
  7. jmp         dword ptr [eax*4+xxxx]; 地址xxxx为case地址表的首地址 

当遇到这样的代码块时,可获取某一变量的信息并对其进行范围检查,如果超过case的最大值,则跳转条件成立 ,跳转目标指明了switch语句块的末尾或者是default块的首地址。条件跳转后紧跟jmp指令,并且是相对比例因子寻址方式,且基址为地址表的首地址,说明此处是线性关系的switch分支结构。对变量做运算,使对齐到case地址表0下标的代码不一定存在(当case的最小值为0时)。根据每条case地址在表中的下标位置,即可反推出线性关系的switch分支结构原型。
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇5.2 if…else…语句(1) 下一篇5.4 switch的真相(3)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: