使用 EBX 寄存器进行内存访问的模式是什么?

2024-01-02

我正在学习逆向工程的基础知识。在逆向crackme时,我碰巧在几乎每个函数的开头都看到了这种模式:

pushl %ebp                            
movl  %esp, %ebp              
pushl %ebx              # because ebx is a callee-saved register
subl  $0x14,%esp        # of course $0x14 changes depending on the function
calll 0x08048766
addl  $0x1a5f, %ebx     # also this value sometime changes depending on the function

在哪里0x08048766有一个函数可以做到这一点:

movl 0(%esp), %ebx         
retl 

所以基本上,正如正常的那样,每个函数首先初始化寄存器ebp and esp。然后注册ebx被压入堆栈,这也是完全可以理解的ebx是一个被调用者保存的寄存器,稍后在函数中使用它来引用一些静态数据(来自.rodata), 例如:

leal  -0x17b7(%ebx), %eax
movl  %eax, 0(%esp) 
calll printf   

现在是最有趣(对我来说也是晦涩难懂)的部分:如果我理解正确的话,ebx首先用指向的值进行初始化esp(这使用了函数0x08048766), 为什么?里面有什么?这不是堆栈中未初始化的点吗?

然后将另一个值添加到ebx。这个值代表什么?

我想更好地了解如何注册ebx在这种情况下使用,以及如何计算它所指向的地址。

你可以看一下完整的程序here https://github.com/Maijin/Workshop2015/blob/master/IOLI-crackme/bin-linux/crackme0x09,但不幸的是没有任何可用的 C 源代码。


这段代码似乎是用-fPIC。 PIC 代表“位置无关代码”,这意味着它可以加载到任何地址,并且仍然能够访问其全局变量。

在这种情况下ebx被称为PIC寄存器,它用于指向GOT(全局偏移表)的末尾。 GOT 具有到所使用的每个全局变量的偏移量(从程序的基地址*)。

很多时候,了解这些事情的最好方法是自己编译一些代码,然后查看输出。当您有符号可供查看时,它尤其会变得更容易。

我们来做一个实验:

pic.c

int global;

int main(void)
{
    global = 4;
    return 0;
}

Compile

$ gcc -v
...
gcc version 5.3.1 20160406 (Red Hat 5.3.1-6) (GCC)

$ gcc -m32 -Wall -Werror -fPIC -o pic pic.c

Sections(略)

$ readelf -S pic
Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [13] .text             PROGBITS        080482f0 0002f0 000182 00  AX  0   0 16
  [15] .rodata           PROGBITS        08048488 000488 00000c 00   A  0   0  4
  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        0804a000 001000 000014 04  WA  0   0  4
  [24] .data             PROGBITS        0804a014 001014 000004 00  WA  0   0  1
  [25] .bss              NOBITS          0804a018 001018 000008 00  WA  0   0  4

拆卸(英特尔语法,因为 AT&T 让我抓狂)

$ objdump -Mintel -d --no-show-raw-insn pic

080483eb <main>:
 80483eb:   push   ebp
 80483ec:   mov    ebp,esp
 80483ee:   call   804840b <__x86.get_pc_thunk.ax> ; EAX = EIP + 5
 80483f3:   add    eax,0x1c0d            ; EAX = 0x804a000 (.got.plt, end of .got)
 80483f8:   lea    eax,[eax+0x1c]        ; EAX = 0x804a01C (.bss + 4)

 80483fe:   mov    DWORD PTR [eax],0x4   ; set `global` to 4
 8048404:   mov    eax,0x0
 8048409:   pop    ebp
 804840a:   ret    

0804840b <__x86.get_pc_thunk.ax>:
 804840b:   mov    eax,DWORD PTR [esp]
 804840e:   ret    
 804840f:   nop

解释

在这种情况下,我的 GCC 决定使用eax作为 PIC 寄存器而不是ebx.

另外,请注意编译器(GCC 5.3.1)在这里做了一些有趣的事情。它不是通过 GOT 访问变量,而是本质上使用 GOT 作为“锚点”,而是直接偏移到变量中.bss部分。


回到你的代码:

pushl %ebp                            
movl %esp, %ebp              
pushl %ebx             ; because ebx is a callee-saved register
subl $0x14,%esp        ; end of typical prologue 

