本文为看雪论坛优秀文章
看雪论坛作者ID:彼岸风
这里强烈推荐老钱和老张写的《C++反汇编与逆向分析技术揭秘》,本文大部分知识点都将以此书作为参考,我个人是不太喜欢看直播和视屏教程的,因为有些关键知识点可能老师一句话就带过去了,要想回来看还得来回拉进度条,但是书不一样,遇到读不懂的地方可以停下来仔细思考,想回头看也就是翻几页的事情,遇到那种写的特别细的作者,那读起来更是一种享受。
编译速度优化
执行速度优化
程序体积优化
对于 Debug 版程序,编译器为了满足单步调试需求,不会对无意义的代码进行优化。无意义的意思是没有发生传递,没有赋值到内存空间。
一
常见的优化类型
int n = 2 + 3 * 6;
// 编译器在处理这段代码时会直接将变量赋予立即数+
// mov n, 20
int n = 2 + 3 * 6;
int m = n * 10;
// mov m, 200
int funtion1() {
int n = 2 + 3 * 6;
int m = n * 10;
return 0;
}
// 无意义的变量,这个函数被编译为汇编也将只有一句代码
// mov eax, 0
int funtion2() {
int n = 2 + 3 * 6;
int m = n * 10;
return m;
}
// 有意义的变量,但因为常量传播,也只有一句代码
// mov eax, 200
if(false) {
printf("you can't find me");
}
在书中还有更多优化示例,这里不做过多列举,其根本就是以上几种优化方式,无意义的代码将被删除,冗余的代码将会被精简,照着这种思路想就对了。得益于编译器的强大,使得再烂的代码也能保持高效。
二
常量为2的幂
乘法将会被替换为执行周期更短的移位指令。
int fun(int n) {
return n * 16;
}
// mov eax, n
// shl eax, 4
常量为非2的幂
我并不推荐你把自己当成编译器,看到算式想着怎么转成汇编,而是推荐记下这种算法,看到计算过程知道怎么转成原式,当然也不追求100%还原,逻辑一致即可。
n * 15 = n * 16 - n = n << 4 - n
n * 12 = n * 3 * 4 = (n << 1 + n) << 2
int value = n * 15;
// rsb.w r0, r1, r1, lsl #4
int value = n * 12;
// add.w r0, r1, r1, lsl #1
n * 4 + 5 = lea edx, [ecx * 4 + 5]
printf("%d", n * 4 + 5);
// mov ecx, n
// lea edx, [ecx * 4 + 5]
// push edx
int fun(int n, int m) {
return n * m;
}
// mov eax, n
// mov ecx, m
// imul ecx
符号问题
两个无符号整数相除,结果依然是无符号
两个有符号整数相除,结果依然是有符号
混除,参数全被当成无符号计算,结果是无符号
取整问题
向下取整 —— floor 函数 存在误差 => ( - a / b ) + ( a / b ) != - ( a / b ) - ( a / b )
向上取整 —— ceil 函数 存在误差 => ( - a / b ) != - ( a / b )
向零取整 —— 截断除法(Truncate),可以理解为放弃小数部分,只取整数部分,可以在任何情况保持恒等,大部分语言用的都是截断除法
大数(负数)
在无符号中,负数的值是很大的,例如 -8 = 0xFFFFFFF8。
UINT value = (UINT)n / -8;
// cmn.w r0, #9 ; cmp r0, -9
// it hi
// movhi r1, #1 ; n > -9 ? 1 : 0
2的幂
简单的移位
UINT value = (UINT)n / 4;
// lsrs r1, r0, #2
非2的幂
公式来源于《C++反汇编与逆向分析技术揭秘》,真的是非常非常的细,书中整个推导过程很完整,很建议各位去仔细研读一遍
printf("%u", (unsigned)argc / 3);
// mov eax, 0xAAAAAAAB ; M
// mul [argc] ; edx:eax = argc * M
// shr edx, 1 ; edx = argc * M >> 32 >> 1
// push edx
看雪ID:彼岸风
https://bbs.pediy.com/user-home-937323.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!