roger 发表于 2020-9-4 12:19:48

反汇编代码还原之加减乘

目录
[*] 代码还原反汇编之加减乘
[*]         一丶加法
[*]               1.加法的高级代码与反汇编
[*]               1.2 加法中的流水线优化
[*]               1.3 加法的lea指令优化
[*]               1.4 加法中用到了常量折叠,常量传播
[*]               1.5 高版本中的汇编
[*]         二丶减法
[*]               2.1 减法的高级代码与反汇编
[*]               2.2 补码转换与lea指令
[*]         三丶乘法的x86x64优化以及反汇编
[*]               3.1乘法的可优化选项与汇编
[*]               3.2 Vc6.0乘法核心代码反汇编x86
[*]                         3.2.1核心汇编对应
[*]                         3.2.2 lea指令对乘法的优化
[*]                         3.2.3 lea指令与sub指令的优化
[*]               3.3 Visual Studio 2019 乘法核心代码反汇编x86
[*]                         3.3.1 核心代码反汇编
[*]                         3.3.2 SHL+sub的指令优化
[*]               3.4 Visual Studio 2019乘法核心代码反汇编x64
[*]                         3.4.1核心代码反汇编x86
[*]                         3.4.2 核心代码反汇编x64
[*]         四丶总结
代码还原反汇编之加减乘  系列文章
  反汇编技术之熟悉IDA工具
  反汇编逆向技术之寻找Main入口点
  反汇编代码还原之优化方式
一丶加法1.加法的高级代码与反汇编加法对应的 汇编 **add** 指令. 如果是加1 有可能就会使用 **inc** 指令  
  而加法也特别简单.配合上一篇讲解的 优化方式. 可以很好的还原
  有如下高级代码
int main(int argc, char* argv[])  {
  /*
  加法
  */
  int NumberOne = 10;
  int NumberTwo = 10;
  //scanf是防止优化
  scanf("%d",&NumberOne);
  scanf("%d",&NumberOne);
  int Count= NumberOne + NumberTwo;
  int Count1 = 1 + 2;
  int Count2 = NumberOne + 2;
  int Count3 = NumberTwo + 3;
  printf("%d",Count1,Count2,Count3);
  //防止优化编译器优化是很强的.虽然我们取地址了但是后续没有改变就会给我们优化掉.所以这里防止优化,但是防止优化也要能骗过编译器.比如我们可以在下面反汇编中看到.我们的count变量有得已经全部优化掉了
  scanf("%d",&Count1);
  scanf("%d",&Count2);
  scanf("%d",&Count3);
  printf("%d%d%d",&Count1,&Count2,&Count3);
  system("pause");
  return 0;
  }
  
  有如下汇编代码
