第 3 集:使用 GDB 跟踪堆栈和函数调用

admin 2024年6月8日14:00:00评论23 views字数 4434阅读14分46秒阅读模式

第 3 集:使用 GDB 跟踪堆栈和函数调用

欢迎来到《制作小操作系统》第 3 集。

在本集中,我将讨论如何实现堆栈和函数调用。此外,我们将讨论如何使用 GDB 来调试我们的引导加载程序,然后我们利用 bios 中断来清除屏幕并在我们的代码中进行一些重构。最后,我们将为我们的项目创建 make 文件以使事情变得更简单。

linux高级usb安全开发与源码分析

第 3 集:使用 GDB 跟踪堆栈和函数调用

linux

第 3 集:使用 GDB 跟踪堆栈和函数调用

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • windows

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • windows()

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • USB()

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • ()

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • ios

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • windbg

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • ()

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用第 3 集:使用 GDB 跟踪堆栈和函数调用第 3 集:使用 GDB 跟踪堆栈和函数调用

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

  • 第 3 集:使用 GDB 跟踪堆栈和函数调用

让我们深入了解如何实现堆栈,而不是讨论它的重要性。

正如我们在前几集中所讨论的,CPU 具有程序逻辑段的寄存器。

事实上,这是细分的想法。为了告诉 CPU,我们想要内存中称为堆栈的某个地方,我们应该设置一些 CPU 寄存器。

我们的引导加载程序应该适合 512 字节(然后我们创建一个第二阶段引导加载程序来绕过这个限制)并放置地址 0x7c00:

第 3 集:使用 GDB 跟踪堆栈和函数调用

现在我们想在内存中的某个地方找到堆栈。

我们有三个寄存器来做到这一点。堆栈段、堆栈指针和基本指针。

堆栈段保存堆栈信息所在的内存块的地址。

注意:同样,在实模式下,段寄存器乘以16以创建实地址。

堆栈指针寄存器保存堆栈的顶部,基本指针寄存器保存堆栈的底部。

假设我们希望在地址 0x8400 上具有堆栈区域。

为了在地址 0x8400 上具有字节号 33792 的堆栈区域,我们应该将堆栈段寄存器设置为 0x840。因为:

0x8400 = 0840 * 16

我们可以使用以下代码实现它:

mov ax, 0x840
mov ss, ax

以下是内存的更新视图:

第 3 集:使用 GDB 跟踪堆栈和函数调用

