吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4342|回复: 21
收起左侧

[调试逆向] 【原创】反汇编分析递归和自写汇编实现

  [复制链接]
lyl610abc 发表于 2021-3-14 22:30
本帖最后由 lyl610abc 于 2021-3-14 23:16 编辑

摸鱼玩玩汇编递归,咸鱼使我快乐{:301_978:}

汇编递归

这次通过简单的递归例子来分析递归的对应汇编

递归求前n项和

描述

求序列的前n项和。输入一个正整数n,输出1+2+3+4+5+6+...+n的前n项和

输入输出示例

输入

5


输出

15(1+2+3+4+5)


代码

int sum(int n){
        if(n==1)return n;
        else{
                return n+sum(n-1);
        }
}
int main(int argc, char* argv[])
{
        int result=sum(5);
        printf("%d\n",result);
        return 0;
}

运行结果

image-20210314205826804


反汇编代码

函数外部
19:       int result=sum(5);
0040D768   push        5
0040D76A   call        @ILT+10(fib) (0040100f)
0040D76F   add         esp,4
0040D772   mov         dword ptr [ebp-4],eax
20:       printf("%d\n",result);

函数内部
8:    int sum(int n){
0040D6F0   push        ebp
0040D6F1   mov         ebp,esp
0040D6F3   sub         esp,40h
0040D6F6   push        ebx
0040D6F7   push        esi
0040D6F8   push        edi
0040D6F9   lea         edi,[ebp-40h]
0040D6FC   mov         ecx,10h
0040D701   mov         eax,0CCCCCCCCh
0040D706   rep stos    dword ptr [edi]
9:        if(n==1)return n;
0040D708   cmp         dword ptr [ebp+8],1
0040D70C   jne         sum+23h (0040d713)
0040D70E   mov         eax,dword ptr [ebp+8]
0040D711   jmp         sum+37h (0040d727)
10:       else{
11:           return n+sum(n-1);
0040D713   mov         eax,dword ptr [ebp+8]
0040D716   sub         eax,1
0040D719   push        eax
0040D71A   call        @ILT+10(fib) (0040100f)
0040D71F   add         esp,4
0040D722   mov         ecx,dword ptr [ebp+8]
0040D725   add         eax,ecx
12:       }
13:   }
0040D727   pop         edi
0040D728   pop         esi
0040D729   pop         ebx
0040D72A   add         esp,40h
0040D72D   cmp         ebp,esp
0040D72F   call        __chkesp (004010e0)
0040D734   mov         esp,ebp
0040D736   pop         ebp
0040D737   ret

反汇编分析

函数外部
19:       int result=sum(5);
0040D768   push        5
0040D76A   call        @ILT+10(fib) (0040100f)
0040D76F   add         esp,4
0040D772   mov         dword ptr [ebp-4],eax

1.压入参数5

0040D768   push        5

2.调用函数

0040D76A   call        @ILT+10(fib) (0040100f)

3.堆栈外平衡(默认采用cdecl调用协定)

0040D76F   add         esp,4

4.将返回值eax赋值给result

0040D772   mov         dword ptr [ebp-4],eax

函数内部

截取出关键代码

9:        if(n==1)return n;
0040D708   cmp         dword ptr [ebp+8],1
0040D70C   jne         sum+23h (0040d713)
0040D70E   mov         eax,dword ptr [ebp+8]
0040D711   jmp         sum+37h (0040d727)
10:       else{
11:           return n+sum(n-1);
0040D713   mov         eax,dword ptr [ebp+8]
0040D716   sub         eax,1
0040D719   push        eax
0040D71A   call        @ILT+10(fib) (0040100f)
0040D71F   add         esp,4
0040D722   mov         ecx,dword ptr [ebp+8]
0040D725   add         eax,ecx
12:       }
13:   }
0040D727   pop         edi

1. cmp比较语句

比较参数和1,这里的[ebp+8]=参数n

0040D708   cmp         dword ptr [ebp+8],1

2.jcc判断跳转语句

jne:jump not equal,不相等则跳转

0040D70C   jne         sum+23h (0040d713)

跳转地址:0040d713

