我正在学习英特尔X86汇编和逆向工程,因为我想创建游戏作弊。
我试图理解内存和堆栈是如何工作的。我知道这是后进先出,有些人说把它想象成一摞书,你往上面加,从上面拿。但是你要想象它是向下的而不是向上的吗?
For example:
push 1
push 2
push 3
pop x
push would equal:
1
2
3 <- esp
pop would equal:
1
2 <- esp
垂直上下不是唯一可能的可视化方式-我们也可以使用水平:
lower addresses ... higher addresses
+----------------------------------------------+
| (free stack) | 3 | 2 | 1 | (in use) |
+----------------------------------------------+
^
|
esp
这个可视化允许以"forward "方向,从左到右;参数列表也从最后推到第一个。Push向左扩展堆栈,这感觉仍然很自然。
YMMV
你基本上可以按照你喜欢的方式可视化堆栈。更常见的表示堆栈的方法是说堆栈从高地址到低地址递减。
为了支持这种叙述,堆栈通常被可视化为
+------------------+ <- top of memory = 0x10000
| used_stack |
| |
+------------------+
| local variables | <- top of stack = 0xff00
| [red zone] | <- red zone is guaranteed to be unused
| unused memory | by exception handlers
| |
| ---------------- |
| HEAP | <- dynamically allocated memory
+------------------+ address = 0x0000
我个人认为这是不直观的,因为这颠倒了其他内存结构或数组通常呈现的顺序。在CPU中没有down
| offset description. hex dump
+-----------+----------------+------------------------
| 0 | char header[4] | 01 02 03 04
| 8 | int16 width | 08 00
| 10 | int16 height | 04 00
| 12 | int32 size | 20 00 00 00
vs.
struct FileFormat {
char header[4];
int16_t width{8};
int16_t height{4};
int32_t size{32};
};
当堆栈被想象成钟乳石(像冰柱一样悬挂)时,它在物理上是向下生长的,但它与现实生活中的堆栈失去了联系,就像一堆盘子或一堆书一样,它们会向上生长。
A B
+-------------+ +--------------+
| ceil of stk | <-high addresses | unused | <-small addresses
| 20 00 00 00 | | |
| 08 00 04 00 | | |
| 01 02 03 04 | <-- top of stack -> | 01 02 03 04 |
| | | 08 00 04 00 |
| | | 20 00 00 00 |
| unused/heap | |bottom of stck|
+-------------+ +--------------+
格式A更方便,至少当你有像
这样的书面文本时push 1
push 2
push 3
在样式A中,它是这样表示的,在样式B中,值是相反的。
<<
当系统是单cpu并且通常运行单线程进程来控制整个系统时,使堆栈变小是一个特别有用的选择。堆栈被放置在可用内存的顶部,堆被放置在底部,它们会互相增长以满足对方。在现代多线程进程中,它的工作并不那么简单,每个线程都需要一个独立的堆栈,尽管堆可以由整个进程共享。在创建线程时,将选择线程的最大大小,如果空间耗尽,则需要生成某种错误条件。但是,请注意,这里的"线程"one_answers"进程"完全取决于操作系统的设计,CPU只是被告知要运行什么代码。