为什么堆栈的顶部有我们的输入?
对于上下文,这是汇编代码和所需的知识。然后,我将分解组件,看看我的理解是否正确。
我的主要问题是:为什么堆栈的顶部具有我们的输入?
这是main
哪个调用phase_2
0x00000000000014b9 <+112>: call 0x1200 <puts@plt>
0x00000000000014be <+117>: call 0x1c56 <read_line>
0x00000000000014c3 <+122>: mov rdi,rax
0x00000000000014c6 <+125>: call 0x15cb <phase_2>
0x00000000000014cb <+130>: call 0x1d9e <phase_defused>
这是phase_2
是从main
调用的。 phase_2
函数调用read_six_numbers
。
; phase_2
0x00000000000015cb <+0>: endbr64
0x00000000000015cf <+4>: push rbp
0x00000000000015d0 <+5>: push rbx
0x00000000000015d1 <+6>: sub rsp,0x28
0x00000000000015d5 <+10>: mov rax,QWORD PTR fs:0x28
0x00000000000015de <+19>: mov QWORD PTR [rsp+0x18],rax
0x00000000000015e3 <+24>: xor eax,eax
0x00000000000015e5 <+26>: mov rsi,rsp
0x00000000000015e8 <+29>: call 0x1c11 <read_six_numbers>
0x00000000000015ed <+34>: cmp DWORD PTR [rsp],0x1
0x00000000000015f1 <+38>: jne 0x15fd <phase_2+50>
0x00000000000015f3 <+40>: mov rbx,rsp
0x00000000000015f6 <+43>: lea rbp,[rsp+0x14]
0x00000000000015fb <+48>: jmp 0x1612 <phase_2+71>
0x00000000000015fd <+50>: call 0x1be5 <explode_bomb>
0x0000000000001602 <+55>: jmp 0x15f3 <phase_2+40>
0x0000000000001604 <+57>: call 0x1be5 <explode_bomb>
0x0000000000001609 <+62>: add rbx,0x4
0x000000000000160d <+66>: cmp rbx,rbp
0x0000000000001610 <+69>: je 0x161d <phase_2+82>
0x0000000000001612 <+71>: mov eax,DWORD PTR [rbx]
0x0000000000001614 <+73>: add eax,eax
0x0000000000001616 <+75>: cmp DWORD PTR [rbx+0x4],eax
0x0000000000001619 <+78>: je 0x1609 <phase_2+62>
0x000000000000161b <+80>: jmp 0x1604 <phase_2+57>
0x000000000000161d <+82>: mov rax,QWORD PTR [rsp+0x18]
0x0000000000001622 <+87>: xor rax,QWORD PTR fs:0x28
0x000000000000162b <+96>: jne 0x1634 <phase_2+105>
0x000000000000162d <+98>: add rsp,0x28
0x0000000000001631 <+102>: pop rbx
0x0000000000001632 <+103>: pop rbp
0x0000000000001633 <+104>: ret
0x0000000000001634 <+105>: call 0x1220 <__stack_chk_fail@plt>
这是read_six_numbers
,在sthep_2
中称为。
; read_six_numbers
0x0000000000001c11 <+0>: endbr64
0x0000000000001c15 <+4>: sub rsp,0x8
0x0000000000001c19 <+8>: mov rdx,rsi
0x0000000000001c1c <+11>: lea rcx,[rsi+0x4]
0x0000000000001c20 <+15>: lea rax,[rsi+0x14]
0x0000000000001c24 <+19>: push rax
0x0000000000001c25 <+20>: lea rax,[rsi+0x10]
0x0000000000001c29 <+24>: push rax
0x0000000000001c2a <+25>: lea r9,[rsi+0xc]
0x0000000000001c2e <+29>: lea r8,[rsi+0x8]
0x0000000000001c32 <+33>: lea rsi,[rip+0x16ca] # 0x3303
0x0000000000001c39 <+40>: mov eax,0x0
0x0000000000001c3e <+45>: call 0x12c0 <__isoc99_sscanf@plt>
0x0000000000001c43 <+50>: add rsp,0x10
0x0000000000001c47 <+54>: cmp eax,0x5
0x0000000000001c4a <+57>: jle 0x1c51 <read_six_numbers+64>
0x0000000000001c4c <+59>: add rsp,0x8
0x0000000000001c50 <+63>: ret
0x0000000000001c51 <+64>: call 0x1be5 <explode_bomb>
这是我对phase_2
的理解:
0x00000000000015cb <+0>: endbr64
0x00000000000015cf <+4>: push rbp
0x00000000000015d0 <+5>: push rbx
0x00000000000015d1 <+6>: sub rsp,0x28
上面是函数序言。 我们保存以前的基础指针。 我们还拥有Callee保存的寄存器rbx
- 我们正在推动保存rbx
。 请注意pop rbx
最后,我们还原RBX的值,因为我们在功能中覆盖rbx
。 RBX
是系统v Callee保存的寄存器 如果Callee希望使用寄存器rbx
,rsp
,rbp
和r12
- r15
代码>,它必须在将控件返回到呼叫者之前还原其原始值。
0x00000000000015d5 <+10>: mov rax,QWORD PTR fs:0x28
以上是堆栈金丝雀。如果修改后,程序结束,
0x00000000000015de <+19>: mov QWORD PTR [rsp+0x18],rax
我们将返回值移动并将其放入本地var中。 上一个值是read_line的返回值。
0x00000000000015e3 <+24>: xor eax,eax
然后,我们清除eax
。 我们清除rax
,因为清除值还取消了rax
的最高值。
0x00000000000015e5 <+26>: mov rsi,rsp
为什么要移动堆栈顶部的地址,即rsp
并将其放入rsi
中? 我猜堆栈的顶部是我们6位数字的开始,但是 我们的6位数字是如何从堆栈开始的?
0x00000000000015ed <+34>: cmp DWORD PTR [rsp],0x1
我们验证rsp
点的值是0x1
。
0x00000000000015f1 <+38>: jne 0x15fd <phase_2+50>
如果没有,我们结束功能。
0x00000000000015f3 <+40>: mov rbx,rsp
我们将堆栈顶部地址的rsp
的内容移至rbx
中。
有什么原因吗?
0x00000000000015f6 <+43>: lea rbp,[rsp+0x14]
我们将本地变量移至rbp
中。
为什么这样做?这个本地变量从未初始化?我是否将随机变量移动到rbp
中,为什么RBP
?
与我们移动RSP + 0x18
时有关。 它更大4个字节,所以我们要访问数组 + 1吗?
0x0000000000001609 <+62>: add rbx,0x4
循环的开始。 我们通过一个整数将数组/字符串递增。
0x0000000000001612 <+71>: mov eax,DWORD PTR [rbx
早些时候,我们将堆栈的顶部移至rbx
中。 我们解除了指向的内容。 现在,我们将其放入eax
中。
0x0000000000001614 <+73>: add eax,eax
我们加倍eax 中的内容是什么,
0x0000000000001616 <+75>: cmp DWORD PTR [rbx+0x4],eax
0x0000000000001619 <+78>: je 0x1609 <phase_2+62>
为什么我们要比较我认为的array [i + 1]
,而不仅仅是array [i]
? 什么是 rbx
,为什么它保留了我们要比较输入的值?
,其余的就是功能结语,并且检查堆栈金丝雀。
For context, here is the assembly code and the required knowledge. I will then break down the assembly to see if my understanding is correct.
My main question is: Why is that the top of stack has our input?
Here is main
which calls phase_2
0x00000000000014b9 <+112>: call 0x1200 <puts@plt>
0x00000000000014be <+117>: call 0x1c56 <read_line>
0x00000000000014c3 <+122>: mov rdi,rax
0x00000000000014c6 <+125>: call 0x15cb <phase_2>
0x00000000000014cb <+130>: call 0x1d9e <phase_defused>
Here is the phase_2
which is called from main
. The phase_2
function calls read_six_numbers
.
; phase_2
0x00000000000015cb <+0>: endbr64
0x00000000000015cf <+4>: push rbp
0x00000000000015d0 <+5>: push rbx
0x00000000000015d1 <+6>: sub rsp,0x28
0x00000000000015d5 <+10>: mov rax,QWORD PTR fs:0x28
0x00000000000015de <+19>: mov QWORD PTR [rsp+0x18],rax
0x00000000000015e3 <+24>: xor eax,eax
0x00000000000015e5 <+26>: mov rsi,rsp
0x00000000000015e8 <+29>: call 0x1c11 <read_six_numbers>
0x00000000000015ed <+34>: cmp DWORD PTR [rsp],0x1
0x00000000000015f1 <+38>: jne 0x15fd <phase_2+50>
0x00000000000015f3 <+40>: mov rbx,rsp
0x00000000000015f6 <+43>: lea rbp,[rsp+0x14]
0x00000000000015fb <+48>: jmp 0x1612 <phase_2+71>
0x00000000000015fd <+50>: call 0x1be5 <explode_bomb>
0x0000000000001602 <+55>: jmp 0x15f3 <phase_2+40>
0x0000000000001604 <+57>: call 0x1be5 <explode_bomb>
0x0000000000001609 <+62>: add rbx,0x4
0x000000000000160d <+66>: cmp rbx,rbp
0x0000000000001610 <+69>: je 0x161d <phase_2+82>
0x0000000000001612 <+71>: mov eax,DWORD PTR [rbx]
0x0000000000001614 <+73>: add eax,eax
0x0000000000001616 <+75>: cmp DWORD PTR [rbx+0x4],eax
0x0000000000001619 <+78>: je 0x1609 <phase_2+62>
0x000000000000161b <+80>: jmp 0x1604 <phase_2+57>
0x000000000000161d <+82>: mov rax,QWORD PTR [rsp+0x18]
0x0000000000001622 <+87>: xor rax,QWORD PTR fs:0x28
0x000000000000162b <+96>: jne 0x1634 <phase_2+105>
0x000000000000162d <+98>: add rsp,0x28
0x0000000000001631 <+102>: pop rbx
0x0000000000001632 <+103>: pop rbp
0x0000000000001633 <+104>: ret
0x0000000000001634 <+105>: call 0x1220 <__stack_chk_fail@plt>
Here is read_six_numbers
which is called in phase_2
.
; read_six_numbers
0x0000000000001c11 <+0>: endbr64
0x0000000000001c15 <+4>: sub rsp,0x8
0x0000000000001c19 <+8>: mov rdx,rsi
0x0000000000001c1c <+11>: lea rcx,[rsi+0x4]
0x0000000000001c20 <+15>: lea rax,[rsi+0x14]
0x0000000000001c24 <+19>: push rax
0x0000000000001c25 <+20>: lea rax,[rsi+0x10]
0x0000000000001c29 <+24>: push rax
0x0000000000001c2a <+25>: lea r9,[rsi+0xc]
0x0000000000001c2e <+29>: lea r8,[rsi+0x8]
0x0000000000001c32 <+33>: lea rsi,[rip+0x16ca] # 0x3303
0x0000000000001c39 <+40>: mov eax,0x0
0x0000000000001c3e <+45>: call 0x12c0 <__isoc99_sscanf@plt>
0x0000000000001c43 <+50>: add rsp,0x10
0x0000000000001c47 <+54>: cmp eax,0x5
0x0000000000001c4a <+57>: jle 0x1c51 <read_six_numbers+64>
0x0000000000001c4c <+59>: add rsp,0x8
0x0000000000001c50 <+63>: ret
0x0000000000001c51 <+64>: call 0x1be5 <explode_bomb>
Here is my understanding of that is happening in phase_2
:
0x00000000000015cb <+0>: endbr64
0x00000000000015cf <+4>: push rbp
0x00000000000015d0 <+5>: push rbx
0x00000000000015d1 <+6>: sub rsp,0x28
Above is the function prologue.
We save the previous base pointer.
We also have the callee saved register rbx
-- we are pushing to save rbx
.
Note the pop rbx
at the end, we restore the value of rbx because we overwrite rbx
in the function.rbx
is a system V callee saved register
If the callee wishes to use registers RBX
, RSP
, RBP
, and R12
–R15
, it must restore their original values before returning control to the caller.
0x00000000000015d5 <+10>: mov rax,QWORD PTR fs:0x28
The above is a stack canary. If modified, the program ends
0x00000000000015de <+19>: mov QWORD PTR [rsp+0x18],rax
We move the return value and put it in a local var.
The previous value is the return value of read_line.
0x00000000000015e3 <+24>: xor eax,eax
We then clear eax
.
we clear rax
because clearing the values also cancels out the top values of rax
.
0x00000000000015e5 <+26>: mov rsi,rsp
Why are moving the address at the top of the stack, i.e., rsp
and placing it into rsi
?
I'm guessing the top of stack is the beginning of our 6 digits, but
how did our 6 digits get to begin at the of the stack?
0x00000000000015ed <+34>: cmp DWORD PTR [rsp],0x1
We verify that the value where rsp
points is 0x1
.
0x00000000000015f1 <+38>: jne 0x15fd <phase_2+50>
If not, we end the function.
0x00000000000015f3 <+40>: mov rbx,rsp
We move the contents of rsp
, the address of the top of the stack, into rbx
.
Is there any reason for this?
0x00000000000015f6 <+43>: lea rbp,[rsp+0x14]
We move a local variable into rbp
.
Why is this done? This local variable was never initialized? Are me moving a random variable into rbp
and why rbp
?
Is it related to when we moved rsp + 0x18
.
It is 4 bytes larger, so are we accessing an array + 1?
0x0000000000001609 <+62>: add rbx,0x4
Beginning of the loop.
We increment the array/string by one integer.
0x0000000000001612 <+71>: mov eax,DWORD PTR [rbx
Earlier we moved the top of the stack into rbx
.
We dereference what was pointed.
We now put that into eax
.
0x0000000000001614 <+73>: add eax,eax
We double what is in eax
0x0000000000001616 <+75>: cmp DWORD PTR [rbx+0x4],eax
0x0000000000001619 <+78>: je 0x1609 <phase_2+62>
Why are we comparing the what I assume to be array[i + 1]
and not just array[i]
?
What is rbx
and why does it it hold the value we are comparing out input to?
And the rest is function epilogue and the checking the stack canary.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论