• 初始花指令

    前言

    在一般的逆向工程中,一般使用IDA进行逆向,比如,逆向一个exe文件,IDA会将exe的二进制字节码转换为汇编指令,如果想要汇编指令更易于理解,可以F5将汇编指令转换为c语言伪代码,但是有些程序开发者为了保护代码,不被逆向,所以采用花指令骗过IDA,使其不能正常还原为伪代码

    花指令是对抗反汇编的有效手段之一,正常代码添加了花指令之后,可以破坏静态反汇编的过程,使反汇编的结果出现错误。错误的反汇编结果会造成破解者的分析工作大量增加,进而使之不能理解程序的结构和算法,也就很难破解程序,从而达到病毒或软件保护的目的。

    分类

    可执行花指令

    可执行式花指令指的是能够正常运行的但又不改变原始程序逻辑性的一组无用指令。这类花指令有如下特点:

    • ①可以正常运行;

    • ②不改变任何寄存器的值;

    • ③反汇编器可以正确反汇编该指令。

    例如这样几组花指令就属于该类别:PUSH EAXPOP EAXNOP,INC EAX&DEC EAX等等。这种类别的花指令组合形式很多,常常用在病毒代码的变形引擎中,病毒在传播时通过变形引擎随机产生一组该类别花指令并插入到病毒正常代码中,可以改变病毒的特征码,从而起到变形的作用。

    不可执行花指令(垃圾指令)

    不可执行花指令,是指被插入到原始代码中但又不改变原始程序逻辑性的一组无用字节。这类花指令有如下特点:

    • ①不可以正常运行;

    • ②不改变任何寄存器的值;

    • ③反汇编器可能会错误反汇编这些字节。

      根据反汇编的工作原理,只有当花指令同正常指令的开始几个字节被反汇编器识别成一条指令时,才能有效破坏反汇编的结果。因此,插入的花指令应当是一些不完整的指令,被插入的不完整指令可以是随机选择的。正因为不可执行花指令有这些特点,该类花指令才能应用到软件保护中。

      Cullen等人指出为了能够有效“迷惑”静态反汇编工具,同时保证代码的正确运行,花指令必须满足两个基本特征,即:

      • 1)垃圾数据必须是某个合法指令的一部分:
      • 2)程序运行时,花指令必须位于实际不可执行的代码路径

    不可执行花指令的成功来自反汇编算法的缺陷

    当前静态分析中采用的反汇编算法主要可以分为2类:线性扫描算法与行进递归算法。

    线性扫描反汇编算法

    线性扫描算法p1从程序的入口点开始反汇编,然后对整个代码段进行扫描,反汇编扫描过程中所遇到的每条指令。线性扫描算法的缺点在于在冯诺依曼体系结构下,无法区分数据与代码,从而导致将代码段中嵌入的数据误解释为指令的操作码,以致最后得到错误的反汇编结果。

    行进递归反拒绾算法

    相比线性扫描算法,行进递归算法通过程序的控制流来确定反汇编的下一条指令,遇到非控制转移指令时顺序进行反汇编,而遇到控制转移指令时则从转移地址处开始进行反汇编。行进递归算法的缺点在于准确确定间接转移目的地址的难度较大。

    典型花指令

    一般花指令有很多种,下面给出一个典型

    花指令程序

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    #include<stdio.h>
    #include<windows.h>

    void example1();
    void example2();
    void example3();
    void example4();
    void example5();
    void example6();
    void example7();

    int example7_0(int a);

    void __declspec(naked)__cdecl cnuF(int* a)//裸函数,开辟和释放堆栈由我们自己写。
    {//55 8b ec 83
    __asm
    {
    push ebp
    mov ebp, esp
    sub esp, 0x40
    push ebx
    push esi
    push edi
    mov eax, 0xCCCCCCCC
    mov ecx, 0x10
    lea edi, dword ptr ds : [ebp - 0x40]
    rep stos dword ptr es : [edi]
    }
    *a = 1;
    _asm
    {
    call LABEL9;
    _emit 0xE8;
    _emit 0x01;
    _emit 0x00;
    _emit 0x00;
    _emit 0x00;

    LABEL9:
    push eax;
    push ebx;
    lea eax, dword ptr ds : [ebp - 0x0]
    add dword ptr ss : [eax-0x50] , 26;

    pop eax;
    pop ebx;
    pop eax;
    jmp eax;
    __emit 0xE8;
    _emit 0x03;
    _emit 0x00;
    _emit 0x00;
    _emit 0x00;
    mov eax,dword ptr ss:[esp-8];

    }

    __asm
    {
    pop edi
    pop esi
    pop ebx
    mov esp, ebp
    pop ebp
    ret
    }
    }

    int a=0;

    int main() {

    ::MessageBox(NULL,"花指令","main",0);

    example1();
    printf("example1 : a = %d\n",a);
    example2();
    printf("example2 : a = %d\n",a);
    example3();
    printf("example3 : a = %d\n",a);
    example4();
    printf("example4 : a = %d\n",a);
    example5();
    printf("example5 : a = %d\n",a);
    cnuF(&a);
    printf("cnuF : a = %d\n",a);
    example6();
    printf("example6 : a = %d\n",a);
    example7();
    printf("example7 : a = %d\n",a);


    return 0;
    }


    void example1()
    {
    __asm {
    xor eax,eax
    test eax,eax
    je LABEL1
    jne LABEL2
    LABEL2:
    _emit 0x5e
    and eax,ebx
    _emit 0x50
    xor eax,ebx
    _emit 0x74
    add eax,edx

    LABEL1:
    }
    a=1;
    }

    void example2()
    {
    __asm {
    push eax;
    xor eax, eax;
    test eax, eax;
    jnz LABEL1;
    jz LABEL2;
    LABEL1:
    _emit 0xE8;
    LABEL2:
    mov byte ptr[a], 0;
    call LABEL3;
    _emit 0xFF;
    LABEL3:
    add dword ptr ss : [esp] , 8;
    ret;
    __emit 0x11;
    mov byte ptr[a], 2;
    pop eax;
    }
    }
    void example3()
    {
    __asm {
    push ebx;
    xor ebx, ebx;
    test ebx, ebx;
    jnz LABEL5;
    jz LABEL6;
    LABEL5:
    _emit 0x21;
    LABEL6:
    pop ebx;
    }
    a=3;
    }

    void example4()
    {
    __asm {
    push ebx;
    xor ebx, ebx;
    test ebx, ebx;
    jnz LABEL7;
    jz LABEL8;
    LABEL7:
    _emit 0xC7;
    LABEL8:
    pop ebx;
    }
    a=4;
    }

    void example5()
    {

    __asm {
    call LABEL9;
    _emit 0x83;
    LABEL9:
    add dword ptr ss : [esp] , 8;
    ret;
    __emit 0xF3;
    }
    a=5;
    }

    void example6()
    {

    ::LoadLibrary("./hhhh");//函数返回值存储于eax中
    _asm{
    cmp eax,0;
    jc LABEL6_1;
    jnc LABEL6_2;
    LABEL6_1:
    _emit 0xE8;
    LABEL6_2:
    }
    a = 6;
    }


    void example7()
    {
    example7_0( 0 );
    _asm{
    cmp eax,0;
    jc LABEL7_1;
    jz LABEL7_2;
    LABEL7_1:
    _emit 0xE8;
    LABEL7_2:
    }
    a = 7;
    }

    int example7_0(int a)
    {
    if(a>0)
    return 1;
    else
    return 0;
    }

    以上给了7个常见的花指令的源码,虽然花指令的原理差不多,但是能够让机器不能正常识别就行,好像有这样一句话,就是不影响程序运行,添加无效垃圾指令

    去花

    在IDA打开这个程序,可以正常的生成没有添加花指令的main函数,但是在各大函数里面就别有洞天了

    image-20220325160410277

    example1

    跟进sub_40100F()函数,直接出现了JUMPOUT(0x401230)的提示,显然就是因为花指令,IDA不能正常给出伪代码

    • TAP键,切换为汇编代码image-20220325161342607

    • 点击00401258,选中地址,UD键将其转换为数据image-20220325162109853

    • C2改成90,90就是nop的字节码,可以Edit–patch program–change byteimage-20220325163026149

    • 选中00401258C键转换为指令,选中040125A,C键,将其还原为指令image-20220325163758514

    发现其依旧不能转换为伪代码

    • 原因在loc_401250里面也有问题,简单点,就是将其全部nop,改为90image-20220325164414637

    • 下面再00401230P键,最后F5将其转换为伪代码image-20220325164947122

    • 最后F5的结果如下image-20220325165616433

    example2

    跟进sub_401014()函数,也可以看到一个JUMPOUT(0x401290);的红色提示,那就需要分析汇编指令了

    • Tab键,查看汇编image-20220325170257391

    • 004012B1处,U键,转换为数据image-20220325170837985

    • 004012B2C键,将004012B2处的内容识别为代码,而前一行004012B1就是垃圾数据,所以将004012B1和跳转这里的jnz short near ptr unk_4012B1的内容给nop掉,可仍然有问题image-20220325172152663

    • 004012B9004012C4的内容nop掉,发现还是有问题,结果是,在上一个retn指令后面有一个垃圾数据,0x11,所以将其nop掉,即可得结果image-20220325173616971

    • 然后在00401290处,先U键后P键,最后F5生成伪代码image-20220325173844628

    example3

    跟进sub_40102D()函数,同样给了个JUMPOUT(0x401300);就没显示其它了,跟进汇编

    • Tab键,查看汇编image-20220325174512134

    • 00401321处,D键,转换为数据image-20220325174900121

    • 然后将0040131D00401321处的内容nop掉,并将00401322的内容C键识别为代码image-20220325175440429

    • 最后在00401300处,依次按U,P,F5键,最后得到伪代码image-20220325175659505

    example4+

    example4,5,6,7的花指令都大同小异,可以参照前面的方法自行去除

    程序链接

    百度云链接:https://pan.baidu.com/s/1xs0lSZJWWcemTNdb1dcFjg 提取码:kata

    阿里云链接:https://www.aliyundrive.com/s/kawsoPvtHrJ

    参考链接

    花指令入门:https://blog.csdn.net/zixiaomuwu/article/details/52895488

    逆向之花:https://the_itach1.gitee.io/2020/12/18/%E8%8A%B1%E6%8C%87%E4%BB%A4/

    反调试入门-花指令:http://www.360doc.com/content/20/1208/20/71939662_950241977.shtml

    上一篇:
    RC4的加密与解密实现
    下一篇:
    微信小程序的理解
    本文目录
    本文目录