switch语句详细逆向分析

news2024/10/7 14:29:53

首先需要明确一点switch语句在游戏当中至关重要,而且基本都会使用它来提高效率!

因为我们在找call的时候,如果能够识别出来switch语句,只要找到一个call,后面的就都搞定了

switch:case必须是整数,也必须是常量

拦截服务器发回来的数据包,找到加红,就找到了其他的一连串技能函数

switch当中几个需要注意的细节:

1、如果没有匹配到就直接跳出break

3、不加break就从匹配的case一直往后执行,直到遇到break为止

ebp-4 -8是局部变量,ebp+8 +C是函数的参数

1、当分支条件比较少的时候,switch和if else效率相当:

前面的部分都是一样的函数调用流程: 

当case的个数很少的时候,不会生成大表,和if..else语句没有任何区别 

n是传进来的函数的参数:n=3

006E1845  mov         eax,dword ptr [n]  
006E1848  mov         dword ptr [ebp-0C4h],eax  
006E184E  cmp         dword ptr [ebp-0C4h],1  
006E1855  je          __$EncStackInitStart+3Fh (06E186Bh)  
006E1857  cmp         dword ptr [ebp-0C4h],2  
006E185E  je          __$EncStackInitStart+4Eh (06E187Ah)  
006E1860  cmp         dword ptr [ebp-0C4h],3  
006E1867  je          __$EncStackInitStart+5Dh (06E1889h)  
006E1869  jmp         __$EncStackInitStart+6Ah (06E1896h)  
006E186B  push        offset string "111\n" (06E7B30h)  
006E1870  call        _printf (06E10CDh)  
006E1875  add         esp,4  
006E1878  jmp         __$EncStackInitStart+6Ah (06E1896h)  
006E187A  push        offset string "222\n" (06E7B38h)  
006E187F  call        _printf (06E10CDh)  
006E1884  add         esp,4  
006E1887  jmp         __$EncStackInitStart+6Ah (06E1896h)  
006E1889  push        offset string "111\n" (06E7B30h)  
006E188E  call        _printf (06E10CDh)  
006E1893  add         esp,4 
006E1896  pop         edi  
006E1897  pop         esi  
006E1898  pop         ebx  
006E1899  add         esp,0C4h  
006E189F  cmp         ebp,esp  
006E18A1  call        __RTC_CheckEsp (06E1244h)  
006E18A6  mov         esp,ebp  
006E18A8  pop         ebp  
006E18A9  ret  

 2、当case的数量超过一定的数值之后,就会生成大表:

这里传进来的n=3

00651845  mov         eax,dword ptr [n]  
00651848  mov         dword ptr [ebp-0C4h],eax  
0065184E  mov         ecx,dword ptr [ebp-0C4h]  
00651854  sub         ecx,1  
00651857  mov         dword ptr [ebp-0C4h],ecx  
0065185D  cmp         dword ptr [ebp-0C4h],3  
00651864  ja          $LN7+0Dh (06518ADh)  
00651866  mov         edx,dword ptr [ebp-0C4h]  
0065186C  jmp         dword ptr [edx*4+6518C4h]
00651873  push        offset string "111\n" (0657B30h)  
00651878  call        _printf (06510CDh)  
0065187D  add         esp,4  
00651880  jmp         $LN7+0Dh (06518ADh)  
00651882  push        offset string "222\n" (0657B38h)  
00651887  call        _printf (06510CDh)  
0065188C  add         esp,4  
0065188F  jmp         $LN7+0Dh (06518ADh)  
00651891  push        offset string "333\n" (0657B40h)  
00651896  call        _printf (06510CDh)  
0065189B  add         esp,4  
0065189E  jmp         $LN7+0Dh (06518ADh)  
006518A0  push        offset string "444\n" (0657B48h)  
006518A5  call        _printf (06510CDh)  
006518AA  add         esp,4  
006518AD  pop         edi  
006518AE  pop         esi  
006518AF  pop         ebx  
006518B0  add         esp,0C4h  
006518B6  cmp         ebp,esp  
006518B8  call        __RTC_CheckEsp (0651244h)  
006518BD  mov         esp,ebp  
006518BF  pop         ebp  
006518C0  ret 

