代码逆向(六)——加法与减法的识别与优化原理
2010-08-12 10:18
232 查看
加法的优化相对来说比较简单,只有3种优化方案,下面我们就以一个简单的例子来说明这三个问题,先看源码:
int _tmain(int argc, _TCHAR* argv[])
{
int nNum, nA = 8;
nNum = argc + nA; // 形式1
printf("%d/r/n",nNum);
nNum = argc + 9; // 形式2
printf("%d/r/n",nNum);
nNum = nNum + 1; // 形式3
printf("%d/r/n",nNum);
return 0;
}
Debug版反汇编代码:
.text:0041301E mov [ebp+nA], 8 ; nA = 8
.text:00413025 mov eax, [ebp+argc] ; eax=argc
.text:00413028 add eax, [ebp+nA] ; eax=eax+nA <-- !!
.text:0041302B mov [ebp+nNum], eax ; nNum=eax
.text:00413030 mov eax, [ebp+nNum] ; eax=nNum=argc+nA
.text:00413033 push eax
.text:00413034 push offset Format ; "%d/r/n"
.text:00413039 call ds:__imp__printf
.text:0041303F add esp, 8
.text:00413049 mov eax, [ebp+argc]
.text:0041304C add eax, 9 ; eax=argc+9 <-- !!
.text:0041304F mov [ebp+nNum], eax
.text:00413054 mov eax, [ebp+nNum]
.text:00413057 push eax
.text:00413058 push offset Format ; "%d/r/n"
.text:0041305D call ds:__imp__printf
.text:00413063 add esp, 8
.text:0041306D mov eax, [ebp+nNum]
.text:00413070 add eax, 1 ; eax=nNum+1 <-- !!
.text:00413073 mov [ebp+nNum], eax
.text:00413078 mov eax, [ebp+nNum]
.text:0041307B push eax
.text:0041307C push offset Format ; "%d/r/n"
.text:00413081 call ds:__imp__printf
.text:00413087 add esp, 8
通过上面的反汇编代码我们可以看到三个在普通不过的加法计算,下面我们再看看Release版的反汇编代码:
.text:00401000 _main proc near ; CODE XREF: __tmainCRTStartup+10Ap
.text:00401000
.text:00401000 argc= dword ptr 4
.text:00401000 argv= dword ptr 8
.text:00401000 envp= dword ptr 0Ch
.text:00401000
.text:00401000 push esi
.text:00401001 mov esi, ds:__imp__printf
.text:00401007 push edi
.text:00401008 mov edi, [esp+8+argc]
.text:0040100C lea eax, [edi+8] ; 优化后的加法
.text:0040100F push eax
.text:00401010 push offset Format ; "%d/r/n"
.text:00401015 call esi ; __imp__printf
.text:00401017 add edi, 9 ; 此处并没有什么优化
.text:0040101A push edi
.text:0040101B push offset Format ; "%d/r/n"
.text:00401020 call esi ; __imp__printf
.text:00401022 inc edi ; 优化后的加法
.text:00401023 push edi
.text:00401024 push offset Format ; "%d/r/n"
.text:00401029 call esi ; __imp__printf
.text:0040102B add esp, 18h
.text:0040102E pop edi
.text:0040102F xor eax, eax
.text:00401031 pop esi
.text:00401032 retn
.text:00401032 _main endp
由上面两段反汇编代码我们可以总结出加法计算的以下优化方案:
变量+变量 = lea Exx,[变量+变量]
变量+常量 = add 变量+常量
变量+1 = inc 变量
1.7.2、减法的识别与优化技巧
减法的优化与加法大同小异,基本相同,因此与加法相同的地方笔者在本文中将不再多说,我们先看源代码:
int _tmain(int argc, _TCHAR* argv[])
{
int nNum, nA = 8;
nNum = argc - nA; // 形式1
printf("%d/r/n",nNum);
nNum = argc - 9; // 形式2
printf("%d/r/n",nNum);
nNum = nNum - 1; // 形式3
printf("%d/r/n",nNum);
return 0;
}
这次我们直接看Release版反汇编代码:
.text:00401000 _main proc near ; CODE XREF: __tmainCRTStartup+10Ap
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 push esi
.text:00401001 mov esi, ds:__imp__printf
.text:00401007 push edi
.text:00401008 mov edi, [esp+8+argc]
.text:0040100C lea eax, [edi-8] ; 减法优化
.text:0040100F push eax
.text:00401010 push offset Format ; "%d/r/n"
.text:00401015 call esi ; __imp__printf
.text:00401017 add edi, 0FFFFFFF7h ; 减法优化 <--!
.text:0040101A push edi
.text:0040101B push offset Format ; "%d/r/n"
.text:00401020 call esi ; __imp__printf
.text:00401022 dec edi ; 减法优化
.text:00401023 push edi
.text:00401024 push offset Format ; "%d/r/n"
.text:00401029 call esi ; __imp__printf
.text:0040102B add esp, 18h
.text:0040102E pop edi
.text:0040102F xor eax, eax
.text:00401031 pop esi
.text:00401032 retn
.text:00401032 _main endp
通过以上代码我们不难发现减法的优化方案与加法基本相同,唯一不同的就是在“形式2”上的体现,我们可以发现编译器将其优化成了一个加法,那么这究竟是为什么,而其原理又是怎样的呢?
仅从指令周期上来讲,老版本的CPU其加法指令周期要比减法短一些,因此这种优化也就这样一直沿袭了下来。而仅从本条指令上的优化来说,编译器将原本的减法转换成了加法,并将常量转成了其原本的补码,我们都知道减一个数与加这个数的补码所得到的结果都是一样的,因此编译器就利用了这个特性。
总结:
变量-变量 = lea Exx,[变量-变量]
变量-常量 = add 变量+补码(常量)
变量-1 = dec 变量
int _tmain(int argc, _TCHAR* argv[])
{
int nNum, nA = 8;
nNum = argc + nA; // 形式1
printf("%d/r/n",nNum);
nNum = argc + 9; // 形式2
printf("%d/r/n",nNum);
nNum = nNum + 1; // 形式3
printf("%d/r/n",nNum);
return 0;
}
Debug版反汇编代码:
.text:0041301E mov [ebp+nA], 8 ; nA = 8
.text:00413025 mov eax, [ebp+argc] ; eax=argc
.text:00413028 add eax, [ebp+nA] ; eax=eax+nA <-- !!
.text:0041302B mov [ebp+nNum], eax ; nNum=eax
.text:00413030 mov eax, [ebp+nNum] ; eax=nNum=argc+nA
.text:00413033 push eax
.text:00413034 push offset Format ; "%d/r/n"
.text:00413039 call ds:__imp__printf
.text:0041303F add esp, 8
.text:00413049 mov eax, [ebp+argc]
.text:0041304C add eax, 9 ; eax=argc+9 <-- !!
.text:0041304F mov [ebp+nNum], eax
.text:00413054 mov eax, [ebp+nNum]
.text:00413057 push eax
.text:00413058 push offset Format ; "%d/r/n"
.text:0041305D call ds:__imp__printf
.text:00413063 add esp, 8
.text:0041306D mov eax, [ebp+nNum]
.text:00413070 add eax, 1 ; eax=nNum+1 <-- !!
.text:00413073 mov [ebp+nNum], eax
.text:00413078 mov eax, [ebp+nNum]
.text:0041307B push eax
.text:0041307C push offset Format ; "%d/r/n"
.text:00413081 call ds:__imp__printf
.text:00413087 add esp, 8
通过上面的反汇编代码我们可以看到三个在普通不过的加法计算,下面我们再看看Release版的反汇编代码:
.text:00401000 _main proc near ; CODE XREF: __tmainCRTStartup+10Ap
.text:00401000
.text:00401000 argc= dword ptr 4
.text:00401000 argv= dword ptr 8
.text:00401000 envp= dword ptr 0Ch
.text:00401000
.text:00401000 push esi
.text:00401001 mov esi, ds:__imp__printf
.text:00401007 push edi
.text:00401008 mov edi, [esp+8+argc]
.text:0040100C lea eax, [edi+8] ; 优化后的加法
.text:0040100F push eax
.text:00401010 push offset Format ; "%d/r/n"
.text:00401015 call esi ; __imp__printf
.text:00401017 add edi, 9 ; 此处并没有什么优化
.text:0040101A push edi
.text:0040101B push offset Format ; "%d/r/n"
.text:00401020 call esi ; __imp__printf
.text:00401022 inc edi ; 优化后的加法
.text:00401023 push edi
.text:00401024 push offset Format ; "%d/r/n"
.text:00401029 call esi ; __imp__printf
.text:0040102B add esp, 18h
.text:0040102E pop edi
.text:0040102F xor eax, eax
.text:00401031 pop esi
.text:00401032 retn
.text:00401032 _main endp
由上面两段反汇编代码我们可以总结出加法计算的以下优化方案:
变量+变量 = lea Exx,[变量+变量]
变量+常量 = add 变量+常量
变量+1 = inc 变量
1.7.2、减法的识别与优化技巧
减法的优化与加法大同小异,基本相同,因此与加法相同的地方笔者在本文中将不再多说,我们先看源代码:
int _tmain(int argc, _TCHAR* argv[])
{
int nNum, nA = 8;
nNum = argc - nA; // 形式1
printf("%d/r/n",nNum);
nNum = argc - 9; // 形式2
printf("%d/r/n",nNum);
nNum = nNum - 1; // 形式3
printf("%d/r/n",nNum);
return 0;
}
这次我们直接看Release版反汇编代码:
.text:00401000 _main proc near ; CODE XREF: __tmainCRTStartup+10Ap
.text:00401000
.text:00401000 argc = dword ptr 4
.text:00401000 argv = dword ptr 8
.text:00401000 envp = dword ptr 0Ch
.text:00401000
.text:00401000 push esi
.text:00401001 mov esi, ds:__imp__printf
.text:00401007 push edi
.text:00401008 mov edi, [esp+8+argc]
.text:0040100C lea eax, [edi-8] ; 减法优化
.text:0040100F push eax
.text:00401010 push offset Format ; "%d/r/n"
.text:00401015 call esi ; __imp__printf
.text:00401017 add edi, 0FFFFFFF7h ; 减法优化 <--!
.text:0040101A push edi
.text:0040101B push offset Format ; "%d/r/n"
.text:00401020 call esi ; __imp__printf
.text:00401022 dec edi ; 减法优化
.text:00401023 push edi
.text:00401024 push offset Format ; "%d/r/n"
.text:00401029 call esi ; __imp__printf
.text:0040102B add esp, 18h
.text:0040102E pop edi
.text:0040102F xor eax, eax
.text:00401031 pop esi
.text:00401032 retn
.text:00401032 _main endp
通过以上代码我们不难发现减法的优化方案与加法基本相同,唯一不同的就是在“形式2”上的体现,我们可以发现编译器将其优化成了一个加法,那么这究竟是为什么,而其原理又是怎样的呢?
仅从指令周期上来讲,老版本的CPU其加法指令周期要比减法短一些,因此这种优化也就这样一直沿袭了下来。而仅从本条指令上的优化来说,编译器将原本的减法转换成了加法,并将常量转成了其原本的补码,我们都知道减一个数与加这个数的补码所得到的结果都是一样的,因此编译器就利用了这个特性。
总结:
变量-变量 = lea Exx,[变量-变量]
变量-常量 = add 变量+补码(常量)
变量-1 = dec 变量
相关文章推荐
- 《黑客免杀攻防》读书笔记-软件逆向工程(7)加法与减法的识别与优化原理
- 代码逆向(八)——除法与取模运算的识别与优化原理
- 代码逆向(七)——乘法的识别与优化原理
- 代码逆向(八)——除法与取模运算的识别与优化原理
- 《黑客免杀攻防》读书笔记-软件逆向工程(8)乘法与除法的识别与优化原理
- 【黑客免杀攻防】读书笔记11 - 加法与减法、乘法与除法优化原理
- 从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块
- 逆向工程之表达式优化识别(2)-乘法
- 从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社
- 转:从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块
- 用选择的方式对数组进行排序,并写出对应的优化后的代码实现。(重点写思路、原理)
- 逆向课程第五讲逆向中的优化方式,除法原理,以及除法优化下
- 代码逆向(二)——if-else分支的识别技巧
- 从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块
- 编译原理结构框架10代码优化
- 使用css sprites来优化你的网站在Retina屏幕下显示实现原理与代码
- php实现验证码识别原理和程序代码实例
- Flash代码执行原理与性能优化笔记
- 人脸表情识别笔记(二)特征提取之LBP(局部二值模式)原理及MATLAB代码
- 从原理到代码:大牛教你如何用 TensorFlow 亲手搭建一套图像识别模块 | AI 研习社