calll 0x08048766       ; __i686_get_pc_thunk_bx
                       ; Gets the current value of EIP after this call into EBX.
                       ; There is no other way to do this in x86 without a call

addl $0x1a5f, %ebx     ; Add the displacement to the end of the GOT.
                       ; This displacement of course changes depending on 
                       ; where the function is.
                       ; EBX now points to the end of the GOT.

leal -0x17b7(%ebx), %eax    ; EAX = EBX - 0x17b7
movl %eax, 0(%esp)          ; Put EAX on stack (arg 0 to printf)
                            ; EAX should point to some string
calll printf   

在您的代码中,它实际上并没有“使用”GOT(否则我们会看到第二个内存取消引用);它使用它作为字符串的锚点,可能在只读数据部分(.rodata) 也出现在 GOT 之前。

如果你看一下这个函数0x08048766,你会看到它看起来像这样:

mov    (%esp),%eax  ; Put return address (pushed onto stack by call insn)
                    ; in eax
ret                 ; Return
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 EBX 寄存器进行内存访问的模式是什么? 的相关文章

  • 现代 x86 硬件不能将单个字节存储到内存中吗?

    说到 C 的并发内存模型 Stroustrup 的C 编程语言 第 4 版 第 1 节 41 2 1 说 就像大多数现代硬件一样 机器无法加载或存储小于单词的任何内容 然而 我的 x86 处理器已经有几年的历史了 它可以并且确实存储小于单词
  • elf .rel.text 部分中 R_386_32/R_386_PC32 的含义

    为了理解重定位的概念 我编写了一个简单的 chk c 程序 如下所示 1 include
  • 无法拦截Android应用程序的流量

    最近 我正在尝试测试和拦截来自开发的应用程序的流量罗莫比尔 我设置了一个代理burp 当然 我已经在我的设备上安装了 burp 证书 因此我可以拦截设备上的其他应用程序 但我无法在 burp suite 上看到相关应用程序的流量 相反 该应
  • 如何获取 VESA BIOS 信息

    我正在跟踪Phil Opp 教程 https os phil opp com 关于用 Rust 编写一个操作系统 在稍微尝试了一下之后 我想在屏幕上显示真实的图形 我发现我应该从使用带有 VESA 的线性帧缓冲区开始 我在 osdev or
  • 一条指令可以同时处于两种寻址模式吗?

    我在书中读到了以下内容从头开始编程 处理器有多种不同的访问数据的方式 称为 寻址模式 最简单的模式是立即模式 其中 要访问的数据嵌入在指令本身中 例如 如果我们想将寄存器初始化为 0 而不是给出 计算机要从中读取 0 的地址 我们将指定立即
  • 如何让c代码执行hex机器代码?

    我想要一个简单的 C 方法能够在 Linux 64 位机器上运行十六进制字节码 这是我的 C 程序 char code x48 x31 xc0 include
  • orpd等SSE2指令有什么意义?

    The orpd指令是 压缩双精度浮点值的按位逻辑或 这不是做完 全相同的事情吗por 按位逻辑或 如果是这样 拥有它还有什么意义呢 请记住 SSE1orps https www felixcloutier com x86 orps首先 实
  • Visual Studio 2017 上的简单装配程序

    386 model flat c stack 100h printf PROTO arg1 Ptr Byte data msg1 byte Hello World 0Ah 0 code main proc INVOKE printf ADD
  • 基于已知 XSL 对未知 XML 进行逆向工程

    Solved 在遵循 Matti 的建议后 我删除了自定义函数 一切都很好 原帖 截至今天 我还是 XSLT 的新手 所以我确信这对你们中的许多人来说是理所当然的 无论如何 我的任务是使用第三方供应商提供的数据为我公司的网站创建一个小部件
  • 近调用/跳转表并不总是在引导加载程序中工作

    一般问题 我一直在开发一个简单的引导加载程序 并在某些环境中偶然发现了一个问题 在这些环境中 此类指令不起作用 mov si call tbl SI Call table pointer call call tbl Call print c
  • 从汇编程序获取命令行参数

    通读 专业汇编语言书籍 似乎它提供了用于读取命令行参数的错误代码 我纠正了一点 现在它从段错误变成了读取参数计数 然后是段错误 这是完整的代码 data output1 asciz There are d params n output2
  • 用于预乘 ARGB 的 SSE alpha 混合

    我正在尝试编写一个支持 SSE 的 alpha 合成器 这就是我想出的 首先 混合两个 4 像素向量的代码 alpha blend two 128 bit 16 byte SSE vectors containing 4 pre multi
  • Linux内核页表更新

    在linux x86 中分页 每个进程都有它自己的页面目录 页表遍历从 CR3 指向的页目录开始 每个进程共享内核页目录内容 假设三个句子是正确的 假设某个进程进入内核 模式并更新他的内核页目录内容 地址映射 访问 权利等 问题 由于内核地
  • 如何从 JW Player 5.1 中提取 .flv 流?

    问题是我的互联网连接速度很慢 并且视频不像 YouTube 那样缓冲 因此我想知道是否可以提取 JW Player 中正在流式传输的 flv 文件的 url 并直接下载它 做了一些初步研究 我发现了以下信息 主要视频网址 http ijf1
  • 是否可以在VM内使用VMX CPU指令?

    VM guest 内部的进程是否有可能使用 VMX AMD V VT x CPU 指令 然后由外部 VMM 处理而不是直接在 CPU 上处理 Edit 假设外部VM使用VMX本身来管理其虚拟客户机 即它在Ring 1中运行 如果可能的话 是
  • Visual Studio 2012 本机 C++ DLL x86 编译

    我最近将我的工具集从 Win 7 x86 Visual Studio 2010 升级到 Win 8 x64 Visual Studio 2012 但是 现在我的本机 C dll 编译为 x64 而不是 x86 除了将代码移至新操作系统并将其
  • 在 x86 汇编中将 64 位常量移至内存

    我正在使用 Intel x64 程序集 NASM 编译器 尝试将 0x4000000000000000 常量移至内存 该常量在 ieee 754 标准双精度中应等于 2 0 我正在使用的代码是 define two 0x4000000000
  • 我们可以拥有一台只用寄存器作为内存的计算机吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 寄存器是计算机中最快的存储器 那么如果我们想构建一台只有寄存器甚至没有缓存的计算机 可能吗 我什至考虑用寄存器代替磁盘 尽管它们本质上是易
  • 为什么在展开的 ADD 循环内重新初始化寄存器会使其运行速度更快,即使循环内有更多指令?

    我有以下代码 include
  • 整数溢出问题

    我不断遇到整数溢出问题 我不知道如何解决它 有人可以帮忙吗 edx 包含 181 eax 包含 174 xor eax edx mov edx 2 div edx 假设你谈论的是x86 div edx这实际上没有意义 32位div将edx