1000 ; int __cdecl main(int argc, const char **argv, const char **envp)  .text:00401000 _main         proc near               ; CODE XREF: start+AF↓p
  .text:00401000
  .text:00401000 var_10          = dword ptr -10h
  .text:00401000 var_C         = dword ptr -0Ch
  .text:00401000 var_8         = dword ptr -8
  .text:00401000 var_4         = dword ptr -4
  .text:00401000 argc            = dword ptr4
  .text:00401000 argv            = dword ptr8
  .text:00401000 envp            = dword ptr0Ch
  .text:00401000 arg_C         = byte ptr10h
  .text:00401000
  .text:00401000               sub   esp, 10h
  .text:00401003               lea   eax,
  .text:00401007               mov   , 0Ah
  .text:0040100F               push    eax
  .text:00401010               push    offset aD       ; "%d"
  .text:00401015               call    _scanf
  .text:0040101A               lea   ecx,
  .text:0040101E               push    ecx
  .text:0040101F               push    offset aD       ; "%d"
  .text:00401024               call    _scanf
  .text:00401029               mov   edx,
  .text:0040102D               push    13
  .text:0040102F               mov   , 3
  .text:00401037               mov   , 13
  .text:0040103F               lea   eax,
  .text:00401042               push    eax
  .text:00401043               push    3
  .text:00401045               push    offset aD       ; "%d"
  .text:0040104A               mov   , eax
  .text:0040104E               call    _printf
  .text:00401053               lea   eax,
  .text:00401057               push    eax
  .text:00401058               push    offset aD       ; "%d"
  .text:0040105D               call    _scanf
  .text:00401062               lea   ecx,
  .text:00401066               push    ecx
  .text:00401067               push    offset aD       ; "%d"
  .text:0040106C               call    _scanf
  .text:00401071               lea   edx,
  .text:00401075               push    edx
  .text:00401076               push    offset aD       ; "%d"
  .text:0040107B               call    _scanf
  .text:00401080               lea   eax,
  .text:00401084               lea   ecx,
  .text:00401088               push    eax
  .text:00401089               lea   edx,
  .text:0040108D               push    ecx
  .text:0040108E               push    edx
  .text:0040108F               push    offset aDDD   ; "%d%d%d"
  .text:00401094               call    _printf
  .text:00401099               add   esp, 48h
  .text:0040109C               push    offset aPause   ; "pause"
  .text:004010A1               call    _system
  .text:004010A6               xor   eax, eax
  .text:004010A8               add   esp, 14h
  .text:004010AB               retn
  .text:004010AB _main         endp
  
1.2 加法中的流水线优化  第一段汇编查看
.text:00401000               sub   esp, 10h  .text:00401003               lea   eax,
  .text:00401007               mov   , 0Ah
  .text:0040100F               push    eax
  .text:00401010               push    offset aD       ; "%d"
  .text:00401015               call    _scanf
  
  这一段代码我们明显就能看出是有流水线优化的.
  正常排序后的汇编代码应该如下
.text:00401000               sub   esp, 10h  .text:00401007               mov   , 0Ah
  .text:00401003               lea   eax,
  .text:0040100F               push    eax
  .text:00401010               push    offset aD       ; "%d"
  .text:00401015               call    _scanf
  
  这里是赋值指令所以直接使用mov了.单独拿出这一段是想告诉大家. 第一篇是基础但也是你学习反汇编的前提.如果以后 有除法 乘法等.他们都有单独的优化.在配合流水线优化 往往就让你发觉很难.导致无法入门
1.3 加法的lea指令优化  看下核心汇编代码.去掉scanf等
.text:00401007               mov   , 0Ah  .text:00401029               mov   edx,
  .text:0040103F               lea   eax,
  
  我们使用scanf去掉优化后.我们的高级代码
int Count2 = NumberOne + 2;  
  直接变成了 lea指令 lea指令在这里并不是取地址的指令. 而是计算的指令. 计算的是 edx +2 结果再返回给 eax
  这是加法的一种优化方式. lea 指令优化
  所以在我们还原的时候可以还原为如下
eax = edx + 2;edx = var10var10 = 0xA  继续变化
  edx = 0xA
  eax = edx + 2
  继续变化
  eax = 10 + 2
  
  此时我们就反推出了加法原型. 我不知道它人是否是这种反汇编风格.我是从下往上.按照上下文来进行还原. 这种方法 也很类似于 WG 找数据.从下向上找.
1.4 加法中用到了常量折叠,常量传播  看高级代码
int Count1 = 1 + 2;  
  这句代码直接被我们优化为了3 符合常量折叠
int Count3 = NumberTwo + 3;  
  高级代码中的NumberTwo因为我们并没有对其取地址.而后续也没有进行修改.所以进行常量传播 + 常量折叠 变成了汇编代码中的13
.text:0040102F               mov   , 3  .text:00401037               mov   , 13
  
