Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • bug定位的一些情形
  • c++性能调优,可能的情况
  • total-编程知识点集锦
  • hpc_common.hpp
  • memory order 内存模型
  • 类型推导之auto-template-decltype
  • 完美转发forward源码分析
  • 左值和右值,右值引用、重载 std-move,引用折叠
  • cmake用法
  • alignas、alignof、sizeof实现内存对齐分配
  • 通过宏定义控制debug打印
  • 程序耗时性能测试
  • 线程池开源项目阅读
  • C++类中包含没有默认构造函数的成员
  • C++可变参数模板
  • C++属性继承 public protected private
  • C++智能指针
  • C++导出so的方法,以及extern C 注意事项
  • 四种spin lock
  • condition_variable和unique_lock
  • dpdk、kernel bypass
  • 智能网卡solarflare、Mellanox、X10等
  • 汇编寄存器和常见指令
    • x86寄存器命名
    • 寄存器用途
    • 汇编语法和编译器
      • AT&T语法 和 Intel语法
      • GAS编译器 和 NASM编译器
      • 源操作数和目的操作数的前后
    • gdb用法
      • 设置参数
      • 回车重复执行上一次命令
      • layout显示代码,tui模式
      • 指定源文件搜索路径
      • 查看内存
      • 查看寄存器的值
      • 设置变量的值
      • 在so的某个偏移量处打断点
      • 监视值的变化
      • dprint 动态打印
      • 执行
      • 切换线程
      • 断点
      • 其他命令
      • 增加源代码的查找路径
    • hook
      • ida中逆向常用的宏定义
    • 典型函数调用过程中栈指针寄存器的变化
      • System V AMD64 函数调用约定:callee-saved寄存器
      • demo查看反汇编
      • 总结
  • c++ 类的静态成员变量未定义
  • C++获取类成员函数地址
  • preload示例
  • C++异常安全和RAII
  • C++11单例模式
  • C++歪门邪道
  • boost-program-option用法
  • c++17通过模板获取类成员的个数
  • 通过模板实现结构体成员拷贝-按成员名匹配
  • STL学习路径
  • boost库安装使用方法
  • C++文件读写
  • linux下socket通信demo,server和client
  • makefile写法
  • RxCpp
  • C++
gaoliu
2022-03-18
目录

汇编寄存器和常见指令

https://www.freesion.com/article/71371199944/ (opens new window)

# x86寄存器命名

64位有16个寄存器,r开头 32位只有8个,e开头

AX、BX、CX、DX、SI、DI、SP、BP 等是16位寄存器 有时候也用SP表示SP、ESP、RSP的统称。

sp 栈指针寄存器 esp和ebp是32位 (extend) rsp和rbp是64位 (register)

rsp是完整的64位; esp是rsp的低32位; sp 是rsp的低16位;

pc 程序计数器

# 寄存器用途

  • %rax 作为函数返回值使用

  • %rsp 栈指针寄存器,指向栈顶

  • %rdi %rsi %rdx %rcx %r8 %r9 用作函数参数,依次对应第1-6个参数. 最多有六个整形或指针型参数通过寄存器传递,超过六个只能通过栈来传递

一般寄存器:AX、BX、CX、DX AX:累积暂存器,BX:基底暂存器,CX:计数暂存器,DX:资料暂存器

索引暂存器:SI、DI SI:来源索引暂存器,DI:目的索引暂存器

堆叠、基底暂存器:SP、BP SP:堆叠指标暂存器,BP:基底指标暂存器

EAX、ECX、EDX、EBX:为ax,bx,cx,dx的延伸,各为32位元 ESI、EDI、ESP、EBP:为si,di,sp,bp的延伸,32位元

eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。

比方说:add eax,-2 ; //可以认为是给变量eax加上-2这样的一个值。

这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。

  • EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。
  • EBX 是"基地址"(base)寄存器, 在内存寻址时存放基地址。
  • ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
  • EDX 则总是被用来放整数除法产生的余数。

# 汇编语法和编译器

http://t.zoukankan.com/zpcdbky-p-14837963.html (opens new window)

# AT&T语法 和 Intel语法

这是两种不同的语法,AT&T主要用于UNIX,Intel语法主要用于Windows

# GAS编译器 和 NASM编译器

这是两种不同的编译器, GAS是GNU Assembler的简写,基于AT&T syntax指令,生成.s文件 NASM是Netwide Assembler的简写,基于Intel syntax指令,生成.asm文件

# 源操作数和目的操作数的前后

https://blog.csdn.net/wanglx_/article/details/7474070 (opens new window)

x86汇编一直存在两种不同的语法,intel语法和AT&T语法,在intel的官方文档中使用intel语法,Windows也使用intel语法,而UNIX平台的汇编器一直使用AT&T语法。

