首页 > 其他分享 >switch语句逆向分析

switch语句逆向分析

时间:2024-10-18 09:11:28浏览次数:1  
标签:语句 逆向 printf break case switch num 跳转 ptr

switch语句逆向分析

有序

小于3时

  • 代码:

#include "stdafx.h"

void MySwitch(int x){

switch(x) {

case 1:

printf("num is 1\n");

break;

case 2:

printf("num is 2\n");

break;

case 3:

printf("num is 3\n");

break;

default:

printf("no cases match\n");

break;

}

}

int main(int argc, char* argv[])

{

MySwitch(2);

return 0;

}

  • 反汇编:

10: switch(x) {

0040D7A8 mov eax,dword ptr [ebp+8]

0040D7AB mov dword ptr [ebp-4],eax

0040D7AE cmp dword ptr [ebp-4],1

0040D7B2 je MySwitch+32h (0040d7c2)

0040D7B4 cmp dword ptr [ebp-4],2

0040D7B8 je MySwitch+41h (0040d7d1)

0040D7BA cmp dword ptr [ebp-4],3

0040D7BE je MySwitch+50h (0040d7e0)

0040D7C0 jmp MySwitch+5Fh (0040d7ef)

11: case 1:

12: printf("num is 1\n");

0040D7C2 push offset string "num is 1\n" (00422fc4)

0040D7C7 call printf (00401060)

0040D7CC add esp,4

13: break;

0040D7CF jmp MySwitch+6Ch (0040d7fc)

14: case 2:

15: printf("num is 2\n");

0040D7D1 push offset string "num is 2\n" (00422fb8)

0040D7D6 call printf (00401060)

0040D7DB add esp,4

16: break;

0040D7DE jmp MySwitch+6Ch (0040d7fc)

17: case 3:

18: printf("num is 3\n");

0040D7E0 push offset string "num is 3\n" (00422fac)

0040D7E5 call printf (00401060)

0040D7EA add esp,4

19: break;

0040D7ED jmp MySwitch+6Ch (0040d7fc)

20: default:

21: printf("no cases match\n");

0040D7EF push offset string "Hello World!\n" (0042201c)

0040D7F4 call printf (00401060)

0040D7F9 add esp,4

22: break;

23: }

24: }

  • 反汇编分析:

1.反汇编代码为将参数x的值赋给eax

2.将eax的值放入堆栈中

3.将前面放入堆栈中的eax拿出来和第1个case中的条件进行比较(也就是比较参数x和case)

4.判断是否要跳转,je:jump equal,前面比较的两个数相同则跳转,跳转的地址为case 1对应的地址

5.如果没有跳转则继续将参数和第2个case中的条件进行比较

6.依旧是根据比较的结果判断是否要跳转,跳转的地址为case 2对应的地址

7.如果没有跳转则继续将参数和第3个case中的条件进行比较

8.依旧是根据比较的结果判断是否要跳转,跳转的地址为case 3对应的地址

9.如果没有跳转则绝对跳转到default:

下面的内容就是 case 1,case 2,case 3了

可以注意到,case里面的break都对应为跳出switch,而default里的break因为下面就已经是退出switch所以没有生成对应的汇编代码

case1里的break

case2里的break

case3里的break

通过上面的分析,发现此时(switch 中的case数量≤3时)的反汇编代码和if else并无本质上的区别,都是要依次比较判断条件

大于三时:(同理简化)

ja指令:jump above,大于时跳转(无符号),也就是比较参数x-1和3(case中的最大差值),最大差值就是最大值减最小值,此案例中就是4-1=3

如果x-1>3则跳转,如果前面参数没有减1的话,就变成了直接判断x>3,如果此时x=4也会产生跳转,不符合程序的逻辑(原本x=4应该对应跳转到case 4)

注意到这里采用的是无符号比较,而不采用有符号比较指令jg:jump greater,大于时跳转(有符号),为什么?

这里的比较代码其实就是判断参数是否在(case中的最小值,case中的最大值)这个区间内

当参数小于case中的最小值时,前面的sub ecx,case中的最小值就后就会产生下溢,此时将其看作无符号数就会相当大,一定会大于case中的最大差值,举个简单的例子,假如此时的参数为0,0-1 = -1对应的是十六进制为FFFF FFFF,将其看作无符号数就是4294967295

