花指令学习

越学越觉得自己菜。。。

Posted by gxkyrftx on January 1, 2019

1.花指令学习

由于数据与代码在编译汇编之后,最终都要变为二进制文件在内存中存储,通俗点说就是一大串0,1序列。在反汇编的过程中,反汇编算法,必须要很好的对指令,数据进行分析,保证反汇编的正确性。 主流的两类反汇编算法:1.线性扫描法 2.递归行进扫描法 主流调试工具使用的方法:

工具名及算法:OllyDbg——线性扫描法/递归行进扫描法(ctrl+a)
            windbg——线性扫描法
            w32dasm——线性扫描法
            ida pro——递归行进扫描法

2.花指令原理

堆栈平衡,说的太高大上了。程序的运行,需要不断调用函数,完成功能,一个接一个,一个套一个,如何保证嵌套的函数执行完毕之后,能带着值,返回到上一层函数?用堆栈。

调用子程序时先保存堆栈信息(某些堆栈相关的寄存器),待子程序返回后将堆栈恢复到调用前的状态(堆栈用于保存局部变量、函数参数等重要信息),以保证程序能够继续正确运行,是为堆栈平衡。 子程序中,对堆栈进行操作,一定要保证指令成对出现,比如有“PUSH EAX”,就要有“POP EAX”,这样在最后执行RETN的时候,地址指向才不会出错,

我们的反汇编软件,反汇编器将花指令反汇编成有效的指令,错的也变成对的了,它在不断尝试理解这些0,1序列,试图提供合乎程序运行的解释,当然就出错了,因为这些指令是与程序主题逻辑无关的。

3.解决办法

所以,我们在看反汇编代码时,不仅要看对应序列能否被反汇编成功,还是要看其是否是有效的指令。编写更好的启发式的脚本用来过滤掉花指令是可行的,但是目前我们最好的武器依旧是是人类的大脑。

3.1. 通过指令级别

x86处理器有四个用于保护的“环”,其中两个环(Ring1和Ring2)通常根本不使用。 内核模式执行Ring0和Ring3用户模式则是常用的两种特权级别。某些指令只能在Ring0中运行。这些特殊的特权指令也碰巧是单字节操作码,并且经常发生在反汇编花指令中。

3.1.1 特权指令

如果知道正在查看的代码不是作为操作系统引导加载程序、内核或设备驱动程序运行的,那么当查看到这些特权指令时我们应该意识到这些反汇编不是真正有效的代码。 突出显示的指令都是IN和OUT指令的变体,主要作用是从硬件端口读取和写入数据。这些可能的指令必须在设备驱动程序中使用,如果在Ring3用户模式下执行,将会产生一个异常。即使试图反汇编内核代码,这些指令发生的频率也会比你反汇编操作系统的任意文件的要高得多。以下是将经常在反汇编的花指令中看到的一些常见Ring0指令的列表:

    IN (INS, INSB, INSW, INSD)
    OUT (OUTS, OUTSB, OUTSW, OUTSD)
    IRET
    IRETD
    ARPL
    ICEBP / INT 1
    CLI
    STI
    HLT

3.1.2 ring3的罕见指令

在用户模式代码中有许多Ring3指令是有效的,但是在编译代码中非常少见。 我们可以将这些指令分为三类:便利指令,不太可能的数学指令和远指针指令。我们来看看这些类别。

便利指令

    ENTER
    LEAVE
    LOOP (LOOPE/LOOPZ, LOOPNE/LOOPNZ)
    PUSHA
    POPA

汇编语言程序员可以使用ENTER和LEAVE指令来获得函数序言和结语,这两个指令可以手动的使用PUSH,MOV和SUB来完成。现代编译器倾向于避免使用ENTER和LEAVE,因此大多数程序员也不会使用它们。因为他们一起占据了操作码范围的近1%,所以它们在花指令代码中的出现是非常普遍的。

LOOP指令及其条件同类的LOOPZ和LOOPNZ提供了一种用汇编语言编写循环的非常直观且有用的方法。 编译器通常选择不使用这些,而是使用JMP和条件跳转指令来制作自己的循环。

PUSHA和POPA指令提供了将所有寄存器的状态保存到堆栈的机制。这为汇编语言程序员提供了一个可能方便的类宏指令。由于它也存储和恢复堆栈指针本身,这使得一个懒惰的编码器在函数启动时盲目地存储寄存器信息,并在函数结束时恢复它们的潜在用途变得复杂了。我们不会在编译代码中找到这些指令,但是它们也占据了可用操作码范围的近1%,所以它们经常出现在花指令中。