1.5 高版本中的汇编  Visual Studio 2019 看下反汇编
.text:00401080 sub_401080      proc near               ; CODE XREF: start-8D↓p  .text:00401080
  .text:00401080 var_10          = dword ptr -10h
  .text:00401080 var_C         = dword ptr -0Ch
  .text:00401080 var_8         = dword ptr -8
  .text:00401080 var_4         = dword ptr -4
  .text:00401080
  .text:00401080               push    ebp
  .text:00401081               mov   ebp, esp
  .text:00401083               sub   esp, 10h
  .text:00401086               lea   eax,
  .text:00401089               mov   , 0Ah
  .text:00401090               push    eax
  .text:00401091               push    offset unk_41ECDC
  .text:00401096               call    sub_401050
  .text:0040109B               lea   eax,
  .text:0040109E               push    eax
  .text:0040109F               push    offset unk_41ECDC
  .text:004010A4               call    sub_401050
  .text:004010A9               mov   eax,
  .text:004010AC               add   eax, 2
  .text:004010AF               mov   , 3
  .text:004010B6               push    0Dh
  .text:004010B8               push    eax
  .text:004010B9               push    3
  .text:004010BB               push    offset unk_41ECDC
  .text:004010C0               mov   , eax
  .text:004010C3               mov   , 0Dh
  .text:004010CA               call    sub_401020
  .text:004010CF               lea   eax,
  .text:004010D2               push    eax
  .text:004010D3               push    offset unk_41ECDC
  .text:004010D8               call    sub_401050
  .text:004010DD               lea   eax,
  .text:004010E0               push    eax
  .text:004010E1               push    offset unk_41ECDC
  .text:004010E6               call    sub_401050
  .text:004010EB               lea   eax,
  .text:004010EE               push    eax
  .text:004010EF               push    offset unk_41ECDC
  .text:004010F4               call    sub_401050
  .text:004010F9               lea   eax,
  .text:004010FC               push    eax
  .text:004010FD               lea   eax,
  .text:00401100               push    eax
  .text:00401101               lea   eax,
  .text:00401104               push    eax
  .text:00401105               push    offset aDDD   ; "%d%d%d"
  .text:0040110A               call    sub_401020
  .text:0040110F               add   esp, 48h
  .text:00401112               push    offset aPause   ; "pause"
  .text:00401117               call    sub_4048F7
  .text:0040111C               add   esp, 4
  .text:0040111F               xor   eax, eax
  .text:00401121               mov   esp, ebp
  .text:00401123               pop   ebp
  .text:00401124               retn
  .text:00401124 sub_401080      endp
  
  高版本代码变多. 函数使用了EBP寻址 vc6.0 使用了esp寻址.所以我们才会看到很多调整指令
  核心汇编位置
.text:00401089               mov   , 0Ah  .text:004010A9               mov   eax,
  .text:004010AC               add   eax, 2
  
  在VC6.0中使用了 lea指令优化.在VS2019中就使用了add+mov的方式进行优化
  本质是没有改变的. 但是两种都要明白.
  加法的这个例子特别适合代码还原. 而且不复杂. 认识了流水线优化 常量传播 常量折叠 就会还原的很简单. 如果有常量传播 以及常量折叠.直接按照常量来还原即可.
  例如:
int a = 10;  int b = a + 1;
  printf("%d",b);
  在汇编中反汇编后.你可能直接变成了如下
  printf("%d",11);
  
  直接按照还原11 即可.
二丶减法2.1 减法的高级代码与反汇编  减法 对应指令sub 如果是自减 那么对应的指令可能就会有 dec 指令
  计算机只会做加法.而不会做减法. 所以对减法的优化 就是变为加法
  高级代码
int main(int argc, char* argv[])  {
  /*
  加法
  */
  int NumberOne = argc;
  int NumberTwo = argc;
  int Count= NumberOne - NumberTwo;
  int Count1 = NumberOne - 2;
  int Count2 = NumberOne - 5;
  int Count3 = NumberTwo - NumberOne;
  printf("%d",Count1,Count2,Count3);
  //防止优化
  scanf("%d",&Count1);
  scanf("%d",&Count2);
  scanf("%d",&Count3);
  printf("%d%d%d",&Count1,&Count2,&Count3);
  system("pause");
  return 0;
  }
  
  Vc6.0汇编