先把ecx-1和第二大的值比较,如果大于就直接跳转到default

那么为什么要先让ecx-1然后去和倒数第二大的数比较,这不是多此一举吗?直接使用ecx和最大的数进行比较他不香吗?😂😂😂,这里先留个悬念,供大家思考。

否则就跳转到 dword ptr [edx*4+6518c4]的位置 

那么6518C4的位置存的什么呢?

如图所示,他存储的是651873,也就是我们需要跳转的地址

你使用edx*4一次跳4个字节,对应的正好是这个case在大表里的位置

这样,你一次比较都不需要,直接就可以用edx*4+651873查表来跳转

到这里,你应该明白为什么ecx要-1了吧?》不就是为了定位它在大表当中的位置吗!

3、交换case的顺序,改成4 2 3 1 😂😂🤣,观察大表的生成是否会发生变化?

009F1845  mov         eax,dword ptr [n]  
009F1848  mov         dword ptr [ebp-0C4h],eax  
009F184E  mov         ecx,dword ptr [ebp-0C4h]  
009F1854  sub         ecx,1  
009F1857  mov         dword ptr [ebp-0C4h],ecx  
009F185D  cmp         dword ptr [ebp-0C4h],3  
009F1864  ja          $LN7+0Dh (09F18ADh)  
009F1866  mov         edx,dword ptr [ebp-0C4h]  
009F186C  jmp         dword ptr [edx*4+9F18C4h]
009F1873  push        offset string "444\n" (09F7B30h)  
009F1878  call        _printf (09F10CDh)  
009F187D  add         esp,4  
009F1880  jmp         $LN7+0Dh (09F18ADh)  
009F1882  push        offset string "222\n" (09F7B38h)  
009F1887  call        _printf (09F10CDh)  
009F188C  add         esp,4  
009F188F  jmp         $LN7+0Dh (09F18ADh)  
009F1891  push        offset string "333\n" (09F7B40h)  
009F1896  call        _printf (09F10CDh)  
009F189B  add         esp,4  
009F189E  jmp         $LN7+0Dh (09F18ADh)  
009F18A0  push        offset string "111\n" (09F7B48h)  
009F18A5  call        _printf (09F10CDh)  
009F18AA  add         esp,4  
009F18AD  pop         edi  
009F18AE  pop         esi  
009F18AF  pop         ebx  
009F18B0  add         esp,0C4h  
009F18B6  cmp         ebp,esp  
009F18B8  call        __RTC_CheckEsp (09F1244h)  
009F18BD  mov         esp,ebp  
009F18BF  pop         ebp  
009F18C0  ret  

 

 

比如你是第一个case 1,edx就是0,然后跳转到f918a0,执行的正好是printf("111")

可以看到switch语句和顺序没有关系,大表中存放的地址是按照你数值的大小来排列的,也就是说你把case 1写后面,它还是在大表的第一个位置存储的,编译器会先帮你排序,然后再把地址放到大表里面存储。

因此对于上面大表里地址的内容如下:

9f18a0:printf("111")

9f1882:printf("222")

9f1891:printf("333")

9f1873:printf("444")

还是按照从小到达的顺序跳转的

由于我们论证了switch当中case的顺序不会影响大表的顺序,所有以后都使用默认排序。

4、当case的数值很大的时候还会生成大表吗?

