/nasm_test

汇编复习

Primary LanguageAssembly

; nasm_test 汇编复习

sudo apt-get install gcc nasm vim gcc-multilib -y

; 代码 int main() { return 0; }

nasm -f elf first.asm -o first.o gcc -m32 first.o -o first

ls first first.asm first.o

./first ; echo $?

; 由于错误的结果,得到错误的结论很容易

0BC71820 00001011 11000111 00011000 00100000

; 讨论的寄存器,它的宽度都是32位,32个2进制

( 847623785 * 12874873 + 274632 ) / 999 =

; 寄存器不够用?需要用内存了 ; 内存地址编号

mov eax, 1 mov [0x5566],eax 寄存器内容保存到内存地址中, 想一下赋值,另int需要4个字节,4个内存地址

mov eax, [0x0699] 取出 ; 以上语句,保护模式无效,用tmp替代

section .data 放文件数据区,分配内存
sui_bian_xie dw 0 开辟4字节空间,用0填充 double word

; add ebx, eax # 默认返回eax, 这样写的结果就是10,汇编真的很容易错

sudo apt-get install gdb -y

nasm -f elf test.asm -o test.o ; gcc -m32 test.o -o test ./test ; echo $?

gdb ./test set disassembly-flavor intel disas main

; 把反汇编的格式调整称为intel的格式

Dump of assembler code for function main: 0x080483f0 <+0>: mov eax,0x1 0x080483f5 <+5>: mov ebx,0x2 0x080483fa <+10>: add eax,ebx 0x080483fc <+12>: ret
0x080483fd <+13>: xchg ax,ax 0x080483ff <+15>: nop End of assembler dump.

; 前面内容为指令的地址

break *0x080483f5 run info register eax info register ebx stepi info register ebx stepi disas continue

; C语言与汇编语言的关系 ; 例程

int x, y, z;

int main() { x = 2; y = 3; z = x + y; return z; }

gcc -m32 test01.c -o test01 ./test01 ; echo $?

nasm -f elf test02.asm -o test02.o gcc -m32 test02.o -o test02 ./test02 ; echo $?

gcc -m32 test01.c -o test01 nasm -f elf test02.asm -o test02.o gcc -m32 -fno-lto test02.o -o test02 ls

gdb ./test01 set disassembly-flavor intel disas main

gdb ./test02 set disassembly-flavor intel disas main

; 对比

x = 2; y = 3; 的语句

mov eax, 2 mov [x], eax mov eax, 3 mov [y], eax

z = x + y; 的语句

mov eax, [x] mov ebx, [y] add eax, ebx mov [z], eax

return z; 的语句 mov eax, [z] ret

; 虽然很啰嗦,但是说明了执行的流程,要多熟悉

; 顺序执行 ; 寄存器保存执行位置的信息 eip ; 不能用 mov 修改

; jmp 相当于goto 跳转

; if 程序 ; c 代码 int main() { int a = 50; if( a > 10 ) { a = a - 10; } return a; }

cmp eax, 10              ; 对eax和10进行比较
jle less_func            ; 小于或等于的时候跳转
sub eax, 10

; 和10,a大于10的时候,进入if块中执行减法 ; 比较eax和10,eax小于等于10的时候,跳过中间的减法 ; 这个比较坑,思路上要注意下

; c代码

int main() { int x = 1; if ( x > 100 ) { x = x - 20; } x = x + 1; return x; }

; 虽然不太好接受,不过貌似不是函数的概念 ; 就是说语句必定运行,除非跳转掉

; c代码

int main() { int x = 10; if ( x > 100 ) { x = x - 20; } if( x <= 10 ) { x = x + 10; } x = x + 1; return 0; }

; 熟悉一下: 一条线执行下去,逻辑判断,就使用jmp的变体跳过 ; 注意,之前直接写的时候,用的0x50 是十进制 80

; 跳转指令后的<main+21>,就对应的是反汇编指令前是<+21>的指令

; 状态寄存器 eflags ; cmp指令实际上是在对两个操作数进行减法,减法后的一些状态最终就会反映到eflags寄存器中 ; 前一次运算的结果是正还是负、计算过程有没有发生进位、计算结果是不是零等信息 ; 跳转指令,就是根据eflags寄存器中的状态,来决定是否要进行跳转的

; 拆散循环结构 ; c 函数

int sum = 0; int i = 1; while( i <= 10 ) { sum = sum + i; i = i + 1; }

; 如果不满足 i <= 10,则跳过代码块 ; 先写成不使用循环的代码

int sum = 10; int i = 1;

_start: if( i <= 10 ) { sum = sum + i; i = i + 1; goto _start; }

; 变化一下

int sum = 10; int i = 1;

_start: if (i > 10){ goto _end_of_block; }

sum = sum + i; i = i + 1;

goto _start;

_end_of_block:

; 单条goto语句可以直接用jmp语句替代 ; if和goto组合的语句块可以用cmp和j*指令的组合替代

; 循环改写

; 函数调用 ; 默默记下现在这个题目算到哪一步 ; 记下现在计算出来的一些结果 ; 套用公式需要些什么数据,代公式的时候直接代入计算 ; 算出来的结果也需要记

; 现场保存,调用函数参数,结果记录,返回计算

; jmp,call 都能让 CPU 的eip 寄存器发生变化 ; jmp 跳转,不考虑返回 ; call 跳转,通过 ret返回

; 在call指令执行的时候,跳转前把eip保存起来,栈

; 栈顶指针,一个索引、下标 ; 放东西入栈,就将指针后移,从栈中取出东西来,就将指针前移?

; 栈顶top寄存器叫做esp, x86环境下,栈朝低地址方向伸长的 ; 入栈,那么栈顶指针递减

sub esp, 4 mov dword ptr[esp], eip ; esp先移动,然后再把eip的值写入到esp指向的内存中

; 实践 gdb ./call disas main

b *0x080483fd run

disas main

info register eip info registers esp p/x (unsigned int)$esp

$1 = 0xf7e3aad3 stepi

disas info register esp p/x (unsigned int)$esp

$2 = 0x8048402 disas main

; s2的值指向<+10>所在的那一行

; 递归调用 int fibo(int n){ if (n ==1 || n ==2){ return 1; } return fibo(n-1) + fibo(n -2); }

; 改写 int fibo(int n){ if (n == 1){ return 1; } if (n == 2) { return 1; } int x = n -1; int y = n -2; int a = fibo(x); int b = fibo(y); int c = a + b; return c; }

; 写成汇编试试 ; 约定eax寄存器作为函数的第一个参数,通过eax也用来传递返回值

; 失败原因 CPU中的寄存器是全局可见,没有局部变量 ; 无法实现递归或者嵌套的结构

; 每一层函数中都将当前比较关键的寄存器保存到堆栈中,然后才去调用下一层函数 ; 下层的函数返回的时候,再将寄存器从堆栈中恢复出来

; 入栈与出栈 push eax ; 将eax的值保存到堆栈中去 pop ebx ; 将堆栈顶的值取出并存放到ebx中

; 尝试

sum_one_to_n:

push ebx ;压

mov ebx, 0

_go_on: cmp eax, 0 je _get_out: add ebx, eax sub eax, 1 jmp _go_on

_get_out: mov eax, ebx pop ebx ;出 ret

; 压栈,出栈的时机,注意体会

; 寄存器有ebx、ecx、edx三个

; 比较后压栈,相加完弹出

来源 https://zhuanlan.zhihu.com/c_144694924