unix下使用gas编译器的AT&T语法主要有以下特点:

  • 寄存器名前缀%
  • 操作数是源在前,目的在后,如 mov %src %tar, 与Intel语法刚好相反。
  • 操作数长度在指令名后缀,b表示8位,w表示16位,l表示32位,如movl %ebx,%eax。
  • 立即操作数(常量)用$标示,如addl $5,%eax
  • 变量加不加$有区别。如movl $foo, %eax表示把foo变量地址放入寄存器%eax。movl foo,%eax表示把foo变量值放入寄存器%eax。

操作数前后的记忆方法小技巧:对于mov指令,假设src在前des在后是比较方便符合直觉的,寄存器加%是不方便的,那么这两种语法总是都有方便和不方便的地方。下面假设src和tar是寄存器,则:

mov %src %tar  # unix下使用GAS编译器的AT&T语法,src和tar前后方便,但是加%不方便 
`mov tar src   # windows下使用NASM编译器的INtel语法, src和tar前后不方便,但是不加%方便
1
2

# gdb用法


title: gdb用法-ida逆向-friad_hook date: 2021-10-06 01:44:49 permalink: /pages/1b0902/ categories:

  • Linux tags:

# 设置参数

进入gdb后: set args 参数1 参数2

或者启动gdb时 gdb --args a.out arg1 arg2

# 回车重复执行上一次命令

# layout显示代码,tui模式

参考: https://blog.csdn.net/zhangjs0322/article/details/10152279 (opens new window)

进入gdb后, 执行layout src layout: 用于分割窗口,可以一边查看代码,一边测试。主要有以下几种用法:

  • layout src: 显示源代码窗口
  • layout asm: 显示汇编窗口
  • layout regs: 显示源代码/汇编和寄存器窗口
  • layout split: 显示源代码和汇编窗口
  • layout next: 显示下一个layout
  • layout prev: 显示上一个layout

layout窗口控制和退出:

  • Ctrl + x,再按1: 单窗口模式,显示一个窗口
  • Ctrl + x,再按2: 双窗口模式,显示两个窗口
  • Ctrl + x,再按a: 回到传统模式,即退出layout,回到执行layout之前的调试窗口。
  • 如果Ctrl+x不生效,则先执行set keymap vi 或者 set editing-mode vi ,就可以生效了。
  • layout 模式显示多个窗口时,如果焦点不在cmd窗口,则上下按键不起作用, 输入fs next可切换窗口焦点至cmd

有时候layout asm显示的代码窗口太小看不全,可以在非layout模式下用disassemble直接显示反汇编代码,

# 指定源文件搜索路径

(gdb) dir <xxx>

gdb停到断点的时候,会显示当前断点的源文件路径

# 查看内存

(gdb) x /nfu addr

  • n,n个单元,数字
  • f,显示格式为f,x十六进制,d十进制,c字符,f浮点数
  • u,单元u的形式,bhwg, 单双四八字节
(gdb) x/20xw  0x7fffffffe230   显示20个单元,16进制显示,4字节每单元
(gdb) x/20xb  0x7fffffffe230   显示20个单元,16进制显示,1字节每单元
(gdb) x/s     0x7fffffffe230      显示指针处的字符串
1
2
3

# 查看寄存器的值

函数入参:

(gdb) x/s $rdi    查看rdi寄存器处字符串的值, (函数入口 rdi表示第一个参数,6个参数分别为 rdi rsi rdx rcx r8 r9)
(gdb) x/64xb $rdi+0x36    将rdi寄存器处的值作为地址,打印其+36处地址的内存
(gdb) p *(int32_t *)($rdi+0x60)    将rdi寄存器处的值作为地址,将其+60处地址转为int32_t类型打印出来

(gdb) p $pc  查看程序计数器的值(当前程序代码运行的地址)
(gdb) p $sp  查看栈指针就寄存器的值
1
2
3
4
5
6

# 设置变量的值

(gdb) set a=1

# 设置临时变量
(gdb) set $i="hello"
1
2
3
4

# 在so的某个偏移量处打断点

# 查看mapping信息,找到so的加载地址
(gdb) i proc mapping

# 创建临时变量so_base
(gdb) set $so_base= 0x7ffff71d8000

# 在so指定偏移量处打断点
(gdb) b *($so_base +0x7B05A)
1
2
3
4
5
6
7
8

# 监视值的变化

四、监视值变化

1、使用watch variable设置监视点,当value变化时,gdb会中断。

2、当离开variable的定义范围时,可能会使监视点不起作用。 可以使用地址监视:

print &variable找出地址address,然后watch *(int*)address。

# dprint 动态打印

# 在指定行打印
dprint sometest.cpp:13,"in dprint a=%d, i=%d\n",a,i

# 在指定函数打印
dprintf  perf_msgr_client.cc:ready,"In fun ready:c= %d,jobs=%d\n",c,jobs

1
2
3
4
5
6

这种动态打印是特殊的断点,可以通过i b或者info break查看

dprintf的本质是断点,因此目标程序并不是持续执行的,目标程序因为断点而暂停执行,等dprintf打印消息后再继续

dprintf输出到文件: https://sourceware.org/gdb/current/onlinedocs/gdb/Dynamic-Printf.html (opens new window)

# 执行

(gdb) run: 重新开始运行文件(run-text: 加载文本文件,run-bin: 加载二进制文件),简写r
(gdb) start: 单步执行,运行程序,停在第一执行语句
(gdb) next: 单步调试(逐过程,函数直接执行),简写n
(gdb) step: 单步调试(逐语句: 跳入自定义函数内部执行),简写s
(gdb) finish: 结束当前函数,返回到函数调用点,相当于跳出
(gdb) continue: 继续运行,简写c
1
2
3
4
5
6

正在运行时,按Ctrl+C暂停

# 切换线程

(gdb) info threads 查看线程信息 (gdb) thread n 切换线程

# 断点

设置断点 break 简写b 设置临时断点: tbreak 简写tb

(gdb) b *(0x7ffff71d806) 在函数地址处打断点 (gdb) b send 在send函数地址处打断点 (gdb) b _ZN7dce_api10common_imp12on_sync_dataEiPv 在_ZN7dcxxx函数地址处打断点 (gdb) b xx.cpp:26 在xx.cpp第26行设置断点

(gdb) info breakpoints: 查看当前设置的所有断点 (gdb) delete breakpoints num: 删除第num个断点,简写d

# 其他命令

(gdb) info proc mapping  查看内存映射
(gdb) info reg  查看寄存器的值,简写为 i r
(gdb) help: 查看命令帮助,具体命令查询在gdb中输入help + 命令,简写h
(gdb) list: 查看原代码(list-n,从第n行开始查看代码。list+ 函数名: 查看具体函数),简写l
(gdb) backtrace: 查看函数的调用的栈帧和层级关系,简写bt
(gdb) frame: 切换函数的栈帧,简写f
(gdb) info: 查看函数内部局部变量的数值,简写i


(gdb) print: 打印值及地址,简写p
(gdb) quit: 退出gdb,简写q
(gdb) display: 追踪查看具体变量值
(gdb) undisplay: 取消追踪观察变量
(gdb) watch: 被设置观察点的变量发生修改时,打印显示
(gdb) i watch: 显示观察点
(gdb) enable breakpoints: 启用断点
(gdb) disable breakpoints: 禁用断点
(gdb) run argv[1] argv[2]: 调试时命令行传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 增加源代码的查找路径

# 如果debug信息中的文件路径是相对路径,则用dir增加对应的folder
(gdb) dir path_to_src_folder

# 如果debug信息中的文件路径是绝对路径,则重定向
(gdb) set substitute-path old_path new_path
1
2
3
4
5

# hook

  • 动态库hook: 一种简单的hook方式是preload
  • 静态库hook: 编译时指定_warp_
  • 任意地址hook: inline hook,可去GitHub上找相关的实现。其实就是修改call指令的目的地址,跳到自己定义的函数处执行。
  • 也有一些专业的hook库,如friad等。

# ida中逆向常用的宏定义

https://blog.csdn.net/huiguixian/article/details/52026710 (opens new window)

在ida安装目录,找到defs.h,包含即可。一般在plugins目录下

# 典型函数调用过程中栈指针寄存器的变化

# System V AMD64 函数调用约定:callee-saved寄存器

  • System V AMD64 是基于x86_64架构Linux系统上广泛使用的一种调用约定, 我们在Linux系统上用gcc编译的代码默认都是遵循这种调用约定。其他还有:
    • cdecl (C declaration):是32位平台常见的一种约定,GCC、Clang、Visual Studio的C编译器都默认使用这种调用约定。
    • Microsoft x64:微软提出的基于x86_64架构的Windows系统上的一种调用约定
    • stdcall:它是用于32位Windows上的一种调用约定。

https://blog.csdn.net/qq_29328443/article/details/107232025 (opens new window)

caller-saved register, 调用者保存寄存器,也叫易失寄存器。因为被调用函数可以随便用这些寄存器,用完了也不需要恢复,caller需要自己负责保存。

callee-saved register,被调用者保存寄存器,也叫非易失寄存器,包括%rbx, %rsp, %rbp, %r12-r14, %r15, x87 CW,其中%rsp和%rbp是重点;

# demo查看反汇编

将下面的程序编译,查看反汇编

test_call.c:

#include <iostream>
#include <unistd.h>
#include <stdio.h>

int foo()
{
    int a = 5;
    int b = 8;
    char s[16] = "hello\n";
    printf("a=%d\n", a);
    return a+b;
}

int main()
{
    char a[16] = "hello";
    int p = foo();
    return p;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

编译: g++ -o a.out -g -O0 test_call.c

查看反汇编:objdump -d a.out

# main 部分
00000000004006c7 <main>:
  4006c7:       55                      push   %rbp
  4006c8:       48 89 e5                mov    %rsp,%rbp
  4006cb:       48 83 ec 20             sub    $0x20,%rsp
  4006cf:       48 b8 68 65 6c 6c 6f    movabs $0x6f6c6c6568,%rax
  4006d6:       00 00 00
  4006d9:       48 89 45 e0             mov    %rax,-0x20(%rbp)
  4006dd:       48 c7 45 e8 00 00 00    movq   $0x0,-0x18(%rbp)
  4006e4:       00
  4006e5:       e8 93 ff ff ff          callq  40067d <_Z3foov>  # 调用foo
            # callq指令,q表示8字节。 e8就是call指令
            # call指令会push当前ip寄存器的下一条指令地址,然后跳转到目标函数地址。
            # 对于64位程序,push rip后,sp指针会自动-8
  4006ea:       89 45 fc                mov    %eax,-0x4(%rbp)
  4006ed:       8b 45 fc                mov    -0x4(%rbp),%eax
  4006f0:       c9                      leaveq
  4006f1:       c3                      retq



# foo部分
000000000040067d <_Z3foov>:
  40067d:       55                      push   %rbp    # 进入函数后,首先push rbp寄存器入栈,保存父函数的rbp到栈里
  40067e:       48 89 e5                mov    %rsp,%rbp  # 然后将父函数的rsp作为当前的rbp
  400681:       48 83 ec 20             sub    $0x20,%rsp  #栈指针下移,分配栈空间。
  400685:       c7 45 fc 05 00 00 00    movl   $0x5,-0x4(%rbp)
  40068c:       c7 45 f8 08 00 00 00    movl   $0x8,-0x8(%rbp)
  400693:       48 b8 68 65 6c 6c 6f    movabs $0xa6f6c6c6568,%rax
  40069a:       0a 00 00
  40069d:       48 89 45 e0             mov    %rax,-0x20(%rbp)
  4006a1:       48 c7 45 e8 00 00 00    movq   $0x0,-0x18(%rbp)
  4006a8:       00
  4006a9:       8b 45 fc                mov    -0x4(%rbp),%eax
  4006ac:       89 c6                   mov    %eax,%esi
  4006ae:       bf e0 07 40 00          mov    $0x4007e0,%edi
  4006b3:       b8 00 00 00 00          mov    $0x0,%eax
  4006b8:       e8 73 fe ff ff          callq  400530 <printf@plt>
  4006bd:       8b 45 f8                mov    -0x8(%rbp),%eax
  4006c0:       8b 55 fc                mov    -0x4(%rbp),%edx
  4006c3:       01 d0                   add    %edx,%eax
  4006c5:       c9                      leaveq  # leaveq相当于两条指令:  
                                                  # `movq %rbp, %rsp`(恢复父函数的rsp)
                                                  # `popq %rbp`(恢复父函数的rbp)
  4006c6:       c3                      retq    # retq相当于:popq %rip (将rip恢复为父函数中call指令的下一条指令地址)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

[ATT汇编]后缀字母:

  • b字节=1字节
  • w字=2字节
  • l双字=4字节
  • q, Quad Word, 四字=8字节

可尝试gdb运行并查看寄存器的变化

$ gdb a.out
(gdb) start       # 进入main函数
(gdb) layout split  # 显示源码
(gdb) layout reg  # 显示寄存器窗口
(gdb) si          # 单步执行
1
2
3
4
5

# 总结

1. push   %rbp    # 调用call进入函数后,首先push rbp寄存器入栈,保存父函数的rbp到栈里
2. mov    %rsp,%rbp  # 然后将父函数的rsp作为当前的rbp
3. sub    $0x20,%rsp  #栈指针下移,分配栈空间。不一定有

... # 执行函数内容

* leaveq  # leaveq相当于两条指令:  
            # `movq %rbp, %rsp`(恢复父函数的rsp)
            # `popq %rbp`(恢复父函数的rbp)
* retq    # retq相当于:popq %rip (将rip恢复为父函数中call指令的下一条指令地址)
1
2
3
4
5
6
7
8
9
10
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
智能网卡solarflare、Mellanox、X10等
c++ 类的静态成员变量未定义

← 智能网卡solarflare、Mellanox、X10等 c++ 类的静态成员变量未定义→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×