吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

搜索
查看: 1368|回复: 22
收起左侧

[原创] 手脱花指令及IDA脚本编写

  [复制链接]
flatcc 发表于 2021-9-13 16:21

综合许多篇帖子而成,大多参考以末尾参考文章形式给出,适用于初学者,感谢大家提出意见。可结合末尾源码参考学习。感觉有用的话感谢大佬们点个赞。

简介

作用

  • 欺骗反汇编器,让反汇编器无法正确反汇编出汇编代码,具体来说是破坏了反编译的分析,使得栈指针在反编译引擎中出现异常
  • 从而加大静态分析的难度,使得逆向分析人员难以识别代码的真正意图

原理

由于反编译器的工作原理一般是线性扫描算法或递归下降反汇编算法。

  • 线性扫描反汇编算法从程序的入口点开始反汇编,然后对整个代码进行扫描,反汇编扫描其过程中所遇到的每条指令。那么线性扫描算法的缺点也就显而易见了,由于其不定常的指令格式,在反汇编扫描过程中无法区分数据与代码,从而导致将代码段中嵌入的数据误解释为指令的操作码,以致最后得到错误的反汇编结果。
  • 递归下降算法,递归下降算法通过程序的控制流来确定反汇编的下一条指令,遇到非控制转移指令时顺序进行反汇编,而遇到控制转移指令时则从转移地址处开始进行反汇编。该算法的缺点在于难于准确确定间接转移的目的地址。

PATCH方法

先在IDA中开启字节码的显示,我这里设置显示的字节码是8。
16303942144038.jpg

以下图中的花指令为例,我们鼠标光标点到0x004560FF,然后按快捷键D,在下方为指令的地址0x00456100地址处,按快捷键C将其转换为指令。然后将0x004560FF处的0xE8Patch为0x90,也就是打补丁为nop指令。
16303943012675.jpg
然后保持副本即可。

实现案例

简单花指令-多层JMP嵌套

如下是单层的JMP形式:

jmp LABEL1
  db junk_code;
LABEL1:

甚至如下的多层嵌套:

//简单花指令-多层JMP嵌套
void example1()
{
    __asm {
        jmp LABEL1;
        _emit 68h;
    LABEL1:
        jmp LABEL2;
        _emit 0CDh;
        _emit 20h;
    LABEL2:
        jmp LABEL3;
        _emit 0E8h;
    LABEL3:
    }
    a = 99;
}

如下图,因为IDA使用的是递归下降算法进行反汇编,所以这种花指令可以被IDA轻松识别。
16303094353221.jpg

互补条件代替JMP跳转

类似如下形式,无论如何都会跳转到LABEL1处:

  jz LABEL1
  jnz LABEL1
  db junk_code
LABEL1:

在如下代码中,先对eax进行xor之后,再进行test比较,zf标志位肯定为1,就肯定执行jz LABEL2;,也就是说中间0xC7永远不会执行。要记得:先压栈保存eax的值,最后再把eax的值pop出来。

void example2_1()
{
    __asm {
        push eax;
        xor eax, eax;
        test eax, eax;
        jnz  LABEL1;
        jz LABEL2;
    LABEL1:
        _emit 0xC7;
    LABEL2:
        pop eax;
    }
    a = 21;
}

如下图,我们可以看到,IDA虽然识别栈帧错误,但是正确的程序流还是比较清晰的。
16303138735041.jpg
再如下,我们将中间填充代码改为0x21,混淆效果明显了一些,结果如下。
16303139029641.jpg

那么我们再来一个加强版,代码如下:

void example2_3()
{
    __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 = 23;
}

16303140480428.jpg

call&ret构造花指令

如下案例,代码中的esp存储的就是函数返回地址,对[esp]+8,就是函数的返回地址+8,正好盖过代码中的函数指令和垃圾数据。

void example3()
{

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

如下图所示,是上述花指令代码的效果,这里最好自己动态跟以下。
16303144293952.jpg

call 指令的直观理解:push 函数返回地址; jmp 立即数
ret 指令的直观理解:pop eip; add esp,4

利用函数返回确定值

有些函数返回值是确定的,比如我们自己写的函数,返回值可以是任意非零整数,就可以自己构造永恒跳转。

还有些API函数也是如此,比如在Win下HMODULE LoadLibraryA(LPCSTR lpLibFileName);函数,如果
我们故意传入一个不存在的模块名称,那么他就会返回一个确定的值NULL,此时就可以通过这个函数来构造永恒跳转。如下例子:

void example4_1()
{

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

混淆效果如下:
16303150474255.jpg

call和ret的组合

如下代码:

void __declspec(naked)__cdecl example5(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 = 5;
    __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]; //将ebp的地址存放于eax
         add dword ptr ss : [eax - 0x50], 26;  //该地址存放的值正好是函数返回值,
                                              //不过该地址并不固定,根据调试所得。加26正好可以跳到下面的mov指令,该值也是调试计算所得
         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];  //将原本的eax值返回eax寄存器

    }

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

16303162086172.jpg

也就是说思路有很多种,按照自己喜欢的方式组合,只要不影响其他正常代码的运行就可以,如下也是比较好的两种思路

