设为首页 加入收藏

TOP

5.10 汇编语言:汇编过程与结构(二)
2023-08-26 21:10:31 】 浏览:200
Tags:5.10 程与结
si ; 保存ESI,ECX push ecx xor eax,eax S1: add eax,dword ptr ds:[esi] ; 取值并相加 add esi,4 ; 递增数组指针 loop S1 pop ecx ; 恢复ESI,ECX pop esi ret ArraySum endp main PROC lea esi,dword ptr ds:[MyArray] ; 取出数组基址 mov ecx,lengthof MyArray ; 取出元素数目 call ArraySum ; 调用方法 mov dword ptr ds:[Sum],eax ; 得到结果 invoke crt_printf,addr szFmt,Sum int 3 main ENDP END main

接着我们来实现一个具有获取随机数功能的案例,在C语言中如果需要获得一个随机数一般会调用Seed函数,如果读者逆向分析过这个函数的实现原理,那么读者应该能理解,在调用取随机数之前会生成一个随机数种子,这个随机数种子的生成则依赖于0x343FDh这个特殊的常量地址,当我们每次访问该地址都会产出一个随机的数据,当得到该数据后,我们再通过除法运算取出溢出数据作为随机数使用实现了该功能。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib

.data
  seed DWORD 1
  szFmt    BYTE '随机数: %d',0dh,0ah,0
.code
  ; 生成 0 - FFFFFFFFh 的随机种子
  Random32 PROC
    push  edx
    mov   eax, 343FDh
    imul  seed
    add   eax, 269EC3h
    mov   seed, eax
    ror   eax,8
    pop   edx
    ret
  Random32 endp
  
  ; 生成随机数
  RandomRange PROC
    push  ebx
    push  edx
    
    mov   ebx,eax
    call  Random32
    mov   edx,0
    div   ebx
    mov   eax,edx

    pop   edx
    pop   ebx
    ret
  RandomRange endp

  main PROC
  
    ; 调用后取出随机数
    call RandomRange
    invoke crt_printf,addr szFmt,eax
    int 3
  main ENDP
END main

10.3 局部参数传递

在汇编语言中,可以使用堆栈来传递函数参数和创建局部变量。当程序执行到函数调用语句时,需要将函数参数传递给被调用函数。为了实现参数传递,程序会将参数压入栈中,然后调用被调用函数。被调用函数从栈中弹出参数并执行,然后将返回值存储在寄存器中,最后通过跳转返回到调用函数。

局部变量也可以通过在栈中分配内存来创建。在函数开始时,可以使用push指令将局部变量压入栈中。在函数结束时,可以使用pop指令将变量从栈中弹出。由于栈是后进先出的数据结构,局部变量的创建可以很方便地通过在栈上压入一些数据来实现。

局部变量是在程序运行时由系统动态的在栈上开辟的,在内存中通常在基址指针(EBP)之下,尽管在汇编时不能给定默认值,但可以在运行时初始化,如下一段C语言伪代码:

void MySub()
{
  int var1 = 10;
  int var2 = 20;
}

上述的代码经过C编译后,会变成如下汇编指令,其中EBP-4必须是4的倍数,因为默认就是4字节存储,如果去掉了mov esp,ebp,那么当执行pop ebp时将会得到EBP等于10,执行RET指令会导致控制转移到内存地址10处执行,从而程序会崩溃。

MySub PROC
  push ebp                  ; 将EBP存储在栈中
  mov ebp,esp               ; 堆栈框架的基址
  sub esp,8                 ; 创建局部变量空间(分配2个局部变量)

  mov DWORD PTR [ebp-8],10  ; var1 = 10
  mov DWORD PTR [ebp-4],20  ; var2 = 20

  mov esp,ebp               ; 从堆栈上删除局部变量
  pop ebp                   ; 恢复EBP指针
  ret 8                     ; 返回,清理堆栈
MySub ENDP

为了使上述代码片段更易于理解,可以在上述的代码的基础上给每个变量的引用地址都定义一个符号,并在代码中使用这些符号,如下代码所示,代码中定义了一个名为MySub的过程,该过程将两个局部变量分别设置为1020

在该过程中,首先使用push ebp指令将旧的基址指针压入栈中,并将ESP寄存器的值存储到ebp中。这个旧的基址指针将在函数执行完毕后被恢复。然后,我们使用sub esp,8指令将8字节的空间分配给两个局部变量。在堆栈上分配的空间可以通过var1_localvar2_local符号来访问。在这里,我们定义了两个符号,将它们与ebp寄存器进行偏移以访问这些局部变量。var1_local的地址为[ebp-8]var2_local的地址为[ebp-4]。然后,我们使用mov指令将1020分别存储到这些局部变量中。最后,我们将ESP寄存器的值存储回ebp中,并使用pop ebp指令将旧的基址指针弹出堆栈。现在,栈顶指针(ESP)下移恢复上面分配的8个字节的空间,最后通过ret 8返回到调用函数。

在使用堆栈传参和创建局部变量时,需要谨慎考虑栈指针的位置,并确保遵守调用约定以确保正确地传递参数和返回值。

var1_local EQU DWORD PTR [ebp-8]   ; 添加符号1
var2_local EQU DWORD PTR [ebp-4]   ; 添加符号2

MySub PROC
  push ebp
  mov ebp,esp
  sub esp,8
  mov var1_local,10
  mov var2_local,20
  mov esp,ebp
  pop ebp
  ret 8
MySub ENDP

接着我们来实现一个具有功能的案例,首先为了能更好的让读者理解我们先使用C语言方式实现MakeArray()函数,该函数的内部是动态生成的一个MyString数组,并通过循环填充为星号字符串,最后使用POP弹出,并输出结果,观察后尝试用汇编实现。

void makeArray()
{
  char MyString[30];
  for(int i=0;i<30;i++)
  {
    myString[i] = "*";
  }
}

call makeArray()

上述C语言代码如果翻译为汇编格式则如下所示,代码使用汇编语言实现makeArray的程序,该程序开辟了一个长度为30的数组,将其中的元素填充为*,然后弹出两个元素,并将它们输出到控制台。

  .386p
  .model flat,stdcall
  option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib
include msvcrt.inc
includelib msvcrt.lib

.data
  szFmt BYTE '出栈数据: %x ',0dh,0ah,0
.code
  makeArray PROC
    push ebp
    mov ebp,esp
    
    ; 开辟局部数组
    sub esp,32                    ; MyString
首页 上一页 1 2 3 4 5 6 下一页 尾页 2/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇两数相加 下一篇ImGui界面优化:使用图标字体、隐..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目