bomb.c phase1 code:
bomb.s phase1 code:
phase_1的参数被存入寄存器esi, 之后调用strings_not_equal函数,
401338 对应 string_not_equal函数:
调用strings_not_equal函数之后,会进行3次push(stack操作), 然后将寄存器rsi的值放入寄存器rbp(callee 存储临时变量的寄存器)
使用gdb 查看寄存信息
在调用strings_not_equal函数时,寄存器rdi和寄存器rsi的内容是不一样,更新寄存器rax
BOOM!!!
bomb.c phase_2 代码:
bomb.s phase_2 代码:
400f02,将寄存器rsp的内容赋值给rsi, 然后调用read_six_numbers函数,
bomb.s read_six_numbers代码:
在read_six_numbers函数中,将rsi分别赋值给不同的存储临时变量的寄存器。sscanf函数是C语言中的一个输入函数,用于从字符串中按照指定的格式提取数据。
sscanf 函数的参数格式是"%d %d %d %d %d %d"
sscanf 函数获取的参数分别是 rsi+0x4 or rcx, rsi+0x14 or rsp+0x8, rsi+0x10 or rsp, rsi+oxc or r9, rsi+0x8 or r8, 和 0x4025c3。 rsp就是第一个参数,…。
read_six_numbers 函数执行过后,会到phase_2函数:
cmpl 比较 0x1 和 rsp存储的值,然后跳转到400f30, 将rsp+0x4保存到rbx, 将rsp+0x18保存到rbp,然后跳转到400f17, rbx-0x4就是rsp的地址, rax or eax保存rsp的值,rbx存储(rax+rax)的值,比较rbx和rax的值, int类型是4字节,所以rsp+0x4就是第二个参数, 所以第二个参数应该是第一个参数的二倍,然后跳转到400f25, 更新rbx+0x4, 以此类推,第三个参数是第二个参数的二倍。
objdump是一个用于分析可执行文件、目标文件和共享库的工具。它可以显示这些文件的二进制指令、符号表、段信息、重定位表等内容,帮助开发者了解和调试程序。
objdump的常用选项和用法如下:
显示二进制指令:objdump -d
,这会以汇编代码的形式显示可执行文件或目标文件的二进制指令。
显示符号表:objdump -t
,这会显示可执行文件或目标文件的符号表,包括函数名、变量名等信息。
显示段信息:objdump -h
,这会显示可执行文件或目标文件的段信息,包括段名、段地址、段大小等。
显示重定位表:objdump -r
,这会显示可执行文件或目标文件的重定位表,用于动态链接和地址重定位。
显示动态符号表:objdump -T
,这会显示可执行文件或共享库的动态符号表,包括动态链接的函数和变量。
反汇编指定函数:objdump -d
,这会反汇编指定节(section)的内容,可以用于分析特定函数的汇编代码。
除了上述常用选项,objdump还提供了许多其他选项,可以根据需要进行使用。可以通过 objdump --help
命令查看完整的选项列表和使用说明。
请注意,objdump是一个命令行工具,可以在Linux、Unix和类似系统中使用。在Windows系统上,可以使用MinGW或Cygwin等工具来使用objdump。
在使用GDB调试程序时,可以使用info registers
命令来打印寄存器的值。该命令会显示所有通用寄存器和特殊寄存器的当前值。
以下是在GDB中打印寄存器值的步骤:
start
命令开始执行程序,或者使用run
命令来执行程序。info registers
命令。除了info registers
命令外,还可以使用以下命令来打印特定寄存器的值:
info register <寄存器名>
:打印指定寄存器的值,例如info register eax
。print $<寄存器名>
:打印指定寄存器的值,例如print $rax
。需要注意的是,寄存器的名称在不同的体系结构和编译器中可能有所不同。因此,需要查阅相关文档或参考特定体系结构的寄存器命名约定来确定正确的寄存器名称。
要查看寄存器中存储的地址的值,可以使用GDB中的x
(examine)命令。该命令用于查看内存中的数据,可以通过指定地址和格式来打印出相应的值。
以下是在GDB中查看寄存器中地址的值的步骤:
start
命令开始执行程序,或者使用run
命令来执行程序。info registers
命令以查看寄存器的值。rax
寄存器中的地址)。x/<格式> $<寄存器名>
命令,其中<格式>
是指定打印格式的选项,常用的格式有:
x
:十六进制格式d
:十进制格式s
:字符串格式i
:汇编指令格式f
:浮点数格式rax
寄存器中的地址,可以输入x/x $rax
。需要注意的是,寄存器中存储的地址必须是有效的,并且对应的内存区域必须可访问,否则将打印出错误信息。
在汇编语言中,SUB(Subtract)指令用于执行两个操作数的减法运算,并将结果存储到目标操作数中。SUB指令有多种形式,可以用于不同的数据类型和寻址方式。
以下是x86汇编语言中SUB指令的一些常见形式:
SUB reg, reg/mem:执行两个寄存器或内存位置的减法操作,并将结果存储到第一个操作数中。例如,SUB EAX, EBX
将EBX的值从EAX中减去,并将结果存储回EAX寄存器。
SUB reg, imm:执行寄存器和立即数之间的减法操作,并将结果存储到寄存器中。例如,SUB EAX, 10
将EAX寄存器的值减去10,并将结果存储回EAX寄存器。
SUB mem, reg/imm:执行内存位置和寄存器或立即数之间的减法操作,并将结果存储到内存位置中。例如,SUB [EBX], ECX
将ECX寄存器的值从EBX指向的内存位置中减去,并将结果存储回该内存位置。
SUB指令的结果影响标志位寄存器(Flags Register),可以通过检查这些标志位来判断减法操作的结果。例如,ZF(Zero Flag)被设置为1表示结果为零,SF(Sign Flag)被设置为1表示结果为负数。
需要注意的是,SUB指令会修改操作数的值,因此在使用之前需要确保操作数的值是正确的,并且结果不会溢出。
在汇编语言中,MOV(Move)指令用于将数据从一个位置复制到另一个位置。它是汇编语言中最常用的指令之一,用于数据的加载、存储和传递。
MOV指令有多种形式,可以用于不同的数据类型和寻址方式。以下是x86汇编语言中MOV指令的一些常见形式:
MOV reg, reg/mem:将一个寄存器或内存位置的值复制到另一个寄存器中。例如,MOV EAX, EBX
将EBX寄存器的值复制到EAX寄存器中。
MOV reg, imm:将一个立即数(常数)复制到寄存器中。例如,MOV EAX, 10
将值10复制到EAX寄存器中。
MOV mem, reg/imm:将一个寄存器或立即数的值复制到内存位置中。例如,MOV [EBX], ECX
将ECX寄存器的值复制到EBX指向的内存位置中。
MOV指令可以用于不同的寻址方式,如直接寻址、间接寻址、基址加变址寻址等。它允许在寄存器和内存之间进行数据传输,以及在寄存器之间进行数据交换。
需要注意的是,MOV指令只能将数据从源位置复制到目标位置,而不能进行数学运算。如果需要进行数学运算,需要使用其他指令,如ADD(加法)、SUB(减法)等。
MOV指令的执行速度非常快,因为它是在寄存器之间直接进行数据传输,而不涉及内存访问。因此,在性能要求较高的场景中,使用MOV指令可以提高程序的执行效率。
在汇编语言中,CALL和CALLQ是两种不同的指令,它们在不同的体系结构中使用。
当执行CALL指令时,会将当前指令的下一条指令的地址(即CALL指令的下一条指令)压入栈中,然后跳转到操作数所指定的子程序或函数的起始地址。
CALLQ指令的操作数可以是一个标签、一个寄存器或一个内存地址。它与CALL指令的区别在于,CALLQ指令使用64位的地址寻址方式,可以访问更大的内存空间。
总结:
CALL指令是在32位x86体系结构中使用的指令,而CALLQ指令是在64位x86-64体系结构中使用的指令。它们的功能相同,都用于调用(跳转到)一个子程序或函数,但是CALLQ指令适用于64位模式下的更大地址空间。
leaq
指令和movq
指令是x86汇编语言中常用的指令,用于处理数据的加载和传输。它们的区别如下:
功能:
leaq
指令用于将一个内存地址或偏移量加载到寄存器中,而不是将内存中的值加载到寄存器。它主要用于计算地址,而不是数据传输。movq
指令用于将一个值从一个位置(寄存器、内存或立即数)复制到另一个位置(寄存器或内存)。语法:
leaq
指令的语法为leaq <源操作数>, <目的操作数>
,其中源操作数是一个内存地址或偏移量,目的操作数是一个寄存器。movq
指令的语法为movq <源操作数>, <目的操作数>
,其中源操作数和目的操作数可以是寄存器、内存位置或立即数。执行时间:
leaq
指令通常比movq
指令更快,因为它只执行地址计算,而不涉及内存读取或写入操作。使用场景:
leaq
指令常用于计算数组元素的地址、计算偏移量、进行指针运算等需要计算地址的场景。movq
指令常用于数据传输、寄存器之间的值交换、常量加载等数据操作场景。总之,leaq
指令用于计算地址,而movq
指令用于数据传输和复制。它们在功能、语法和使用场景上有明显的区别。
rbx
是x86架构中的一个通用寄存器,具有多种功能和用途。以下是rbx
寄存器的主要功能:
通用数据存储:rbx
寄存器可以用于存储通用数据,例如整数、指针或其他数据。它可以用作临时存储器或在算术和逻辑运算中作为操作数使用。
基址寄存器:在内存寻址中,rbx
寄存器可以作为基址寄存器使用。当进行间接寻址时,可以将rbx
寄存器与偏移量相加来计算内存地址。
数组和缓冲区访问:rbx
寄存器经常用于访问数组、缓冲区或数据结构中的元素。通过将数组或缓冲区的基地址存储在rbx
寄存器中,可以使用偏移量或索引来访问特定的元素。
函数调用:rbx
寄存器在函数调用时可以用作通用寄存器。它可以用于存储函数的返回值、参数传递或保存调用者保存的寄存器。
需要注意的是,rbx
寄存器在函数调用过程中可能会被破坏,因此在使用之前需要保存其值,并在使用后进行恢复。此外,rbx
寄存器也可以被用作循环计数器或其他需要通用寄存器的场景。
rbp
是x86架构中的一个通用寄存器,具有多种功能和用途。以下是rbp
寄存器的主要功能:
基址指针:rbp
寄存器通常被用作基址指针寄存器。在函数调用过程中,rbp
寄存器用于指向当前函数的栈帧基址。栈帧是用于存储局部变量、函数参数和其他与函数调用相关的信息的一块内存区域。通过rbp
寄存器可以在栈上定位和访问这些数据。
栈帧指针:rbp
寄存器还可以用作栈帧指针。在函数调用过程中,rbp
寄存器指向栈帧的底部,即局部变量和函数参数的起始位置。通过调整rbp
寄存器的值,可以在栈上分配和释放局部变量的空间。
函数调用:rbp
寄存器在函数调用过程中用于保存调用者的栈帧基址。在函数调用时,调用者的rbp
寄存器的值会被保存到栈上,以便在函数返回后能够恢复调用者的上下文。
栈帧操作:rbp
寄存器可以用于栈帧的操作,如栈帧的创建、销毁和访问。通过调整rbp
寄存器的值,可以在栈上分配和释放内存空间,并通过偏移量访问栈上的局部变量和函数参数。
需要注意的是,rbp
寄存器在函数调用过程中可能会被破坏,因此在使用之前需要保存其值,并在使用后进行恢复。此外,rbp
寄存器还可以用作通用寄存器,存储临时数据或作为操作数使用。
https://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/lectures/05-machine-basics.pdf
https://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/lectures/06-machine-control.pdf
https://www.cs.cmu.edu/afs/cs/academic/class/15213-f15/www/lectures/07-machine-procedures.pdf