不太可能的数学指令

浮点指令
    F*
    WAIT/FWAIT 浮点指令通常以字母“F”开始。虽然有些程序使用浮点数学,但大部分程序都不使用浮点数学。浮点指令占用操作码范围的很大一部分,所以它们在花指令中的是普遍存在的。这就是你对你正在尝试进行逆向工程的代码设计的知识将派上用场的地方。如果您使用3D图形对程序进行反向工程,则需要查找潜在的大量浮点指令。对于我们在FLARE团队所做的工作,恶意软件分析,浮点数学是非常罕见的。值得注意的例外是shellcode通常使用一些浮点指令作为获取指向自身的指针。

    SAHF
    LAHF SAHF和LAHF指令将AH寄存器的内容复制到标志寄存器EFLAGS中。这是一种编程行为,不会从高级语言转换下来,因此编译器通常不会输出这些指令。如果不是手动编写汇编代码,那么这些指令将会很少出现。由于这些是单操作码范围内的单字节指令,所以它们经常在反汇编花指令中出现。
ASCII调整指令
    AAA
    AAS
    AAM
    AAD “AA”系列指令涉及以二进制编码的十进制形式处理数据。这是一个比较早的编码方式,基本很难再现代计算中遇到。但是,我们会经常在反汇编花指令中遇到这些指令,因为它们是单字节指令。

    SBB SBB指令与SUB相似,只是它将进位标志添加到源操作数。这可以在合法的代码中看到,特别是当试图对大于机器字长的数字进行算术运算时(32位代码中的64位数学)。不幸的是,SBB指令不少于九个操作码,占可能范围的3.5%。它们不是单字节指令,但是有很多形式和很多操作码,它们经常在反汇编的的花指令中出现。

    XLAT 在汇编代码中XLAT是一个有趣的指令。它没有直接的翻译到一个单一的高级语言结构,由于它是一个单字节指令,所以会发现它比使用汇编语言找到程序员更频繁。

    CLC
    STC
    CLD
    STD 这些指令清除并设置进位和目标标志。 它们可能在流操作附近的编译器生成的代码中找到(通常会在REP前缀的地方)。尽管如此,由于它们都是单字节指令,它们在花指令中出现的可能性非常高。

远指针指令

    LDS
    LSS
    LES
    LFS
    LGS 在英特尔架构中,远指针的使用并不存在于16位时代。但是,设置远指针的指令仍然占用两个单字节操作码和两个字节操作码范围中的三个值。因此,会看到这些指令经常出现在反汇编花指令中。
指令前缀

x86中的指令可以有前缀。前缀字节通常会修改后续指令的行为。前缀的常用用法是改变操作数的大小。例如,如果正在以32位模式执行指令,并且希望使用16位寄存器或操作数执行计算,则可以在计算指令中添加一个前缀,以通知CPU它是一个16位指令而不是一个32位的。有许多这样的前缀,不幸的是,其中许多都落在ASCII表的字母范围内。这意味着,当反汇编ASCII文本(如我们的lorem ipsum),指令前缀将是非常普遍的。这些指令前缀会出现在正常指令中,但没有在花指令中出现的频繁。 如果正在拆解32位代码,并且看到大量使用的16位寄存器(AX,BX,CX,DX,SP,BP等,而不是EAX,EBX,ECX,EDX,ESP和EBP ),可能就是花指令。

反汇编器还将代表在指令助记符之前添加了某些符号的其他前缀。如果在代码中经常看到任何这些关键字,那很可能是花指令代码:

    LOCK
    BOUND
    WAIT 段选择器

    FS
    GS
    SS
    ES 在16位模式下,使用段寄存器(CS,DS,FS,GS,SS,ES)完成寻址存储器。程序的代码通常是基于CS“代码段”寄存器来引用的,程序处理的数据是从DS“数据段”寄存器引用的 ES,FS和GS是额外的数据段寄存器,合法地用于32位代码,但这是一个更高级的话题。有段选择符前缀字节,可以在指令之前添加段,强制指定它参考内存基于特定的段,而不是它设计使用的默认段。 由于这些都占用了那个单字节操作码空间,所以它们会在反汇编的花指令中频繁出现。

本文访问量: