C/C++汇编学习(五)——尝试修改汇编代码并观察不同的执行结果

发布时间:2024年01月09日

????????在汇编语言中进行修改并观察不同的执行结果是理解汇编指令和程序行为之间关系的一种有效方式。让我们以一个简单的例子来演示这一点。

目录

示例:C++代码

对应的汇编代码

将汇编转为可执行文件

总结

1. 编写和编译C++代码

2. 修改汇编代码

3. 重新编译汇编代码

4. 运行和验证结果

结论


示例:C++代码

????????假设我们有以下C++代码:

#include <iostream>

int main() 
{
   int t = 1;
   std::cout<<t<<std::endl;
   return 0;
}

???????代码输出结果:

对应的汇编代码

????????ubuntu 22.04 g++11

?? ? ? ?转换位汇编:

g++ -S c.c 

??

	.file	"c.c"
	.text
	.local	_ZStL8__ioinit
	.comm	_ZStL8__ioinit,1,1
	.globl	main
	.type	main, @function
main:
.LFB1731:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$1, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %esi
	leaq	_ZSt4cout(%rip), %rax
	movq	%rax, %rdi
	call	_ZNSolsEi@PLT
	movq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	_ZNSolsEPFRSoS_E@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE1731:
	.size	main, .-main
	.type	_Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB2231:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	%edi, -4(%rbp)
	movl	%esi, -8(%rbp)
	cmpl	$1, -4(%rbp)
	jne	.L5
	cmpl	$65535, -8(%rbp)
	jne	.L5
	leaq	_ZStL8__ioinit(%rip), %rax
	movq	%rax, %rdi
	call	_ZNSt8ios_base4InitC1Ev@PLT
	leaq	__dso_handle(%rip), %rax
	movq	%rax, %rdx
	leaq	_ZStL8__ioinit(%rip), %rax
	movq	%rax, %rsi
	movq	_ZNSt8ios_base4InitD1Ev@GOTPCREL(%rip), %rax
	movq	%rax, %rdi
	call	__cxa_atexit@PLT
.L5:
	nop
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2231:
	.size	_Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
	.type	_GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB2232:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	movl	$65535, %esi
	movl	$1, %edi
	call	_Z41__static_initialization_and_destruction_0ii
	popq	%rbp
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE2232:
	.size	_GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
	.section	.init_array,"aw"
	.align 8
	.quad	_GLOBAL__sub_I_main
	.hidden	__dso_handle
	.ident	"GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	1f - 0f
	.long	4f - 1f
	.long	5
0:
	.string	"GNU"
1:
	.align 8
	.long	0xc0000002
	.long	3f - 2f
2:
	.long	0x3
3:
	.align 8
4:

? ? ? ? 核心代码:

main:
.LFB1731:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$1, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %esi
	leaq	_ZSt4cout(%rip), %rax
	movq	%rax, %rdi
	call	_ZNSolsEi@PLT
	movq	_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_@GOTPCREL(%rip), %rdx
	movq	%rdx, %rsi
	movq	%rax, %rdi
	call	_ZNSolsEPFRSoS_E@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc

????????我们需要改这块:

?movl	$1, -4(%rbp)

????????并修改为:

movl  $2, -4(%rbp)

指令格式

  • movl: 这是一个汇编指令,代表“move long”。它用于将数据从一个位置移动到另一个位置。这里的“long”意味着它操作的是32位的值(在x86架构中)。

操作数

  • $1$2: 这是一个立即数操作数。$表示随后的数字是一个立即数,即直接提供的值,而不是一个地址或寄存器中的值。在第一个指令中,这个值是1,在修改后的指令中,这个值变成了2

  • -4(%rbp): 这是一个内存地址操作数,用于指定目标内存位置。它使用基址寄存器加偏移量的形式。

    • %rbp: 基址寄存器(Base Pointer),在函数调用中常用于定位栈帧的基础位置。在大多数情况下,它指向函数栈帧的开始。
    • -4: 这是一个偏移量,表示从%rbp指向的地址向下(向较小的地址)移动4个字节。在这个上下文中,它通常用于定位局部变量。

操作解释

  • 在原始指令中,movl $1, -4(%rbp)的作用是将值1存储到栈上由%rbp-4指定的位置。在C++代码中,这对应于int t = 1;这一行,即初始化整型变量t1
  • 修改后的指令,movl $2, -4(%rbp),其作用是将值2存储到相同的位置。这相当于将C++代码中的t变量初始化为2

结果

????????这个更改导致程序在执行时,局部变量t的初始值变为2,而不是原来的1。因此,当程序打印这个变量的值时,输出将是2而不是1

将汇编转为可执行文件

? ? ? ? 命令:

g++ c.s -o main

? ? ? ? ?执行:

./main

????????输出结果:?

总结

1. 编写和编译C++代码

????????首先,您编写了一个简单的C++程序,其中定义了一个整型变量t并初始化为1。然后,使用g++编译器将这段C++代码编译成汇编代码。这一步骤是通过g++ -S命令完成的,它生成了一个汇编语言文件(通常以.s为扩展名)。

2. 修改汇编代码

????????接下来,您打开了生成的汇编文件,并找到了对应于变量t初始化的那部分代码。在汇编代码中,t被初始化为1的指令是movl $1, -4(%rbp)。您将这条指令修改为movl $2, -4(%rbp),即改变了t的初始值从1为2。

3. 重新编译汇编代码

????????修改汇编代码后,您使用g++将修改后的汇编文件编译成一个可执行文件。这是通过类似g++ main.s -o main的命令完成的。

4. 运行和验证结果

????????最后,您运行了生成的可执行文件。由于汇编代码中t的值被改为了2,程序的输出相应地变为了2,而不是原始C++代码中的1。

结论

????????这个过程展示了从高级语言到低级汇编语言的转换,以及如何通过修改汇编代码来改变程序的行为。这是理解计算机体系结构和操作系统底层工作原理的一个实用例子。同时,它也揭示了编译器如何将高级代码转换为机器能够执行的指令。

文章来源:https://blog.csdn.net/weixin_44120785/article/details/135469531
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。