00DE1845  mov         eax,dword ptr [n]  
00DE1848  mov         dword ptr [ebp-0C4h],eax  
00DE184E  mov         ecx,dword ptr [ebp-0C4h]  
00DE1854  sub         ecx,65h  
00DE1857  mov         dword ptr [ebp-0C4h],ecx  
00DE185D  cmp         dword ptr [ebp-0C4h],3  
00DE1864  ja          $LN7+0Dh (0DE18ADh)  
00DE1866  mov         edx,dword ptr [ebp-0C4h]  
00DE186C  jmp         dword ptr [edx*4+0DE18C4h]
00DE1873  push        offset string "111\n" (0DE7B30h)  
00DE1878  call        _printf (0DE10CDh)  
00DE187D  add         esp,4  
00DE1880  jmp         $LN7+0Dh (0DE18ADh)  
00DE1882  push        offset string "222\n" (0DE7B38h)  
00DE1887  call        _printf (0DE10CDh)  
00DE188C  add         esp,4  
00DE188F  jmp         $LN7+0Dh (0DE18ADh)  
00DE1891  push        offset string "333\n" (0DE7B40h)  
00DE1896  call        _printf (0DE10CDh)  
00DE189B  add         esp,4  
00DE189E  jmp         $LN7+0Dh (0DE18ADh)  
00DE18A0  push        offset string "111\n" (0DE7B48h)  
00DE18A5  call        _printf (0DE10CDh)  
00DE18AA  add         esp,4  
00DE18AD  pop         edi  
00DE18AE  pop         esi  
00DE18AF  pop         ebx  
00DE18B0  add         esp,0C4h  
00DE18B6  cmp         ebp,esp  
00DE18B8  call        __RTC_CheckEsp (0DE1244h)  
00DE18BD  mov         esp,ebp  
00DE18BF  pop         ebp  
00DE18C0  ret  

 

可以看到仍然可以生成大表,只需要ecx-65就可以定位到元素在大表当中的位置了

由此可以论证只要是连续的/比较接近的数值,可以生成大表

5、那么离散的情况呢?

我们测试101 104 105 106的情况,即中间缺少了2个连续的项

00811845  mov         eax,dword ptr [n]  
00811848  mov         dword ptr [ebp-0C4h],eax  
0081184E  mov         ecx,dword ptr [ebp-0C4h]  
00811854  sub         ecx,65h  
00811857  mov         dword ptr [ebp-0C4h],ecx  
0081185D  cmp         dword ptr [ebp-0C4h],5  
00811864  ja          $LN7+0Dh (08118ADh)  
00811866  mov         edx,dword ptr [ebp-0C4h]  
0081186C  jmp         dword ptr [edx*4+8118C4h]

 

 我们发现它依然会生成大表,而且空缺的那两个不连续的项,编译器不是填0,而是填上default的跳转地址。

6、如果只保留头和尾,把中间项都删掉呢?

即:401 409,中间都删除了:

00411845  mov         eax,dword ptr [n]  
00411848  mov         dword ptr [ebp-0C4h],eax  
0041184E  mov         ecx,dword ptr [ebp-0C4h]  
00411854  sub         ecx,65h  
00411857  mov         dword ptr [ebp-0C4h],ecx  
0041185D  cmp         dword ptr [ebp-0C4h],9  
00411864  ja          $LN7+0Dh (04118B4h)  
00411866  mov         edx,dword ptr [ebp-0C4h]  
0041186C  movzx       eax,byte ptr [edx+4118DCh]  
00411873  jmp         dword ptr [eax*4+4118C8h]

我们需要注意这一行代码:

00411866  mov         edx,dword ptr [ebp-0C4h]  
0041186C  movzx       eax,byte ptr [edx+4118DCh]  
00411873  jmp         dword ptr [eax*4+4118C8h]

 

大表下面的这个按照字节进行检索的就是小表。

当删除到一定程度的时候,就把大表精简了,把default部分删除掉,使用小表来辅助:

小表就是紧挨着大表下面存储的,可以节省空间,因为它是按照字节来存储跳转的default地址的,可以看到它存的都是04,用来跳转到default,节省一些空间。

7、毫不连续的情况:

120 45 310 88

肯定就是按照if..else的方式来生成汇编代码,因为你不管是大表小表都对性能提升没有上面意义,所以直接转化成if..else语句即可。

总结:什么情况下使用switch?

》越连续连续;越相近越好;常量最好是连续挨在一起!

