一, 编译使用命令
1, 汇编命令
gcc -Og -S main.c
2, intel 汇编指令
gcc -Og -S -masm=intel main.c
3, 反编译 命令
objdump -d ${main}
二, 汇编指令
1, mov 指令的使用
C语言
void decode(long *xp, long *yp, long *zp)
{
long x = *xp;
long y = *yp;
long z = *zp;
*yp = x;
*zp = y;
*xp = z;
}
编译汇编
_Z6decodePlS_S_:
.LFB989:
.cfi_startproc
movq (%rdi), %r8
movq (%rsi), %rcx
movq (%rdx), %rax
movq %r8, (%rsi)
movq %rcx, (%rdx)
movq %rax, (%rdi)
ret
.cfi_endproc
.LFE989
2, 加载有效地址
指令leaq 实际上是movq的指令变形
long scale(long x, long y, long z)
{
long t = x + 4 * y + 12 * z;
//long t = 5 * x + 2 * y + 8 * z; // example
return t;
}
// x in %rdi, y in %rsi, z in %rdx
_Z5scalelll:
.LFB990:
.cfi_startproc
leaq (%rdi,%rsi,4), %rcx // x + 4 * y
leaq (%rdx,%rdx,2), %rax // z + 2 * z = 3 * z
leaq (%rax,%rdx,4), %rax // (x + 4 * y) + 4 * ( 3 * z) = x + 4 * y + 12 * z
//salq $2, %rax
//addq %rcx, %rax
ret
.cfi_endproc
// ----------------------example-------
//_Z5scalelll:
//.LFB990:
// .cfi_startproc
// leaq (%rdi,%rdi,4), %rax
// leaq (%rax,%rsi,2), %rax
// leaq (%rax,%rdx,8), %rax
// ret
// .cfi_endproc
3, 位操作
long arith(long x, long y, long z)
{
long t1 = x ^ y;
long t2 = z * 48;
long t3 = t1 & 0x0F0F0F0F;
long t4 = t2 - t3;
return t4;
}
// long arith (long x, long y, long z)
// x in %rdi , y in %rsi , z in %rdx
_Z5arithlll:
.LFB991:
.cfi_startproc
xorq %rsi, %rdi // t1 = x ^ y;
leaq (%rdx,%rdx,2), %rax // 3 * z;
salq $4, %rax // t2 = 16 * (3 * z) = 48 * z
andl $252645135, %edi // t3 = t1 & 0X0F0F0F0F;
subq %rdi, %rax // return t2 - t3
ret
.cfi_endproc
4, GDB 调试
常见的命令
命令 | 效果 |
---|---|
开始和停止 | |
quit | 退出GDB |
run | 运行程序(在此给出命令行参数) |
kill | 停止程序 |
断点 | |
break multstore | 在函数mulstore入口处设置断点 |
break * 0x400540 | 在地址0x400540处设置断点 |
delete 1 | 删除 1 |
delete | 删除所有断点 |
执行 | |
stepi | 执行1条指令 |
stepi 4 | 执行4条指令 |
nexti | 类似于stepi, 但以函数调用为单位 |
continue | 继续执行 |
finish | 运行到当前函数返回 |
检查代码 | |
disas | 反汇编当前函数 |
disas multstore | 反汇编函数multstore |
disas 0x400540 | 反汇编位于地址0x400540附近的函数 |
disas 0x400540, 0x40043d | 反汇编指定地址范围内的代码 |
print /x $rip | 以十六进制输出程序计数器的值 |
检查数据 | |
print $rax | 以十进制输出%rax的内容 |
print /x $rax | 以十六进制输出%rax的内容 |
print /t $rax | 以二进制输出%rax的内容 |
print 0x100 | 输出0x100的十进制表示 |
print /x 555 | 输555 的十六进制表示 |
print *(long *) 0x7fffffffe818 | 输出位于地址0x7fffffffe818的长整数 |
print *(long *)($rsp + 8) | 输出位于地址$rsp + 8处的长整数 |
x/2q 0x7fffffffe818 | 检查从地址0x7fffffffe818开始的双(8字节)字 |
x/20bmultstore | 检查函数multstore的前20个字节 |
有用的信息 | |
info frame | 有关当前栈帧的信息 |
info registers | 所有寄存器的值 |
help | 获取有关GDB的信息 |
5, 内存越界引用和缓冲区溢出
C/C++对于数组的引用不进行任何边界检查, 而且局部变量和状态信息(例如保存的寄存器值和返回地址)都存放在栈中。两种情况集合到一起就能导致严重的程序错误, 对越界的数组元素的写操作会破坏存储在栈中的状态信息。当程序使用这个被破坏的状态
实例
char *getsl(char *s)
{
return s;
}
void echo()
{
char buf[8];
getsl(buf);
puts(buf);
}
_Z5getslPc:
.LFB993:
.cfi_startproc
movq %rdi, %rax
ret
.cfi_endproc
.LFE993:
.size _Z5getslPc, .-_Z5getslPc
.globl _Z4echov
.type _Z4echov, @function
_Z4echov:
.LFB994:
.cfi_startproc
subq $24, %rsp //alloc 24 stack
.cfi_def_cfa_offset 32
movq %rsp, %rdi
call puts
addq $24, %rsp //delete 24 stack
.cfi_def_cfa_offset 8
ret
.cfi_endproc
解释:
解释gcc 优化编译生成保存的栈区
使用 -fno-stack-protector -Og 参数
g++ -S -fno-stack-protector main.cpp
_Z5getslPc:
.LFB977:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movq %rdi, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE977:
.size _Z5getslPc, .-_Z5getslPc
.globl _Z4echov
.type _Z4echov, @function
_Z4echov:
.LFB978:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
leaq -16(%rbp), %rax
movq %rax, %rdi
call _Z5getslPc
leaq -16(%rbp), %rax
movq %rax, %rdi
call puts
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
网友评论