avatar

二进制常用工具与技术的执行过程与原理

今天面试被问傻了,感觉自己对不少技术的了解都还停留在表面,于是打算来更深入的理解一下工具和技术的具体执行过程和原理

调试工具

二进制漏洞的学习和分析过程中最常用就是各种调试工具

静态调试

ida通常被用于静态的反编译工具来查看程序

反编译扫描有两种方式,线性扫描和递归扫描

线性扫描

所谓线性扫描就是从起始逐条反汇编指令,直到完成整个代码段,如gdb、windbg、objdump采用的就是线性扫描

递归扫描

递归扫描就是根据一条指令是否被另一条指令引用来决定是否对其进行反汇编

  • 顺序流指令:直接解析它后面的下一条指令,如MOV、PUSH、POP
  • 条件分支指令:解析它的所有条件路径,如JNZ
  • 无条件分支指令:反汇编器会尝试定位到跳转的目标,但有可能失败(如JMP EAX,EAX在静态环境下无法确认)
  • 函数调用指令:和无条件分支指令相似,如CALL EAX

ida使用的就是递归扫描,这也是花指令可以作为对抗ida的手段的原理

动态调试

最典型的应该就是使用ptrace来调试进程

断点

断点的执行过程是这样,当我们想在程序的某个地方断下来的时候,就把那个地方的值置为0xCC,0xCC对应的汇编指令是int 3,是专门用来调试的中断指令,然后再去执行子进程,当遇到断点时获取信号并恢复断点位置原有的指令。

实现可参考:http://researchcomplete.blogspot.com/2016/08/on-subject-of-debuggers-and-tracers_5.html

内存断点

gdb的watch命令可以实现对某个变量/内存地址的监控,如果监控的值被读/写,就会停住程序

实现watch point一般需要CPU支持硬件断点,软件实现非常消耗CPU

如果支持硬件断点,那么可以将监控的操作交给硬件来完成,而gdb这边只要做个简单的逻辑处理就行

如果是软件断点,在gdb 和 watchpoint中说的是设置watchpoint所在的那个页表为不可读/访问,然后在缺页处理那检测当前的页和地址是否是软设置watchpoint所在的页和watchpoint的地址,如果是,则可以假设该watchpoint发生了

获取shell

execve

在编写shellcode的时候,最常见的获取shell方式就是execve(/bin/sh),execve系统调用的执行过程如下:

  1. 陷入内核
  2. 加载新的可执行文件并进行可执行性检查
  3. 将新的可执行文件映射到当前运行进程的进程空间中,并覆盖原来的进程数据
  4. 将EIP的值设置为新的可执行程序的入口地址。如果可执行程序是静态链接的程序,或不需要其他的动态链接库,则新的入口地址就是新的可执行文件的main函数地址;如果可执行程序还需要其他的动态链接库,则入口地址是加载器ld的入口地址
  5. 返回用户态,程序从新的EIP出开始继续往下执行。至此,老进程的上下文已经被新的进程完全替代了,但是进程的PID还是原来的。从这个角度来看,新的运行进程中已经找不到原来的对execve调用的代码了,所以execve函数的一个特别之处是他从来不会成功返回,而总是实现了一次完全的变身。

one gadget

简单来说就是找到所有的/bin/sh及其引用,然后通过符号执行寻找约束

环境搭建

qemu

qemu最主要的功能有两个:

  • 作为用户态模拟器:通过动态代码翻译机制来执行不同架构的代码,例如在X86平台上模拟ARM平台下执行环境。
  • 作为虚拟机监视器:模拟全系统,利用其它VMM(Xen,KVM等等)来使用硬件提供的虚拟化支持,创建接近于主机性能的虚拟机。

所谓动态代码翻译机制,简而言之就是把目标代码的指令解释为一条条的微操作,然后再转化成本机可以执行的指令

KVM是Linux内核的一个虚拟化特性,由一组内核模块文件组成,它可以让QEMU中运行的Guest OS的指令直接在Host OS上的CPU中执行,前提是Guest OS和Host OS的硬件架构相同

KVM 本身不执行任何模拟,需要用户空间应用程序 QEMU 通过 /dev/kvm 接口设置一个客户机虚拟服务器的地址空间,向它提供模拟的 I/O,KVM 模块主要功能是初始化CPU硬件,打开虚拟化模式,然后将虚客户机运行在虚拟机模式下,并对虚拟客户机的运行提供一定的支持。在硬件虚拟化技术的支持下,内核的 KVM 模块与 QEMU 的设备模拟协同工作,构成一套和物理计算机系统完全一致的虚拟化计算机软硬件系统。

docker与虚拟机

虚拟机

虚拟机是一种模拟系统,即在软件层面上通过模拟硬件的输入和输出,让虚拟机的操作系统得以运行在没有物理硬件的环境中(也就是宿主机的操作系统上)。其中,这个能够模拟出硬件输入输出,让虚拟机的操作系统可以启动起来的程序,被叫做hypervisor。

一般来说,虚拟机都会有自己的kernel,自己的硬件,这样虚拟机启动的时候需要先做开机自检,启动kernel,启动用户进程等一系列行为,虽然现在电脑运行速度挺快,但是这一系列检查做下来,也要几十秒,也就是虚拟机需要几十秒来启动。

docker

宿主机和虚拟机的kernel是一致的(与虚拟机区别:不用做硬件输入输出的搬运工了,只需要做kernel输入输出的搬运工即可),这种虚拟机被命名为操作系统层虚拟化,也被叫做容器。
由于在虚拟机的系统中,虚拟机认为自己有独立的文件系统,进程系统,内存系统,等等一系列,所以为了让容器接近虚拟机,也需要有独立的文件系统,进程系统,内存系统,等等一系列,为了达成这一目的,宿主机系统采用的办法是:只要隔离容器不让它看到主机的文件系统,进程系统,内存系统,等等一系列,那么容器系统就是一个接近虚拟机的玩意了。

文章作者: 0bs3rver
文章链接: http://yoursite.com/2020/12/10/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%B8%B8%E7%94%A8%E5%B7%A5%E5%85%B7%E4%B8%8E%E6%8A%80%E6%9C%AF%E7%9A%84%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B%E4%B8%8E%E5%8E%9F%E7%90%86/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 0bs3rver的小屋