text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)  .text:00401000 _main         proc near               ; CODE XREF: start+AF↓p
  .text:00401000
  .text:00401000 var_8         = dword ptr -8
  .text:00401000 var_4         = dword ptr -4
  .text:00401000 argc            = dword ptr4
  .text:00401000 argv            = dword ptr8
  .text:00401000 envp            = dword ptr0Ch
  .text:00401000
  .text:00401000               sub   esp, 8
  .text:00401003               mov   eax,
  .text:00401007               xor   edx, edx
  .text:00401009               push    edx
  .text:0040100A               mov   , edx
  .text:0040100E               lea   ecx,
  .text:00401011               add   eax, 0FFFFFFFBh
  .text:00401014               push    eax
  .text:00401015               push    ecx
  .text:00401016               push    offset aD       ; "%d"
  .text:0040101B               mov   , ecx
  .text:0040101F               mov   , eax
  .text:00401023               call    _printf
  .text:00401028               lea   eax,
  .text:0040102C               push    eax
  .text:0040102D               push    offset aD       ; "%d"
  .text:00401032               call    _scanf
  .text:00401037               lea   ecx,
  .text:0040103B               push    ecx
  .text:0040103C               push    offset aD       ; "%d"
  .text:00401041               call    _scanf
  .text:00401046               lea   edx,
  .text:0040104A               push    edx
  .text:0040104B               push    offset aD       ; "%d"
  .text:00401050               call    _scanf
  .text:00401055               lea   eax,
  .text:00401059               lea   ecx,
  .text:0040105D               push    eax
  .text:0040105E               lea   edx,
  .text:00401062               push    ecx
  .text:00401063               push    edx
  .text:00401064               push    offset aDDD   ; "%d%d%d"
  .text:00401069               call    _printf
  .text:0040106E               push    offset aPause   ; "pause"
  .text:00401073               call    _system
  .text:00401078               xor   eax, eax
  .text:0040107A               add   esp, 44h
  .text:0040107D               retn
  .text:0040107D _main         endp
  
2.2 补码转换与lea指令  看下核心汇编
.text:00401000               sub   esp, 8  .text:00401003               mov   eax,        eax = argc
  .text:00401007               xor   edx, edx
  .text:00401009               push    edx
  .text:0040100A               mov   , edxargc = 0
  .text:0040100E               lea   ecx,
  .text:00401011               add   eax, 0FFFFFFFBh
  .text:00401014               push    eax
  .text:00401015               push    ecx
  .text:00401016               push    offset aD       ; "%d"
  .text:0040101B               mov   , ecx
  .text:0040101F               mov   , eax
  
  第一种方式跟加法一样.使用 lea进行计算 第二种方式 使用了add指令.加了一个很大的数0FFFFFFFBh
  其实这个地方就是对减法的优化这个很大的数是补码
  这里补充下基础知识
名称含义原码以二进制来说,原码以符号位加上其绝对值来表示的一个数 也就是用二进制的第一位表示符号位 其余位表示真值 表示-127~127的值反码正数的反码就是自己本身,负数的反码是符号位不变 其余各位取反补码正数的补码就是自己本身,负数的补码就是在其原码的基础上,符号位不变,其余各位取反.最后加1. 也就是 负数 = 反码 + 1  其实负数就变成了 补码了补码的变化就是 负数取反 + 1那么还原也是
  Not(0FFFFFFFBh) + 1 就会得出真实的值.
  其余减法优化就会配合第一篇讲的.各种优化方式进行代码优化.
  如果代码还原请谨记
  如果add 操作数使用了负数的表现形式.那么就可以还原为减法. 因为执行的操作是减法而不是加法.
三丶乘法的x86x64优化以及反汇编3.1乘法的可优化选项与汇编  乘法 对应指令 Imul 以及对应指令 Mul 分别是有符号乘法 以及无符号乘法. 两个数都是变量的时候.且不满足前面所讲的变量去除优化选项那么是无法进行优化的. 乘法对于 2的幂是可以进行优化的.会使用指令较短的周期来进行优化
  高级代码
