汇编取得函数地址

// 1.cpp : 定义控制台应用程序的入口点。

//

 

#include "stdafx.h"

#include <iostream>

using namespace std;

#define     array_size 10

int a[array_size]={42, 73, 65, 97, 23, 59, 18, 84, 36, 6};

int _tmain(int argc, _TCHAR* argv[])

{

 

     int *p;

     p=&a[0];

     p--;

 

     __asm

     {

         mov esi,p;

         mov ecx,array_size;

_outloop:

         mov edx,ecx;

_inloop:

        mov eax, [ esi+ecx*4 ]; //一个int占4字节

        mov ebx, [ esi+edx*4 ];

        cmp eax, ebx;

         jnb _noxchg; //不交换

         mov [ esi+ecx*4 ], ebx;

         mov [ esi+edx*4 ], eax;

_noxchg:

              dec edx;

         jnz _inloop;

         loop _outloop;

     }

 

         for (int i=0;i<10;i++)

              cout<<a[i]<<" ";

cin>>a[0];

     return 0;

}

 

 

int RemoteThread()

{

_asm

{

     call @Delta                   //编译的时候产生文件基址地址

          @Delta:

     pop ebp                       //取得加载基址

sub ebp,offset @Delta         //call函数的情况下地址不会变,但是涉及到引用变量,

//PE Loader不会“重定位”引用变量的地址,所以需要自己

//在文件偏移上加上装载基址

     mov eax, fs:[0]

     @FindSEHEnding:

     cmp Dword ptr [eax],$ffffffff

    je @FoundSEHEnding

     mov eax,[eax]

     jmp @FindSEHEnding

     @FoundSEHEnding:

    mov eax, [eax+4]             //根据SEH的结构,其+4为异常函数地址,该异常函数地址在K.Dll内部

    xor ax,ax

     @FindKnlHead:

    sub eax,$10000

    cmp word ptr [eax],$5A4D

    jne @FindKnlHead

     mov ebx,eax

     mov eax, [eax+$3c]

     add eax,ebx

    mov edx, [eax+$78]

     add edx,ebx

    mov edi, [edx+$1c]

     add edi,ebx

    mov esi, [edx+$20]

     add esi,ebx

     xor ecx,ecx

     @Find_ocAd:

     mov eax, [esi]

     add eax,ebx

    cmp dword ptr [eax+5],$6441636f  //"ocAd"的ASC码

    je @Found_ocAd

     @Find_ocAd2:

     add ecx,4

     add esi,4

     jmp @Find_ocAd

     @Found_ocAd:

     cmp word ptr [eax+$0d], $73

    jne @Find_ocAd2

    mov esi,edi

     add esi,ecx

     mov esi,[esi]

     add esi,ebx            //典型的基址加上偏移地址

    call @StrGetModuleHandle

     db 'GetModuleHandleA',0

     @StrGetModuleHandle:   //push字符串,原因是调用CALL的时候,堆栈里先压入了下一条地址

//(如果是调用函数,应该在"另一个"堆栈中压入了参数和EIP,EBP,

//退出时会自动平衡)

    push ebx

     call esi

     mov edi,eax

     call @StrFreeLibrary

     db 'StrFreeLibrary',0

     @StrFreeLibrary:

     push ebx

    call esi

     mov esi,eax

     lea ecx, [ebp+@DLLName]  

    push ecx

    call edi

     push eax

    call esi

     rtn

     @DLLName DD 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

                 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,

                   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

}

 

 

}

 

### C++ 函数调用时生成的汇编指令详解 当C++程序中的函数被调用时,编译器会将其转换成一系列低级机器码指令。这些指令不仅执行实际的功能逻辑,还负责管理栈空间、传递参数以及处理返回值。 #### 参数准备与栈帧建立 在函数`test(int a, int b)`被调用之前,其所需的两个整型参数会被按照特定顺序放置于栈上或寄存器内,这取决于所使用的调用约定[^2]。对于采用默认`__cdecl`调用方式的情况,在调用方处完成参数推送操作: ```assembly push 90 ; 推送第二个参数b=90到栈顶 push 10 ; 推送第一个参数a=10到栈顶 call _test ; 跳转至目标函数并保存返回地址 add esp, 8 ; 清理掉已传入的两个参数占用的空间 ``` 进入`test()`内部后,则需构建新的栈框架(stack frame),以便局部变量能够得到妥善安置,并允许访问上级作用域的数据成员。此过程通常涉及调整基址指针(`EBP`)指向当前活动记录底部位置的操作[^3]。 #### 局部变量初始化及计算表达式 一旦建立了正确的上下文环境,接下来就是对形参进行必要的变换——这里增加了固定的偏移量给输入数值: ```cpp a = a + 3; b = b + 5; ``` 对应的汇编实现可能是这样的形式: ```assembly mov eax, [ebp+arg_0] ; 将实参'a'加载到EAX中 add eax, 3 ; 对'EAX'内的数据加上常数3 mov [ebp-4], eax ; 把结果写回到本地副本里去 mov edx, [ebp+arg_4] ; 类似地对待'b' add edx, 5 mov [ebp-8], edx ``` 最后一步则是求解最终要返回的结果值,即将修改后的两部分相加起来形成单一输出项: ```assembly mov eax, [ebp-4] ; 取得更新过的新'a' add eax, [ebp-8] ; 加上新'b', 结果放在EAX里 ``` 此时,累加所得之和已经位于通用目的寄存器EAX之中等待着由RET指令带回至上层调用者那里继续后续流程。 #### 返回值传输与控制流恢复 随着所有预期的任务都已完成,现在可以安全地结束子程序运行并向外界反馈有用的信息了。由于大多数情况下都是通过EAX来携带简单类型的返回值得以实现这一点,因此无需额外的动作就能满足需求。而为了使CPU能顺利跳转回原来的位置接着往下走,先前CALL命令自动压入的返回地址会在遇到RET的时候弹出来作为PC(Program Counter)的新起点[^4]。 综上所述,整个过程中涉及到多个层面的工作协同配合才得以顺利完成一次完整的函数交互行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值