反编译(二进制文件或者so库)
bash
objdump --help
objdump -M intel -j .text -ld -C -S out > out.txt #显示源代码同时显示行号, 代码段反汇编
-M intel 英特尔语法
-M x86-64
-C:将C++符号名逆向解析
-S 反汇编的同时,将反汇编代码和源代码交替显示
-l 插入源文件名和行号
-d 将代码段反汇编
-j section:仅反汇编指定的section
一、基础内联汇编:
用
单个%
前缀修饰寄存器立即数都有 '$' 前缀。如 "movl $78, %1 \n"
以 'b'、'w' 和 'l' 为后缀指明内存访问长度是:movb movl
基址寄存器是放在小括号 () 内的。例:section: disp(base, index, scale)
二、扩展内联汇编:
用
两个%
前缀修饰寄存器, 单个 % 前缀修饰操作数
语法:
`asm` [`volatile`] (
汇编程序
: 输出
: 输入
: 寄存器列表
);
__asm__ 等同于 asm
__volatile__ 等同于 volatile
示例 参考:https://blog.csdn.net/lwx62/article/details/82796364
cpp
#include <iostream>
using namespace std;
int main11(int argc, char*argv[])
{
int a=10, b;
asm ("movl %1, %%eax;"
"movl %%eax, %0;"
:"=b"(b) /* output */
:"c"(a) /* input */
:"%eax" /* clobbered register */
);
int count = 1;
char *str = "hello world!\n";
asm("int $0x80"
: "=a"(count)
: "a"(5), "b"(1), "c"(str), "d"(13));
int var = 100;
asm ("incl %0" :"=a"(var):"0"(var)); //+1
asm ("decl %0" :"=a"(var):"0"(var)); //-1
//asm ("sidt %0\n" : :"m"(var));
cout << var << endl;
return 0;
}
int main22()
{
int foo = 10, bar = 15;
__asm__ __volatile__("addl %%ebx,%%eax"
:"=a"(foo)
:"a"(foo), "b"(bar)
);
printf("foo+bar=%d\n", foo);
return 0;
}
int main33()
{
int my_var = 10;
int my_int = 20;
int foo = 10, bar = 15;
__asm__ __volatile__(
" lock ;\n"
" subl %1,%0 ;\n"
: "=m" (my_var)
: "ir" (my_int), "m" (my_var)
:
);
cout << "my_var-my_int=:" << my_var << endl;
cout << "my_int:" << my_int << endl;
return 0;
}
static inline char * _strcpy(char * dest,const char *src)
{
int d0, d1, d2;
__asm__ __volatile__( "1:\tlodsb\n\t"
"stosb\n\t"
"testb %%al,%%al\n\t"
"jne 1b"
: "=&S"(d0), "=&D"(d1), "=&a"(d2)
: "0" (src), "1" (dest)
: "memory");
//lodsb:load string;
//stosb:store string;
//testb:就是test测试检查src/dst是否一致
//约束"&S","&D","&a"表示寄存器esi,edi和eax,
//是early clobber寄存器,即它们的内容将在函数完成之前改变
//jne:判断上一步结果是否等于0,如果不等于0,则ZF=0,则进行跳转
//1b: 表示 backward 向前跳转,1表示局部标签1
//1f: 表示 forward 向后跳转
return dest;
}
int main()
{
int a = 1;
int b = 2;
asm("movl %%eax,%1" ::"a"(a), "m"(b));
cout << b << endl;
int var = 100;
asm ("incl %0" :"=a"(var):"0"(var)); //自+1
asm ("decl %0" :"=a"(var):"0"(var)); //自-1
//
const char* s1="hello ASM";
char buf[1024]={0};
_strcpy(buf,s1);
cout << " s1:" << s1 << endl;
cout << "buf:" << buf << endl;
}
/*
%0、%1 ... %9 它们依次代表 10 个操作数
a:表示寄存器eax
b:表示寄存器ebx
c:表示寄存器ecx
d:表示寄存器edx
D:表示寄存器edi
S:表示寄存器esi
q:表示以下任意四个寄存器之一:eax/ebx/ecx/edx
r:表示任意六个通用寄存器之一:eax/ebx/ecx/edx/edi/esi
g:表示可以存放到任意地点
A:把eax和edx组合成64位数
f:表示浮点寄存器
t:表示第一个浮点寄存器
u:表示第二个浮点寄存器
m:操作数内存
o:偏移量访问
*/
32位前缀 E
64位前缀 R
两家汇编主要区别
Intel Code |
AT&T Code |
---|---|
mov eax,1 | movl $ 1,% eax |
mov ebx,0ffh | movl $0xff,%ebx |
int 80h | int $0x80 |
mov ebx, eax | movl %eax, %ebx |
mov eax,[ecx] | movl (%ecx),%eax |
mov eax,[ebx+3] | movl 3(%ebx),%eax |
mov eax,[ebx+20h] | movl 0x20(%ebx),%eax |
add eax,[ebx+ecx*2h] | addl (%ebx,%ecx,0x2),%eax |
lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax |
sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax |
CPU 一般规则:
bash
eax: 执行加法,函数返回值
ebx: 数据存取
ecx: 计数器
edx: 读写I/O端口时,edx用来存放端口号
esp: 栈顶指针
ebp: 栈底指针,ebp+偏移量 来定位 栈中变量
esi: 字符串操作时,用于存放数据源的地址
edi: 字符串操作时,用于存放目的地址的
寄存器常识
bash
通用寄存器:r8-r15
标志寄存器
CF 进位标志
PF 奇偶标志
ZF 零标志
SF 符号标志
OF 补码溢出标志
TF 跟踪标志
IF 中断标志
...
指令寄存器
段寄存器
控制寄存器
int3软中断指令,向量号为3
32位:cr0-cr4
cr0: CPU控制标记和工作状态
cr1: 保留未使用
cr2: 页错误出现时保存导致出错的地址
cr3: 当前进程的虚拟地址空间的重要信息:页目录地址
cr4: 也存储了CPU工作相关以及当前人任务的一些信息
64位:cr8
调试寄存器
8个:DR0~DR7
描述符寄存器
全局描述符表GDT(Global Descriptor Table):
一级描述符表,一个处理器对应一个GDT,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口
LGDT和SGDT分别用于加载和保存GDTR寄存器的内容
局部描述符表LDT(Local Descriptor Table):
二级描述符表,有若干张,每个任务一张;LDTR可以在程序中随时改变,通过使用lldt指令
由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表,
则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问
中断描述符表寄存器IDTR:LIDT和SIDT分别用于加载和保存IDTR寄存器的内容
任务寄存器TR:
用于寻址一个特殊的任务状态段(Task State Segment,TSS)指令LTR和STR分别用于加载和保存TR寄存器的段选择符部分
MSR寄存器