int main(int argc, char* argv[])  {
  /*
  乘法
  */
  int NumberOne = argc;
  int NumberTwo = argc;
  scanf("%d",&NumberOne);
  scanf("%d",&NumberTwo);
  int Count1 = NumberOne * NumberTwo;//不满足变量去除则就会使用原生除法指令优化
  int Count2 = NumberOne * 4;
  int Count3 = NumberTwo * 15;
  int Count4 = NumberOne + 4 * 3;      //混合运算
  int Count5 = NumberTwo * 7 + 5;      //混合运算
  printf("%d%d%d%d%d",Count1,Count2,Count3,Count4,Count5);
  system("pause");
  return 0;
  }
  
  VC6.0 对应汇编
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)  .text:00401000 _main         proc near               ; CODE XREF: start+AF↓p
  .text:00401000
  .text:00401000 var_4         = dword ptr -4
  .text:00401000 argc            = dword ptr4
  .text:00401000 argv            = dword ptr8
  .text:00401000 envp            = dword ptr0Ch
  .text:00401000
  .text:00401000               push    ecx
  .text:00401001               mov   eax,
  .text:00401005               mov   , eax
  .text:00401009               mov   , eax
  .text:0040100D               lea   eax,
  .text:00401011               push    eax
  .text:00401012               push    offset aD       ; "%d"
  .text:00401017               call    _scanf
  .text:0040101C               lea   ecx,
  .text:00401020               push    ecx
  .text:00401021               push    offset aD       ; "%d"
  .text:00401026               call    _scanf
  .text:0040102B               mov   eax,
  .text:0040102F               mov   ecx,
  .text:00401033               lea   edx, ds:0
  .text:0040103A               sub   edx, eax
  .text:0040103C               add   edx, 5
  .text:0040103F               push    edx
  .text:00401040               lea   edx,
  .text:00401043               push    edx
  .text:00401044               lea   edx,
  .text:00401047               imul    eax, ecx
  .text:0040104A               lea   edx,
  .text:0040104D               push    edx
  .text:0040104E               lea   edx, ds:0
  .text:00401055               push    edx
  .text:00401056               push    eax
  .text:00401057               push    offset aDDDDD   ; "%d%d%d%d%d"
  .text:0040105C               call    _printf
  .text:00401061               push    offset aPause   ; "pause"
  .text:00401066               call    _system
  .text:0040106B               xor   eax, eax
  .text:0040106D               add   esp, 30h
  .text:00401070               retn
  .text:00401070 _main         endp
  
  vs2019 x86对应汇编
.text:00401080 sub_401080      proc near               ; CODE XREF: start-8D↓p  .text:00401080
  .text:00401080 var_4         = dword ptr -4
  .text:00401080 arg_0         = dword ptr8
  .text:00401080
  .text:00401080               push    ebp
  .text:00401081               mov   ebp, esp
  .text:00401083               push    ecx
  .text:00401084               mov   eax,
  .text:00401087               mov   , eax
  .text:0040108A               mov   , eax
  .text:0040108D               lea   eax,
  .text:00401090               push    eax
  .text:00401091               push    offset unk_41ECDC
  .text:00401096               call    sub_401050
  .text:0040109B               lea   eax,
  .text:0040109E               push    eax
  .text:0040109F               push    offset unk_41ECDC
  .text:004010A4               call    sub_401050
  .text:004010A9               mov   edx,
  .text:004010AC               mov   ecx,
  .text:004010AF               lea   eax, ds:0
  .text:004010B6               sub   eax, edx
  .text:004010B8               add   eax, 5
  .text:004010BB               push    eax
  .text:004010BC               lea   eax,
  .text:004010BF               push    eax
  .text:004010C0               mov   eax, edx
  .text:004010C2               shl   eax, 4
  .text:004010C5               sub   eax, edx
  .text:004010C7               imul    edx, ecx
  .text:004010CA               push    eax
  .text:004010CB               lea   eax, ds:0
  .text:004010D2               push    eax
  .text:004010D3               push    edx
  .text:004010D4               push    offset aDDDDD   ; "%d%d%d%d%d"
  .text:004010D9               call    sub_401020
  .text:004010DE               push    offset aPause   ; "pause"
  .text:004010E3               call    sub_4048C7
  .text:004010E8               add   esp, 2Ch
  .text:004010EB               xor   eax, eax
  .text:004010ED               mov   esp, ebp
  .text:004010EF               pop   ebp
  .text:004010F0               retn
  .text:004010F0 sub_401080      endp
  
  Vs2019 X64对应汇编