在 x86 体系结构上,堆栈在内存中向下增长。(堆栈的增长方向与内存地址增长方向相反。

是时候确定堆栈的大小了。为此,我们将堆栈指针和基本指针都设置为相同的地址。

此地址与堆栈段偏移,并确定堆栈大小。

例如,如果我们将“SP”和“BP”设置为寻址0x2000(是的,这些不是段寄存器,如果我们的意思是0x2000,则无需设置0x200)

结果将是:

第 3 集:使用 GDB 跟踪堆栈和函数调用

如图所示,无论我们在 BP 和 SP 上放置什么,都会偏离 SS.in 以制作 8KB 堆栈的顺序,我们使用以下代码将 0x2000 设置为 SP 和 BP:

mov ax, 0x2000
mov sp, ax
mov bp, ax

为了进行测试,我们将一些值推送并弹出到堆栈中,以确保一切都正确完成:

push ax
push ax
pop bx

现在我们需要测试我们的引导加载程序并检查其运行时。我们还需要检查堆栈值,以确保我们的代码正常工作。

调试器是关键。我们使用 GDB 来调试我们的程序。GDB 在我们的项目中非常方便,因为它可以连接到 qemu 并调试我们的引导加载程序。

因此,要调试我们的程序,首先我们需要告诉 QEMU 加载我们的程序,但不要启动它,并且还要侦听默认端口(即 1234),以便调试器连接到它。

为此,我们将 qemu 命令更改为:

qemu-system-i386 -fda boot.bin -s -S

上述命令的结果是 qemu 等待调试器的连接:

第 3 集:使用 GDB 跟踪堆栈和函数调用

然后我们需要使用以下命令运行 gdb:

gdb boot.asm

然后我们将看到GDB程序打开,如下图所示:

第 3 集:使用 GDB 跟踪堆栈和函数调用

现在是时候将 GDB 连接到 QEMU 了。为此,我们在 gdb 提示符下使用以下命令:

target remote :1234

现在是时候设置断点以启动程序,然后运行它了。

为此,我们在 gdb 中使用以下命令:

b *0x7c00

此命令在地址 0x7c00 处创建一个断点,该断点是引导加载程序代码的开头。

现在是时候查看我们的代码了。为此,我们告诉 GDB 在内存中显示操作码的汇编等效物。为此,我们使用:

layout asm

这将导致以下输出:

第 3 集:使用 GDB 跟踪堆栈和函数调用

如果已准备好前进到断点,请立即执行此操作。为此,我们在 gdb 命令提示符下键入以下命令:

continue

结果如下:

第 3 集:使用 GDB 跟踪堆栈和函数调用

如您所见,GDB 将我们的代码反汇编为 32 位汇编操作码,因此使用 EAX、EBP 和 ...

要解决此问题,我们应该告诉 GDB 将操作码反汇编为 16 位,而不是 32 位。

标准方法是以下命令:

set architecture i8086

但这行不通。这不是我们的错,但据我了解,gdb 从目标获取架构,在我们的例子中是 qemu i386,并且因为它是一台 32 位机器,gdb 更喜欢继续使用目标架构,不服从我们的设置架构命令。

因此,要解决这个问题,我们需要外部帮助。我从这个 GitHub gist 中找到了一个解决方案。

我们需要两个名为“target.xml”和“i386–32bit.xml”的文件。(我把这些文件放在一个名为“gdb_asset”的文件夹中,你可以在项目仓库中访问它们)

为了解决这个问题,我们在 gdb 命令提示符下运行以下命令:

set tdesc filename gdb_asset/target.xml

然后我们需要通过触发反汇编命令来刷新装配布局:

disassemble $eip-10, 5

结果是:

第 3 集:使用 GDB 跟踪堆栈和函数调用

如您所见,GDB 现在将操作码反汇编为 16 位汇编操作码。

现在,我们可以在程序中使用 Step Command 来执行第 1 步指令。为此,请执行以下操作:

stepi

或者干脆

si

第 3 集:使用 GDB 跟踪堆栈和函数调用

如图底部所示,程序计数器或 IP 寄存器现在位于 0x7c03。这意味着 CPU 已在地址 0x7c00 执行操作码,即 mov ax,0x7c0,现在正在等待我们的命令执行0x7c03。

如果我们再次键入“si”命令,它将继续突出显示的指令并等待我们的命令。

但就目前而言,我更愿意看看“ax”寄存器是否真的有0x7c0值。要检查寄存器值,我们使用以下命令:

info registers register_name

在我们的例子中,它将是:

i r ax

(是的,我们可以使用 I R 代替 Info Register)

结果是:

ax    0x7c0     1984

结果说明:寄存器名称,十六进制值,十进制值

现在,我们可以使用多个步骤,直到达到0x7c12或推送指令。

第 3 集:使用 GDB 跟踪堆栈和函数调用

在此地址,CPU 刚刚执行了 push ax,将寄存器 ax 中的值推送到堆栈。

我们想检查堆栈,看看它是否有效。

要做到这一点,首先,我们从前面的说明中知道“斧头”0x2000价值。

现在要检查它是否真的推送到堆栈,有一些计算:

正如我们之前所讨论的,“sp”显示了堆栈的顶部。它的价值0x2000。

现在,通过将某些东西推送到堆栈,“sp”有一个新值,即 0x2000–2(2 是推送到堆栈的“AX”的大小)

0x2000 - 0x02 = 0x1ffe

我们知道 “sp” 在堆栈段区域内,因此我们应该将堆栈指针地址添加到堆栈段,以显示内存中的 “sp” 地址:

0x8400 + 0x1ffe = 0xa3fe

现在,如果我们使用以下命令查看此地址:

x/4x 0xa3fe

我们将看到以下结果:

第 3 集:使用 GDB 跟踪堆栈和函数调用

如您所见0x2000在堆栈上。任务通过!

第 3 集:使用 GDB 跟踪堆栈和函数调用

现在我们已经成功地实现了一个堆栈,我们可以用它来定义函数并调用它们,所有这些都挂载在堆栈上。

例如,这是一个清除屏幕的BIOS功能。我只是从 Joe Bergeron 的博客中复制了它:


clear_screen:
mov ah, 0x07 ; tells BIOS to scroll down window
mov al, 0x00 ; clear entire window
mov bh, 0x07 ; white on black
mov cx, 0x00 ; specifies top left of screen as (0,0)
mov dh, 0x18 ; 18h = 24 rows of chars
mov dl, 0x4f ; 4fh = 79 cols of chars
int 0x10 ; calls video interrupt ret

ret

然后我们调用这个函数,而不是跳跃。通过调用函数,返回地址自动推送到堆栈,并在执行“ret”指令时从堆栈中弹出并跳转到该地址。

我们可以为我们的程序定义一个例程,例如:首先清除屏幕,然后打印 Hello, world!在屏幕上,然后完成。

现在我们有了函数,我们可以像这样做:

call clear_screen
call print_text
call finish

到目前为止,我们的程序是这样的:

bits 16

mov ax, 0x7c0
mov ds, ax

mov ax, 0x840
mov ss, ax

mov ax, 0x2000
mov sp, ax
mov bp, ax

push ax
push ax
pop bx

call clear_screen
call print_text
call finish


print_text:

mov ah, 0x0E
mov bh, 0x00
mov bl, 0x00
mov si, 0
mov cx, 0

print_loop:
cmp cx, 14
je exit
lea si, msg
add si, cx
mov al, [si]
int 0x10
inc cx
jmp print_loop
ret

clear_screen:

mov ah, 0x07 ; tells BIOS to scroll down window
mov al, 0x00 ; clear entire window
mov bh, 0x07 ; white on black
mov cx, 0x00 ; specifies top left of screen as (0,0)
mov dh, 0x18 ; 18h = 24 rows of chars
mov dl, 0x4f ; 4fh = 79 cols of chars
int 0x10 ; calls video interrupt
ret


exit:
mov ax, 0


finish:
hlt



msg: db "Hello, world!"
times 510-($-$$) db 0
dw 0xAA55

结果将是:

第 3 集:使用 GDB 跟踪堆栈和函数调用

似乎有很多步骤可以组装和运行 QEMU,然后启动 GDB 并对其进行配置。

所以我做了一个 makefile,让我们的生活更轻松:

boot.bin: boot.asm
nasm -f bin -o boot.bin boot.asm


.PHONY: run debug

run:
$(MAKE) && qemu-system-i386 -fda boot.bin -s -S

debug:
#qemu-system-i386 -s -S -fda boot.bin
gdb -ex "target remote :1234" -ex "b *0x7c00" -ex "set tdesc filename gdb_asset/target.xml" -ex "layout asm"

通过使用这个 makefile,我们可以简单地在单独的终端中运行 “make run”,然后运行 “make debug”。这将打开连接到 qemu 的 gdb,通过 16 位架构进行反汇编,应如下所示:

第 3 集:使用 GDB 跟踪堆栈和函数调用

在 gdb 命令提示符下输入“continue”后,gdb 将跳转到地址0x7c00,这标志着我们程序的开始。

这就是现在的全部内容。如果您有兴趣访问此项目的代码以及更多内容,请查看以下 GitHub 存储库:

https://github.com/flydeoo/mya

另外,请查看第 3 集的发布:

https://github.com/flydeoo/mya/releases/tag/v0.031

感谢您的阅读,下期再见。

原文始发于微信公众号(安全狗的自我修养):第 3 集:使用 GDB 跟踪堆栈和函数调用

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年6月8日14:00:00
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   第 3 集:使用 GDB 跟踪堆栈和函数调用https://cn-sec.com/archives/2830480.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息