至此,我们完成了今天的逆向任务!喜欢的话多多点赞关注吧!🧡🧡🧡

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/622018.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#,码海拾贝(37)——求解“托伯利兹方程组“的“列文逊方法“之C#源代码

using System; namespace Zhou.CSharp.Algorithm { /// <summary> /// 求解线性方程组的类 LEquations /// 原作 周长发 /// 改编 深度混淆 /// </summary> public static partial class LEquations { /// <summary> /…

【高危】Linux kernel Netfilter UAF漏洞(POC公开)

漏洞描述 Netfilter 是 Linux kernel 的一个子系统&#xff0c;用于提供网络数据包过滤和网络地址转换功能。 Linux kernel 6.3.1及之前版本中&#xff0c;当 Netfilter 处理批量请求以更新 nf_tables 配置时&#xff0c;由于对匿名集合的操作处理不当会导致use-after-free&a…

【Java】Java核心要点总结:61

文章目录 1. java中的线程池是如何实现的2. 创建线程池的几个核心参数3. Java 中线程池的执行流程4. 为什么要使用线程池5. 线程池的拒绝策略 1. java中的线程池是如何实现的 Java 中的线程池是通过 ThreadPoolExecutor 类实现的。ThreadPoolExecutor 继承自 AbstractExecutorS…

chatgpt赋能python:如何快速复制Python库到其他电脑

如何快速复制Python库到其他电脑 作为一名拥有10年Python编程经验的工程师&#xff0c;我深知Python库在项目开发中扮演着非常重要的角色。Python库能够帮助我们快速实现功能、减少重复工作以及提高代码质量。但是&#xff0c;在换电脑或在新的团队合作时&#xff0c;我们常常…

【PWN · ret2libc | Canary】[2021 鹤城杯]littleof

最近比较忙&#xff0c;这道题用了好长时间来debug&#xff0c;甚至贡献了第一次在csdn上提问。。。 目录 前言 一、题目重述&思路分析 二、exp 三、Canary 四、萌新遇到的困难 总结 前言 Canary作为经典且基本的栈保护措施&#xff0c;在后期的题目中必然是基本标…

STL——string模拟实现(一)

目录 构造函数的实现 拷贝构造 赋值重载 const问题 迭代器打印 范围for打印 运算符重载 reserve模拟 插入数据 push_back append 构造函数的实现 先贴出一段错误代码&#xff1a; #include<iostream> #include<assert.h> namespace zzl//避免与库冲突 {…

Servlet 详解

目录 什么是 servlet? Servlet 是做甚的? 如何编写一个 Servlet 程序? 解析访问出错情况 Servlet 的运行原理 1. 接收请求 2. 根据请求计算响应 3. 返回响应 Servlet API 详解 HTTPServlet HttpServletRequset HttpServletResponse 什么是 servlet? Servlet 是…

String模拟实现(二)

resize resize的特点是扩容加初始化&#xff0c;如果所给的长度小于空间大小就会删除多余的数据。前面我们实现了reserve&#xff0c;但有这样一个问题&#xff0c;如果reserve的长度小于空间就会导致缩容&#xff0c;而我们知道&#xff0c;string中缩容用的是shrink_to_fit&a…

外观设计模式解读

目录 问题引进 传统方式解决影院管理 外观模式基本介绍 概念 外观模式原理类图 分类外观模式的角色 外观模式解决影院管理 传统方式解决影院管理说明 外观模式应用实例 外观模式的注意事项和细节 s统的内部细节 > 外观模式 外观模式基本介绍 概念 1) 外观模式&…

XGBoost的介绍

一、XGBoost的介绍 1.什么是XGBoost&#xff1f; XGBoost&#xff08;eXtreme Gradient Boosting&#xff09;是一种基于梯度提升树的机器学习算法&#xff0c;它在解决分类和回归问题上表现出色。它是由陈天奇在2014年开发的&#xff0c;如今已成为机器学习领域中最流行和强…

集合框架知识汇总

