iOS开发逆向之循环选择指针(下)
本文主要讲解Switch
的汇编代码
Switch
1、假设
switch
语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else
2、各个
分支常量的差值较大
时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else的结构3、在
分支比较多
的时候,在编译的时候会生成一个表
,不同的case通过跳转表的不同地址,每个地址占四个字节。
案例分析
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130595548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
1、当case有3个时
void func(int a){ switch (a) { case 1: printf("打坐"); break; case 2: printf("加红"); break; case 3: printf("加蓝"); break; default: printf("啥也不干"); break; } }int main(int argc, char * argv[]) { func(1);
断点调试func
其中3个case的汇编是以
if-else
的形式
2、如果是4个case呢?
void func(int a){ switch (a) { case 1: printf("打坐"); break; case 2: printf("加红"); break; case 3: printf("加蓝"); break; case 4: printf("打怪"); break; default: printf("啥也不干"); break; } }int main(int argc, char * argv[]) { func(1); }
此时
w8
就是传入的参数,即w8=4
subs w8, w8, #0x1
:subs会影响目标寄存器,即影响w8(注:但不执行汇编命令:ni
)ubfx x9, x9, #0, #32
:将x9高32位清零
,下面通过修改x9寄存器的值来验证:确实是将高32位清零(从x9第0位开始,保留低32位,高位用0补齐)下面是实际操作后的结果
执行
cmp x9 #0x4
~ldr x11,[sp]
,在这里是x9和4比较,判断是否等于。此时的结果是不等于则继续往下执行ldrsw x10, [x8, x11, lsl #2]
(先运算后面,再运算前面):将x8的值加上x11(需要先将x11左移2位,即0x3左移2位,为1100,为12)作为地址,然后将地址的值给x10,此时x10的值就是-24 ????(即0xffffffffffffffe8)执行
ldrsw x10, [x8, x11, lsl #2]
后的x10从
x8 = 0x0000000102e22828
与最后0x102e22824
对比,可以看出,x8的地址是func执行完后下一句代码的地址add x9, x8, x10
:x8的地址加上x10偏移(由于x10为负数,所以是x8-x10
),给到x9.即x9的值为0x102e22828-0x18(24的十六进制)=0x102e22810
,即到下面这句,这里就是走到default
执行到
ldp x29, x30, [sp, #0x10]
,输出啥也不干(lldb)
3、修改案例
void func(int a){ switch (a) { case 5: printf("打坐"); break; case 6: printf("加红"); break; case 7: printf("加蓝"); break; case 8: printf("打怪"); break; default: printf("啥也不干"); break; } }int main(int argc, char * argv[]) { func(4); }
其汇编如下,发现subs
减的是5
汇编代码分析如下
4、修改2:将第二个case修改为2
void func(int a){ switch (a) { case 5: printf("打坐"); break; case 2: printf("加红"); break; case 7: printf("加蓝"); break; case 8: printf("打怪"); break; default: printf("啥也不干"); break; } }
汇编如下,从这里可以发现,subs后减的是最小的case
疑问1:其中的
cmp x9,#0x6
,这里的0x6又是怎么来的呢?其实是最小case与最大case(即8-2)的差值
,疑问2:为什么要这么比较呢?主要是为了确认传入的参数
是否在[最小case,最大case]这个区间内
,如果不在,则走default
疑问3:为什么是无符号?因为如果传入比最小case小的值,例如传入的是1,得到的是一个负数,
如果是
有符号数
运算,一定比区间值小如果是
无符号数
运算,是一个非常大的数,一定超过了范围,直接走到default
分析
代码中有一张表,表中有7个字节,都是负数,为什么是7个?因为8-2=6,加上default,所以是7个。
以下是switch表中的7个字节
switch分支的代码是连续的
结论:表中的字节数,取决于
最大case-最小case+1(1表示default)
目的:为什么这么创建?
用空间换时间
,为了让代码效率更高ldrsw
为什么是左移2位?因为一个字节是4位,便于查找下一个case的值(x11 是 参数-最小case的值)
switch总结
1、先将参数减去最小case
2、先判断是否是default,如果不是,则在范围之内,可以通过表直接拿到地址,反之则default
疑问:表中为什么不直接存地址,而是存差值?
- 1)地址太长了
- 2)!!地址在运行时期才会知道虚拟地址(即ASLR),如果存的是偏移地址,使用时需要加上ASLR
如果case很大呢?
- 只要是连续的就行,因为汇编会减去最小case
- 如果case之间相差太大了,编译器会进行优化,变成if-else
void func(int a){ switch (a) { case 1: printf("打坐"); break; case 400: printf("加红"); break; case 800: printf("加蓝"); break; case 8: printf("打怪"); break; default: printf("啥也不干"); break; } }int main(int argc, char * argv[]) { func(4); }
首先作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130595548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
总结
1、假设
switch
语句的分支比较少时(例如3,少于4的时候没有意义),没有必要使用次结构,相当于if-else
2、各个分支常量的
差值较大
时,编译器会在效率还是内存进行取舍,这时编译器还是会编译成类似于if-else
的结构3、在
分支比较多
的时候,在编译的时候会生成一个表
(跳转表每个地址四个字节)。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
关于ssd存储原理的介绍
众所周知SSD的读写速度远比hdd磁盘要快,理解ssd的工作原理使我们开发处高效储存方案。 linux 相关指令 fstrim--fstab--verbose##回收(discard)文件系统上对应磁盘未使用的块 blkdiscard/dev/nvme1n1##回收并擦除(discard)整个SSD块设备 wipefs-a/dev/nvme1n1##擦除(erase)文件系统的签名 fstrim fstrim命令实际可以视为手动对SSD磁盘下发TRIM指令。使用-v参数可以直接查看TRIM回收擦除空间的大小。fstrim是针对已挂载的文件系统的SSD分区 root@xxxx:~#fstrim--help Usage: fstrim[options]<mountpoint>Discardunusedblocksonamountedfilesystem. Options: -a,--alltrimallsupportedmountedfilesystems -A,--fstabtrimallsupportedmountedfilesystemsfrom/etc/fstab -...
- 下一篇
iGcm Release 1.2.0 (2021-05-07)
Gcm 1.2.0版本发布了!GitHub Milestone 感谢以下贡献者: qianmoQ 24kGarry General changes 支持Quartz组件 支持Kafka组件 重构组件目录结构 Netty changes 修复中文乱码问题 修复由于同一路径上有多个请求而无法跳转的问题issues-35 支持静态文件渲染issues-36
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Hadoop3单机部署,实现最简伪集群
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS关闭SELinux安全模块
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 2048小游戏-低调大师作品