0040D7BB ja $L539+0Fh (0040d803)

跳转的地址为:0040d803,对应为default的地址

23: default:

24: printf("no cases match\n");

0040D803 push offset string "Hello World!\n" (0042201c)

0040D808 call printf (00401060)

0040D80D add esp,4

25: break;

如果前面没有跳转,这里则又将前面保存的参数-1的值取了出来,并赋值给edx

0040D7BD mov edx,dword ptr [ebp-4]

过上面的分析,发现此时(switch 中的case数量>3时)的反汇编代码和if else的差别就体现出来了,但到达一定条件后一般都会采用到case地址表首地址+偏移的方法,此时是将参数的值减case中的最小值,然后判断这个减完的数值是否大于case中的最大差值,如果大于则直接跳转到default,如果小于或等于则通过jmp [存储case地址表的首地址+偏移×4]的方式直接跳转到对应case的地址,而不再像if else中那样依次比较来判断是否要跳转

无序

  • 代码

switch(x) {

case 2:

printf("num is 2\n");

break;

case 3:

printf("num is 3\n");

break;

case 4:

printf("num is 4\n");

break;

case 1:

printf("num is 1\n");

break;

default:

printf("no cases match\n");

break;

}

简单地调换了一下case语句的顺序,观察其反汇编

  • 反汇编代码

10: switch(x) {

0040D7A8 mov eax,dword ptr [ebp+8]

0040D7AB mov dword ptr [ebp-4],eax

0040D7AE mov ecx,dword ptr [ebp-4]

0040D7B1 sub ecx,1

0040D7B4 mov dword ptr [ebp-4],ecx

0040D7B7 cmp dword ptr [ebp-4],3

0040D7BB ja $L539+0Fh (0040d803)

0040D7BD mov edx,dword ptr [ebp-4]

0040D7C0 jmp dword ptr [edx*4+40D821h]

11: case 2:

12: printf("num is 2\n");

0040D7C7 push offset string "num is 1\n" (00422fd0)

0040D7CC call printf (00401060)

0040D7D1 add esp,4

13: break;

0040D7D4 jmp $L539+1Ch (0040d810)

14: case 3:

15: printf("num is 3\n");

0040D7D6 push offset string "num is 2\n" (00422fc4)

0040D7DB call printf (00401060)

0040D7E0 add esp,4

16: break;

0040D7E3 jmp $L539+1Ch (0040d810)

17: case 4:

18: printf("num is 4\n");

0040D7E5 push offset string "num is 3\n" (00422fb8)

0040D7EA call printf (00401060)

0040D7EF add esp,4

19: break;

0040D7F2 jmp $L539+1Ch (0040d810)

20: case 1:

21: printf("num is 1\n");

0040D7F4 push offset string "num is 271\n" (00422fac)

0040D7F9 call printf (00401060)

0040D7FE add esp,4

22: break;

0040D801 jmp $L539+1Ch (0040d810)

23: default:

24: printf("no cases match\n");

0040D803 push offset string "no cases match\n" (0042201c)

0040D808 call printf (00401060)

0040D80D add esp,4

25: break;

26: }

27: }

EAX = 00000002 EBX = 7FFDB000

ECX = 00000001 EDX = 00000001

ESI = 00000000 EDI = 0012FF28

EIP = 0040D4C0 ESP = 0012FED8

EBP = 0012FF28 EFL = 00000293

内存

0040D521 D6 D4 40 00 衷@.

0040D525 C7 D4 40 00 窃@.

0040D529 F4 D4 40 00 粼@.

0040D52D E5 D4 40 00 逶@.

在值连续的情况下,顺序并不会影响生成大表

空缺地址通过填充default语句块地址解决,但会造成内存浪费

;大表

0040D8A6 1F D8 40 00 .谸.

0040D8AA 2E D8 40 00 .谸.

0040D8AE 3D D8 40 00 =谸.

0040D8B2 4C D8 40 00 L谸.