随机推荐

  • ZF2 SessionManager 用法

    我是 ZF2 的新手 不太习惯如何做事 我想使用会话来跟踪用户 记住我 我在课堂上有这样的代码 sessionManager new Zend Session SessionManager sessionManager gt remembe
  • 聚合物纸张输入和表格提交

    我正在开发论坛主题并开始使用 Web 组件 但表单元素不起作用 我有这样的东西
  • Mac QuickTime 组件调试

    首先 我是Mac编程的新手 我已经下载了马卡姆项目 http webcam osx sourceforge net index html并使用 Xcode 4 1 成功将调试版本编译为 32 位代码 输出是一个 QuickTime 组件 我
  • 如何读取作为 Blob 文件存储在 Azure 存储中的 Excel 文件

    我想使用 C 中的 Epplus 包读取作为 Blob 存储在 Azure 存储容器中的 Excel 文件 我尝试用这段代码做一些事情 string uri blob Uri AbsoluteUri FileInfo fileInfo ne
  • 如何在 CLR 存储过程中使用实体框架?

    我期待将所有逻辑 作为操作 Entity Framework 4 对象实现 移至服务器端 它看起来会很简单 由于应用程序结构 并且有益 因为我只有一台旧笔记本电脑作为客户端和一台运行 SQL Server 2008 的强大服务器 并且为逻辑
  • 在 Java 中存储颜色 - byte;byte;byte 与 byte[3] 与 int

    我需要存储大量 RGB 颜色对象 对于一些常见用途 它们占用了我的应用程序总内存的 8 到 12 我目前将其定义如下 class MyColor byte red byte green byte blue 我假设 大多数 JVM 实际上对每
  • Python:如何在列表中查找重复项并通过添加渐进字母重命名它们来更新这些重复项

    我有一个这样的项目列表 T1 T2 T2 T2 T2 T3 T3 我需要确保重复项被重命名并添加了渐进字母 如下所示 T1 T2A T2B T2C T2D T3A T3B 但前提是同一项目出现超过 1 次 另外 是否可以在不生成新列表的情况
  • Code::Blocks 无法编译应用程序

    这是我的你好世界代码 include
  • Mongoid 和 has_secure_password

    我正在尝试使用 mongoid 而不是活动模型来使用 Rails 3 1 身份验证 class User include Mongoid Document include ActiveModel SecurePassword has sec
  • 在哪里可以找到 Oracle JVM 的默认 -Xss(堆栈大小)值?

    有没有人找到过列出默认值的单个 JVM 文档 Xss不同版本的 Oracle JVM 和不同操作系统的值 我已经能够在中找到这张表jrockit 文档 http download oracle com docs cd E13150 01 j
  • 单例组件不能依赖于作用域组件

    在工作的同时Android应用程序使用Dagger2用于定义时的依赖注入Dagger组件我收到此错误 Error 13 1 error This Singleton component cannot depend on scoped com
  • 实现 copy.deepcopy() 克隆函数

    仅用于教育目的 我正在尝试实现一个克隆功能copy deepcopy 经过一些有趣的代码玩弄和谷歌搜索后 我想出了以下函数 def my deepcopy data if isinstance data dict result for ke
  • 为什么里氏代换原理需要论证是逆变的?

    其中一项规则是里氏替换原则 https en wikipedia org wiki Liskov substitution principle施加在派生类中的方法签名是 子类型中方法参数的逆变 如果我理解正确的话 就是说派生类的重写函数应该
  • 如何设置JQGRID ROW背景颜色?

    我想根据条件设置 JQGRID 行 bgcolor 怎么做 我正在使用 php 提前致谢 look at jqGrid 根据单元格值对网格中的整行进行着色 https stackoverflow com questions 2931732
  • 使用 REST API 将测试结果与测试用例链接起来

    我们使用 cypress 为 Web 应用程序实现了 UI 测试 并且它们在 Azure DevOps 中的发布管道上执行 我们需要将 ui 测试和测试结果与测试计划中定义的测试用例链接起来 为了将 ui 测试与测试用例链接起来 我们使用
  • 什么是MySQL排序规则,如何在实践中使用它?

    假设我想用 4 种语言用一些奇怪的语言制作一个搜索引擎 英语 瑞典 希伯来语 阿拉伯 我如何在 MySQL 中设置排序规则 排序规则定义 用于存储字符的字符集 UTF 8 ISO 8859等 排序和呈现规则 如果您想要不同的语言 正如您提到
  • DXF文件块中的polyLines顶点和点之间有什么关系?

    DXF文件块中的折线和点之间有什么关系 如果我提供档差表文件 如何连接它们 另一个问题 缩放点太少 所以效果不好 如下图所示 作为第一种答案 如果缩放点太少 是否需要在两个已知缩放点形成的圆弧上生成新的缩放点 不存在这样的关系 DXF 格式
  • try catch和finally块的执行顺序

    我对 try catch 和 finally 块执行的顺序感到困惑 我还想知道什么时候应该使用try catch块以及应该在try catch块中放入什么 我还想知道 try 块中是否出现了一些异常 那么如果采取了与 try 块相对应的操作
  • 对多个Mysql语句进行分组以获取多个状态的计数[重复]

    这个问题在这里已经有答案了 目前 我正在使用 Mysql 和 CodeIgniter 在特定时间范围内从数据库中获取条目 每个条目在数据库中的状态为 D N Y 现在 为了显示此数据 我对每个状态都有一个不同的语句 我想将其分组为 1 个语
  • 使用 EBX 寄存器进行内存访问的模式是什么?

    我正在学习逆向工程的基础知识 在逆向crackme时 我碰巧在几乎每个函数的开头都看到了这种模式 pushl ebp movl esp ebp pushl ebx because ebx is a callee saved register