前言
如果只运用c语言进行编写pwn题目 对于一些寄存器或者栈结构一类的没有办法操控
这时候利用内联汇编就可以巧妙的解决问题的所在
同时由于我想学习免杀 其中有一种利用方法为内联汇编花指令 刚好可以利用这个来打基础
本文会伴随着本人对于内联汇编的不断学习而更新 不全面也不严谨 仅供参考
内联函数
在编写c语言时 如果你在一个函数中调用另外一个函数 其是通过call指令来进行跳转 跳转到对应函数的地址开始执行函数内容
最后通过存储在bp寄存器中的原函数地址来返回
比如下面这个程序
|
其汇编形式如下图所示
如果是一个循环调用函数 那么就会造成栈空间过于陈杂 这个时候就可以使用内联函数来解决问题
在对应函数定义时前缀加上inline
|
此时当main函数执行到调用test函数的时候 会直接在当前栈中执行test函数 而非跳转
但是你会发现正常编译会报错未定义的函数 我们需要手动链接
先将其编译成.o格式的文件 再进行动态链接
gcc -O -c -o test test.c |
内联汇编
基本内联汇编
基于上述你对内联函数的认识 那么显然易见 内联汇编就相当于我们直接往对应位置写入汇编代码 这就使得我们拥有了在程序编写的初期就拥有了操控寄存器值的能力 或者是修改栈结构 篡改程序执行流
对于pwn题来说 常用的编译是基于GCC的 而GCC采用的是AT&T/UNIX 汇编语法
不同的汇编语法对于内联汇编的编写格式要求不一样
正常的一个汇编语句 比如要使得rax寄存器赋值为1 需要这样编写
mov rax,1 |
而受汇编语法约束的内联汇编需要这样编写
mov $1,%rax |
也就是使得源操作数和目的操作数调换位置
其中1是属于一个立即操作数 我们需要在其前面添加一个$符号 否则1会被识别为一个地址
00001131 488b042501000000 mov rax, qword [__elf_header.ident.signature[1]] {0x10102464c45} |
可以看到其被识别为了ELF文件的文件头结构体中的第一个元素的第一个字节
00000000 struct Elf64_Header __elf_header = |
我们需要使得计算机明白1在这里是一个即时操作数 而非地址 所以需要加上$符号
至于rax寄存器前面的%符号 是寄存器的固定格式
同时和常规汇编一致 在操作符后面加上特定字符 可以决定操作数的字节大小
诸如’b’ ‘w’ ‘l’
如果我们想要调用寄存器中的值 可以用括号将寄存器套起来
mov (%rbx),%rax |
00001131 488b03 mov rax, qword [rbx] |
最后 对于每行的汇编语句结束以后 都需要加上’\t\n‘ 以此来区分每行汇编 并且每行汇编都需要加上双引号 如下:
|
扩展内联汇编
上述的基本内联汇编 往往只局限于内联汇编中的数据操作 而在扩展形式中
我们还可以指定操作数 并且可以选择输入输出寄存器 以及指明要修改的寄存器列表
asm ( assembler template |
上面是扩展内联汇编的基本格式
第一个冒号后面指定的是输出操作数
第二个冒号指定的是输入操作数
第三个冒号解释起来复杂 由于内联汇编是直接插入在我们原本的函数汇编代码中 再加上我们对于寄存器的值进行了操作
这会对正常函数的执行造成影响 所以我们需要在这里列出损坏的寄存器列表 让系统执行完内联汇编后还原一下寄存器的值 使用memory可以还原所有寄存器
asm ( |
下面我们基于上述的一段扩展内联汇编来帮助分析
开头的两行汇编属于汇编指令部分 语法问题和基本内联汇编一致 唯一要注意的是第一行的%1是什么
其代表的是我们引入的输入操作数a 其在整个扩展内联汇编中是第二个变量 也就是说我们想要引用输出变量b 就是%0 所以是从0开始递增的
接着来看”=m” 其中’=’是约束修饰符 用来指定其为输出操作数并且是可写的
‘m’也同为约束符 通常是用来指定这个操作数的存放
“a” 将输入变量放入eax |
对于一个char数组 一般是用’m’来约束 其他的我测试是会报错 同时 只能操作一个字长的数据 超过了就不行了
运用在pwn中
泄露libc基址
这个一般是用来自己方便调试的 这样可以帮助我们快速获取libc基址
比如我堆系列的博客用到的示例程序就使用了相同的代码来方便我快速获取libc基址 从而可以在exp中更自由的调试
char a[0x20]; |
原理就是利用向局部变量a中输入数据 其会被存放在栈上 同时rsp指针指向了输入的数据 这个时候调用pop指令出栈 就成功的把数据传到了寄存器rsi 这个时候调用write函数 就可以直接打印出函数真实地址了
调整寄存器值
这个办法比较简单 第一次被我利用是在出canary那一题的时候 用来调整函数结束时寄存器的值 引导解题者手写shellcode 或者进行合理的rop链构造
asm( |
比较简单就不解释了