.text:00000001400010D0 sub_1400010D0   proc near               ; CODE XREF: sub_140001268+107↓p  .text:00000001400010D0                                       ; DATA XREF: .pdata:000000014002700C↓o ...
  .text:00000001400010D0
  .text:00000001400010D0 var_18          = dword ptr -18h
  .text:00000001400010D0 var_10          = dword ptr -10h
  .text:00000001400010D0 arg_0         = dword ptr8
  .text:00000001400010D0 arg_10          = dword ptr18h
  .text:00000001400010D0
  .text:00000001400010D0               sub   rsp, 38h
  .text:00000001400010D4               mov   , ecx
  .text:00000001400010D8               lea   rdx,
  .text:00000001400010DD               mov   , ecx
  .text:00000001400010E1               lea   rcx, unk_140022760
  .text:00000001400010E8               call    sub_140001080
  .text:00000001400010ED               lea   rdx,
  .text:00000001400010F2               lea   rcx, unk_140022760
  .text:00000001400010F9               call    sub_140001080
  .text:00000001400010FE               mov   edx,
  .text:0000000140001102               mov   eax,
  .text:0000000140001106               imul    r10d, edx, 7
  .text:000000014000110A               imul    r9d, edx, 0Fh
  .text:000000014000110E               imul    edx, eax
  .text:0000000140001111               lea   ecx,
  .text:0000000140001114               lea   r8d, ds:0
  .text:000000014000111C               add   r10d, 5
  .text:0000000140001120               mov   , r10d
  .text:0000000140001125               mov   , ecx
  .text:0000000140001129               lea   rcx, aDDDDD   ; "%d%d%d%d%d"
  .text:0000000140001130               call    sub_140001020
  .text:0000000140001135               lea   rcx, aPause   ; "pause"
  .text:000000014000113C               call    sub_140004584
  .text:0000000140001141               xor   eax, eax
  .text:0000000140001143               add   rsp, 38h
  .text:0000000140001147               retn
  .text:0000000140001147 sub_1400010D0   endp
  
3.2 Vc6.0乘法核心代码反汇编x863.2.1核心汇编对应.text:0040102B               mov   eax, 变量赋值  .text:0040102F               mov   ecx,
  .text:00401033               lea   edx, ds:0      lea指令计算
  .text:0040103A               sub   edx, eax                减法计算
  .text:0040103C               add   edx, 5                混合运算
  .text:00401040               lea   edx,
  .text:00401044               lea   edx,    3eax
  .text:00401047               imul    eax, ecx
  .text:0040104A               lea   edx,    3eax + 3eax * 4 eax15
  .text:0040104E               lea   edx, ds:0
  
  高级代码 与汇编对应
int NumberOne = argc;  int NumberTwo = argc;
  .text:0040102B               mov   eax, 变量赋值
  .text:0040102F               mov   ecx,
  int Count5 = NumberTwo * 7 + 5;      //混合运算
  .text:00401033               lea   edx, ds:0      lea指令计算
  .text:0040103A               sub   edx, eax                减法计算
  .text:0040103C               add   edx, 5                混合运算
  int Count4 = NumberOne + 4 * 3;      //混合运算
  .text:00401040               lea   edx,
  int Count1 = NumberOne * NumberTwo;
  .text:00401047               imul    eax, ecx
  int Count3 = NumberTwo * 15;
  .text:00401044               lea   edx,    3eax
  .text:0040104A               lea   edx,    3eax + 3eax * 4
  int Count2 = NumberOne * 4;
  .text:0040104E               lea   edx, ds:0
  
  通过上面我们去掉流水线优化.人肉反汇编可以看出. 在乘法优化中. 大部分使用lea指令进行优化.特别是混合运算