集合框架 集合 概念&#xff1a;对象的容器&#xff0c;定义了对多个对象进行操作的常用方法。可以实现数组功能 和数组的区别 数组长度固定&#xff0c;集合长度不固定 数组可以存储基本类型和引用类型&#xff0c;集合只能存储引用类型 总结 List集合 有序&#xff0c;有…

软考网工易混淆知识点总结(持续更新中,按照知识点先后排序)

1.数据编码--原码、反码和补码 原码 数值前面增加了一位符号位(即最高位为符号位)&#xff0c;该位为0时表示正数&#xff0c;为1时则表示负数&#xff0c;其余各位表示数值的大小反码 正数的反码与原码相同&#xff0c;负数的反码符号位为1&#xff0c;其余各位为该数绝对值的…

大型语言模(LLM) : 提示词工程(一)

今天我学习了DeepLearning.AI的 Prompt Engineering 的在线课程&#xff0c;我想和大家一起分享一下该门课程的一些主要内容。 下面是我们访问大型语言模(LLM)的主要代码&#xff1a; import openaiopenai.api_key XXXXXXXXXdef get_completion(prompt, model"gpt-3.5-…

高性能分布式API网关Kong

目录 1 kong网关简介2 为什么需要 API 网关2.1 和Spring Cloud Gateway区别 3 为什么要使用kong3.1 kong的组成部分3.2 Kong网关的特性 4 kong网关架构4.1 Kong网关请求流程 5 kong 部署5.1 搭建网络5.2 搭建数据库环境5.3 kong网关部署5.3.1 初始化kong数据5.3.2 启动Kong容器…

【项目】树莓派4B镜像安装

本文主要记录下如何使用Raspberry Pi image 软件进行树莓派镜像进行安装。 官网&#xff1a;Raspberry Pi OS – Raspberry Pi 百度网盘&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1G7z1Fdvk5Chmhj894WPU3A 提取码&#xff1a;xnzw 一、格式化SD卡 若SD卡存在…

释放潜能——解读新时代OEM竞争规则,打造精雕细琢的用户体验

从消费零售全领域的实践观察来看&#xff0c;仅仅凭借产品赢得竞争的时代已经过去&#xff0c;商业模式创新体现在越来越多企业向“产品服务”转变&#xff0c;向用户全生命周期需求挖掘转变。企业与消费者之间的关系从过去的一次性、断点式产品交易&#xff0c;转向持续性、覆…

读写ini配置文件(C++)

文章目录 1、为什么要使用ini或者其它(例如xml,json)配置文件&#xff1f;2、ini文件基本介绍3、ini配置文件的格式4、C读写ini配置文件5、 代码示例6、 配置文件的解析库 文章转载于&#xff1a;https://blog.csdn.net/weixin_44517656/article/details/109014236 1、为什么要…

基于 Web 和 Deep Zoom 的高分辨率大图查看器的实践

基于 Web 和 Deep Zoom 的高分辨率大图查看器的实践 高分辨率大图像在 Web 中查看可以使用 Deep Zoom 技术&#xff0c;这是一种用于查看和浏览大型高分辨率图像的技术&#xff0c;它可以让用户以交互方式浏览高分辨率大图像&#xff0c;并且能够在不影响图像质量的情况下进行…

搭建个人网站 保姆级教程(四)Navicat链接mySql 失败

长时间没有折腾云服务器上的mysql了&#xff0c;今天再次使用Navicat连接云服务器上的mysql时&#xff0c;输入密码报错&#xff01; 1130 - Host ‘119.130.212.168’ is not allowed to connect to this MySQL server 1.于是Royal TSX 远程服务器查看mysql的状态 systemctl …

通达信标记文字中可能用到的特殊符号大全

特殊符号是难以直接输入的符号&#xff0c;特殊符号是符号的一种&#xff0c;比如说圆圈&#xff08;〇&#xff09;、叉号&#xff08;✕、✖、✘&#xff09;、五角星&#xff08;★、☆&#xff09;、勾号&#xff08;✓、✔&#xff09; 。 01.特殊符号简表 ♠♣♧♡♥❤…