0040D8B6 5B D8 40 00 [谸.

0040D8BA 6A D8 40 00 j谸.

0040D8BE 79 D8 40 00 y谸.

0040D8C2 88 D8 40 00 堌@.

;小表

0040D8C6 00 01 02 07 ....

0040D8CA 07 07 03 07 ....

0040D8CE 04 07 07 07 ....

0040D8D2 05 07 07 06 ....

小表的解释:

当空缺值太多时内存的浪费也会变多,编译器当然知道这样不是办法,所以利用小表来解决这个问题。小表可以看作是一个智能蹦床,对于不同的玩家会给出不同的力,遇到没有付费的玩家(空缺值)直接将他抛出场外(给出参数,使其跳转到default的语句块),遇到付费玩家(存在的值)则按照他的等级给出不同的力(给出参数,使其跳转到其对应的语句块)

可以看出,在小表中所有的空缺值都是07(因为在这个样例中,当edx=7时[edx*4+40D8A6h]的地址为default语句块的地址),而存在的值的对应值从0递增。

标签:语句,逆向,printf,break,case,switch,num,跳转,ptr
From: https://www.cnblogs.com/maqun/p/18473453

相关文章

  • 常见逻辑语句逆向分析
    Ifelse语句逆向分析#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>intmain(intargc,char*argv[]){intsum=0;for(inti=0;i<=argc;i++){sum+=i;}returnsum;汇编代码00401006movdwordptr[ebp-8],0;sum=0......
  • ollydbg逆向基础
    实验目的理解编译过程和调试信息,了解debug模式和release模式的exe进行逆向分析的过程。尝试多种方法找到main函数。实验环境系统:Windows11软件:VS、ollydbg实验代码#include<stdio.h>intmain(){ printf("hellomaqun"); return0;}实验过程查找代码字符在debug文件夹拖拽ex......
  • C语句和程序流
    1.C语言表达式和语句在C中,表达式代表值,而语句代表给计算机的指令。表达式表达式由运算符和操作数组成。最简单的表达式只是一个不带运算符的常量或者变量,例如12或者num。复杂一些的例子是20+30和a=12。语句语句是对计算机的命令。任何以分号结尾的表达式都是一个语句,它不一定......
  • SQL语句——日期题目总结
    第一题:查询本周考试的学生成绩。 DATA_ADD()语法:date就是要操作的日期,INTERVAL就是要间隔的日期expr可以写数字,unit用来写单位,比如DATE_ADD(CURDATE(),INTERVAL7DAY)就是当前日期加上一星期。CURDATE()就是当前日期,格式:DATE_ADD(date,INTERVALexprunit)代码解释:就......
  • MySql和简单的sql语句
    安装数据库今天进行mysql的安装学习了简单sql语句mysql去官网安装mysql的社区版的八点几版本,安装之后需要设置密码,执行mysql-uroot-p,输入密码就可以进入mysql,使用exit;退出SQL语句分为DDL,DML,DQL,DCL,几大类,creatbasedata......
  • yield 语句 - 提供下一个元素
    yield语句-提供下一个元素项目2024/10/153个参与者反馈本文内容迭代器的执行C#语言规范另请参阅在迭代器中使用 yield 语句提供下一个值或表示迭代结束。 yield 语句有以下两种形式:yieldreturn:在迭代中提供下一个值,如以下示例所示:C#复制 运行f......
  • Dell-switch ios升级
    Dell-switchios升级1.showbootsystemstack-unitall查看目前ios的版本2.上传ios到A和B分区DellEMC#upgradesystemtftp:A:Addressornameofremotehost[]:172.29.8.33Sourcefilename[]:FTOS-Z9100-ON-9.14.1.5.binDellEMC#upgradesystemtftp:B:Addressor......
  • goto语句的风险
    在编程中,goto语句会使程序控制流跳转到指定的标签位置。尽管它在某些情况下可以简化代码(例如在错误处理或异常情况下快速退出多个嵌套的循环),但通常建议慎用甚至避免使用goto语句。主要原因如下:1. 破坏代码的结构化goto语句允许程序跳转到代码中的任意位置,从而打破了程序的结构......
  • C#中判断的应用说明二(switch语句)
    一.判断的定义说明判断结构要求程序员指定一个或多个要评估或测试的条件,以及条件为真时要执行的语句(必需的)和条件为假时要执行的语句(可选的)。下面是大多数编程语言中典型的判断结构的一般形式:二.判断语句C#提供了以下类型的判断语句,查看每个语句的细节。语句描述switch语......
  • 如何避免日志中打印SQL语句:完整解决方案
    个人名片......