3.2.2 lea指令对乘法的优化  首先我们根据上面汇编先讲解下lea指令对乘法的优化
  高级代码
int Count3 = NumberTwo * 15;  
  对应汇编
.text:00401044               lea   edx,    3eax  .text:0040104A               lea   edx,    3eax + 3eax * 4
  
  在这里 lea是计算指令而不是取地址 第一行汇编 我们产生了定式也就是eax *2 + eax 等价于 3eax
  那么第二行汇编又依赖于第一行的输出.那么产生的 定式也就是
  3eax + 3eax * 4    先算乘法得出3eax + 12eax继续计算 得出 15eax
  那么我们就可以还原高级代码为 eax * 15, 而eax我们也看到赋值了,也就是 argc变量
  所以最终代码就是 argc * 15 我们的原来的代码应该是NumberTwo. 这里也是被编译器给优化掉了.
  所以 乘法的优化一定要明白 lea指令,包括我们的加法也会使用.一定要认识lea 以及自己会换算
3.2.3 lea指令与sub指令的优化  lea指令我们已经知道了.其实这里着重讲解一下sub指令优化
  高级代码与反汇编
int Count5 = NumberTwo * 7 + 5;  .text:00401033               lea   edx, ds:0      lea指令计算
  .text:0040103A               sub   edx, eax                减法计算
  .text:0040103C               add   edx, 5                混合运算
  
  lea指令我们说了.在括号内是计算一个值. 通过汇编我们也可以得出 lea得出的结果是 8eax. 而这里又使用 sub 来减去自己本身.等价于 8eax - eax = 7eax 最后在加 5
  eax我们知道是argc 所以得出的反汇编为 7eax + 5<===> 7 argc + 5== argc + 7 + 5
  这里是首先使用lea换算成2的幂来进行优化的. 然后减去自己本身. 这样也比直接用 IMUL 或者MUL执行效率高.
  其他基本上都是使用lea指令进行优化了.熟悉lea看一眼即可.
int Count4 = NumberOne + 4 * 3;  .text:00401040               lea   edx,
  两个未知变量做不了优化所以使用Imul 使用Imul代码是有符号乘法
  int Count1 = NumberOne * NumberTwo;
  .text:00401047               imul    eax, ecx
  int Count2 = NumberOne * 4;
  .text:0040104E               lea   edx, ds:0
  
3.3 Visual Studio 2019 乘法核心代码反汇编x863.3.1 核心代码反汇编获取变量到寄存器用于计算  .text:004010A9               mov   edx,
  .text:004010AC               mov   ecx,
  int Count5 = NumberTwo * 7 + 5;      //混合运算
  .text:004010AF               lea   eax, ds:0
  .text:004010B6               sub   eax, edx
  .text:004010B8               add   eax, 5
  int Count4 = NumberOne + 4 * 3;
  .text:004010BC               lea   eax,
  int Count3 = NumberTwo * 15;
  .text:004010C0               mov   eax, edx
  .text:004010C2               shl   eax, 4
  .text:004010C5               sub   eax, edx
  int Count1 = NumberOne * NumberTwo;
  .text:004010C7               imul    edx, ecx
  int Count2 = NumberOne * 4;
  .text:004010CB               lea   eax, ds:0
  
  观看反汇编 除了NumberTwo * 15 有所变化其余的全部同Vc6.0一样. 所以说在编译器优化上.并不是越高级的编译器越好的IDE是最好的. 底层是一样的.优化方式还是同几十年前的VC6.0