call嵌套的其他思路1

    call LABEL1
    db 0E8h
LABEL2:
    jmp LABEL3
    db 0
    db 0
    db 0E8h
    db 0F6h
    db 0FFh
    db OFFh
    db OFFh
LABEL1: 
    :call LABEL2
LABEL3:
    add esp,8

call嵌套的其他思路2

    push eax
    call LABEL1
    db 29h
    db 5Ah
LABEL1:
    POP eax
    imul eax,3
    call LABEL2
    db 29h
    db5Ah
LABEL2:
    add esp,4
    pop eax

花指令原理另类利用

当我们理解了花指令的原理后,我们可以在将花指令中的垃圾数据替换为一些特定的特征码,可以对应的$“定位功能”$,尤其在SMC自解码这个反调试技术中可以运用。例如:

asm
{
  Jz Label
  Jnz Label
  _emit 'h'
  _emit 'E'
  _emit 'l'
  _emit 'L'
  _emit 'e'
  _emit 'w'
  _emit 'o'
  _emit 'R'
  _emit 'l'
  _emit 'D'
Label:
}

将这串特征码hElLowoRlD嵌入到代码中,那我们只需要在当前进程中搜索hElLowoRlD字符串,就可以定位到当前代码位置,然后对下面的代码进行SMC自解密。

小结

构造永恒跳转,添加垃圾数据

综合题目案例

IDC脚本去花

我们以题目[MSLRH].exe为例子,分析一段花指令,使用IDA中的快捷键DC来手动过花指令。手动分析清楚其流程后,我们可以写一个脚本,来批量匹配花指令的模式NOP掉影响静态分析的代码。
16297700199269.jpg

写好的IDC脚本如下:

//文件名:test.idc
#include <idc.idc>
static main() 
{
    auto x,FBin,ProcRange;
    FBin = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF";
    //目标 = "E8 0A tel:00 00 00 90 EB 0C tel:90 90 90 90 90 90 90";
    //花指令1的特征码
    for (x = FindBinary(MinEA(),0x03,FBin);x != BADADDR;x = FindBinary(x,0x03,FBin))
    {
            x=x+5; //返回的x是第一个E8的地址,
                  //加上5是第二个E8的地址
            PatchByte (x,0x90);//nop掉
            x = x + 3; //00
            PatchByte (x,0x90);
            x++;  //00 E8
            PatchWord (x,0x9090);
            x =x + 2 ; //F6 FF FF FF
            PatchDword(x,0x90909090);
    }
}

使用IDAPython去花

import idautils
import idc

def my_nop(addr, endaddr):
    while addr < endaddr:
        patch_byte(addr, 0x90)
        addr += 1

pattern = "E8 0A 00 00 00 E8 EB 0C 00 00 E8 F6 FF FF FF"
cur_addr = 0x456000
end_addr = 0x467894

while cur_addr<end_addr:
    cur_addr = idc.find_binary(cur_addr,SEARCH_DOWN,pattern)
    print("patch address: " + str(cur_addr)) # 打印提示信息
    if cur_addr == idc.BADADDR:
        break
    else:
        my_nop(cur_addr+5,cur_addr+6)
        my_nop(cur_addr+8,cur_addr+14)
    cur_addr = idc.next_head(cur_addr)

附带源码:
JunkCode.7z (5.11 KB, 下载次数: 23)

参考

免费评分

参与人数 16吾爱币 +18 热心值 +16 收起 理由
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
xmmyzht + 1 + 1 谢谢@Thanks!
努力加载中 + 1 + 1 热心回复!
longestusername + 2 + 1 谢谢@Thanks!
nevinhappy + 1 + 1 用心讨论,共获提升!
timeni + 1 + 1 我很赞同!
dsanke + 1 + 1 用心讨论,共获提升!
笙若 + 1 + 1 谢谢@Thanks!
杨辣子 + 1 + 1 谢谢@Thanks!
15183222057 + 1 + 1 用心讨论,共获提升!
wws天池 + 1 + 1 热心回复!
wdraemv + 1 + 1 谢谢@Thanks!
Ravey + 1 + 1 谢谢@Thanks!
鹤舞九月天 + 1 + 1 用心讨论,共获提升!
mqlv + 1 + 1 用心讨论,共获提升!
xouou + 1 + 1 666

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yupi1984 发表于 2021-9-15 10:49
纯技术干货,很不错,花指令通常用于干扰脱壳,不知还有没有其它应用场景。支持楼主。
njzyjyyd 发表于 2021-9-13 18:31
guangzisam 发表于 2021-9-13 19:43
bsjasdq 发表于 2021-9-14 12:32
vmp的代码能还原不
mic2zac 发表于 2021-9-14 17:36
知识盲区了
submariner 发表于 2021-9-14 18:23
好复杂,看着头晕
fyh505099 发表于 2021-9-14 23:10
学习学习,谢谢分享
yangyi888 发表于 2021-9-14 23:11
谢谢楼主的分享
残风逝雪夜阑天 发表于 2021-9-14 23:28
感谢分享
zhhispig 发表于 2021-9-14 23:44
除了案例都看懂了 感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2021-9-18 07:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表