10:       else{
11:           return n+sum(n-1);
0040D713   mov         eax,dword ptr [ebp+8]

即跳转到else


3.将返回值赋值给eax

注意这里是前面的跳转语句没执行才能运行的,也就是前面[ebp+8]=1

这里其实就是mov eax,1

0040D70E   mov         eax,dword ptr [ebp+8]

4.绝对跳转语句

0040D711   jmp         sum+37h (0040d727)

跳转地址:0040d727

0040D727   pop         edi

跳转到要返回前的恢复现场的语句


5.将参数赋值给eax

这里属于else的代码,这里相当于eax=参数n

0040D713   mov         eax,dword ptr [ebp+8]

6.将eax减1

相当于eax=eax-1,结合前面的eax=参数n,此时eax=参数n - 1

0040D716   sub         eax,1

7.将eax作为参数压入堆栈

0040D719   push        eax

8.再次调用函数本身,实现递归

0040D71A   call        @ILT+10(fib) (0040100f)

9.call执行后进行堆栈外平衡

0040D71F   add         esp,4

10.将参数n赋值给ecx

相当于ecx=n

0040D722   mov         ecx,dword ptr [ebp+8]

11.将前面的ecx加给eax

函数返回值存储在eax中,相当于eax=eax+ecx,结合前面的赋值语句,相当于eax=n+sum(n-1)

0040D725   add         eax,ecx

自写汇编实现

仅仅是分析汇编显然不够interesting(>人<;),自己用汇编安排它(ง •_•)ง

代码

#include "stdafx.h"
int sum(int n){
        int address=(int)sum;
        int result;
        __asm{
_if:
                cmp n,1                                //比较参数n和1
                jne _else                        //如果参数n不等于1则跳转到_else段
_match:
                mov eax,1                        //将1赋值给eax
                mov result,eax                //将eax赋值给result,结合上面相当于mov result,1
                jmp _ret                        //绝对跳转到_ret段
_else:
        mov eax,n                        //将参数n赋值给eax
                dec eax                                //让eax自减1,相当于eax=eax-1,结合上面相当于eax=参数n-1
                push eax                        //将eax作为参数压入堆栈
                call address                //调用函数自身
                add esp,4                        //堆栈外平衡
                add eax,n                        //相当于eax=eax+n
                mov result,eax                //将eax赋值给result
_ret:

        }
        return result;
}
int main(int argc, char* argv[])
{

        int result=sum(5);
        printf("%d\n",result);
        return 0;
}

代码分析

这次的自写的函数没有使用裸函数,而是直接在代码中插入汇编代码
裸函数相关可前往:逆向基础笔记九 C语言内联汇编和调用协定

代码流程如下:

1.获取函数的地址

int address=(int)sum;

因为在汇编中需要调用函数本身,所以需要函数的地址


2.声明一个变量用于存储返回值

int result;

3.声明四个代码段

  • _if段:对应if(n==1)的代码段
  • _match段:对应n==1后执行的代码段
  • _else段:对应else的代码段
  • _ret段:对应返回后的代码段,这里为空,采用了c和asm混编的写法,后面直接就是return result

代码段的详细注释已在上面给出,这里不再赘述


总结

递归函数和普通函数的反汇编其实并没用什么特别大的出入,只不过在函数内部调用的函数是自身

函数调用的优先级较其它运算更高(调用函数也可以看作是一种运算),在汇编语句中可以看到是先调用了函数然后再进行的加法

递归函数如果没有正确返回就会不断向堆栈中压入参数,就会导致所谓的递归栈溢出

免费评分

参与人数 9威望 +1 吾爱币 +28 热心值 +8 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
-default- + 1 谢谢@Thanks!
非凡公子 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
qaz007 + 1 + 1 用心讨论,共获提升!
woyucheng + 1 + 1 谢谢@Thanks!
debug_cat + 2 + 1 日常支持大佬打卡
fanvalen + 1 + 1 我很赞同!
13124666733 + 1 + 1 热心回复!
844043335 + 1 很好,加油

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| lyl610abc 发表于 2021-3-14 23:19
fjqisba 发表于 2021-3-14 23:00
楼猪这是要精通x86汇编吗,可以根据指令的功能、类型等方面给常见x86指令分个类

这个可以回顾先前的逆向基础笔记:
逆向基础笔记三 通用寄存器和内存读写
还有后面的相关内容
love514415 发表于 2021-3-15 08:34
 楼主| lyl610abc 发表于 2021-3-30 18:48
陈先森9944 发表于 2021-3-30 18:21
是不是n+sum(n-1)的值,5+sum(5-1)的值,就是4,然后就5+4+3+2+1,条件1成立就返回n,每次加的值是存在n里 ...

递推公式:sum(n)=n+sum(n-1)
sum(5)=5+sum(4)
sum(4)=4+sum(3)
sum(3)=3+sum(2)
sum(2)=2+sum(1)
sum(1)=1
sum(2)=2+1
sum(3)=3+2+1
sum(4)=4+3+2+1
sum(5)=5+4+3+2+1
fjqisba 发表于 2021-3-14 23:00
楼猪这是要精通x86汇编吗,可以根据指令的功能、类型等方面给常见x86指令分个类
S_ingularity 发表于 2021-3-14 23:52
好家伙用最简单的问题探讨最深层问题,NBNB
sw7057 发表于 2021-3-15 05:10
学习了,谢谢楼主分享。
PENN922 发表于 2021-3-15 08:24
太厉害了,感谢分享
ocean2021 发表于 2021-3-15 08:45
谢谢楼主,学习了区以
qaz007 发表于 2021-3-15 08:54
学习了,谢谢分享。
-default- 发表于 2021-3-15 12:43
谢谢楼主,最近正好在学汇编
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-21 19:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表