3.3.2 SHL+sub的指令优化  其实之前说过.对于2的幂.编译器可能会选择使用移位来进行优化.这里也就出现了. 也是先优化为2的幂减去自己本身
int Count3 = NumberTwo * 15;  .text:004010C0               mov   eax, edx
  .text:004010C2               shl   eax, 4
  .text:004010C5               sub   eax, edx
  
  SHL eax,4= eax *4= 16eax
  然后使用sub16eax - eax = 15 eax 那么就得出计算的值了.
  总结来说原理还是同vc6.0优化一样.所以要熟悉汇编.
3.4 Visual Studio 2019乘法核心代码反汇编x643.4.1核心代码反汇编x86  在x64下如果你还是使用x86写代码的形式.那么可能都不用优化了.优化的前提是两个操作数相乘.结果可能溢出.所以有时候会用两个寄存器表示.而在64位下.如果你还是使用两个32位操作数相乘.那么基本上就不给你优化了. 原因是32*32 顶多跟64位的数相等.那么我直接使用指令进行操作了
  代码如下
.text:00000001400010FE               mov   edx,   .text:0000000140001102               mov   eax,
  混合运算直接imul计算并想加
  .text:0000000140001106               imul    r10d, edx, 7
  .text:000000014000111C               add   r10d, 5
  以下都是直接使用imul 或者 lea进行计算了.没有可讲的
  .text:000000014000110A               imul    r9d, edx, 0Fh
  .text:000000014000110E               imul    edx, eax
  .text:0000000140001111               lea   ecx,
  .text:0000000140001114               lea   r8d, ds:0
  
3.4.2 核心代码反汇编x64  之前高级代码都是32位数.那么我们已_int64的操作数来进行查看.
  高级代码
__int64 NumberOne = argc;  __int64 NumberTwo = argc;
  scanf("%I64d", &NumberOne);
  scanf("%I64d", &NumberTwo);
  __int64 Count1 = NumberOne * NumberTwo;//不满足变量去除则就会使用原生除法指令优化
  __int64 Count2 = NumberOne * 4;
  __int64 Count3 = NumberTwo * 15;
  __int64 Count4 = NumberOne + 4 * 3;      //混合运算
  __int64 Count5 = NumberTwo * 7 + 5;      //混合运算
  printf("%I64d%I64d%I64d%I64d%I64d", Count1, Count2, Count3, Count4, Count5);
  system("pause");
  
.text:0000000140001103               mov   rdx,   .text:0000000140001108               mov   rax,
  .text:000000014000110D               imul    r10, rdx, 7
  .text:0000000140001111               imul    r9, rdx, 0Fh
  .text:0000000140001115               imul    rdx, rax
  .text:0000000140001119               lea   rcx,
  .text:000000014000111D               add   r10, 5
  .text:0000000140001121               mov   , r10
  .text:0000000140001126               lea   r8, ds:0
  .text:000000014000112E               mov   , rcx
  
  但是发现还是同上.换个大点的数看看
__int64 Count3 = NumberTwo * 161 * 256 *323 * NumberOne;  
.text:000000014000110F               imul    rax,   .text:0000000140001115               imul    rdx, rax, 0CB2300h
  
  其实可以发现.首先常量是直接配合常量折叠 已经给算出来了. 而其它直接使用Imul进行计算了
  无符号大家可以自己建立工程查看.原理同上
四丶总结  我们学习了乘法 加法 减法 那么可以进行总结
  1.lea 指令是比较常见与乘法的计算以及加法的计算
  2.减法指令是可以转为补码 .优化为加法.
  还是那句话 高手复习 新手学习.
  《0day安全 软件漏洞分析技术(第二版)》第三次再版印刷预售开始!
最后于12小时前被TkBinary编辑,原因:

zhanglyl 发表于 2020-9-17 09:59:11

Mark一下。周末学习。谢谢!{:28:}
页: [1]
查看完整版本: 反汇编代码还原之加减乘