首页
社区
课程
招聘
[原创]三个反汇编库的简单介绍和使用
2021-5-16 23:28 14527

[原创]三个反汇编库的简单介绍和使用

2021-5-16 23:28
14527

三个反汇编库的简单介绍和使用


 

环境 : VS2017 + Windows 10

udis86" class="anchor" href="#一、 udis86">一、 Udis86

​ udis86的代码风格十分简洁,功能函数短小,变量命名清楚又简洁,接口干净意思明白,操作灵活。从github(官网)下载源码,解压之后我们把 libudis86 文件夹放到我们的项目里面,然后把udis86.h外一层,结果我们的设置好后编译,发现有点问题:(github上下的是新一点的版本,但是编译提示很多错误,比如少itab.h,什么的,但是打开官网就发现这个文件确实是在udis86这个文件夹下,不知道什么情况,也懒得研究了,直接用官网的那个吧)
optionbuild
由于我用的是VS2017,可能限制比较多,然后我们把这些设置好后,发现还有个memset未定义没有解决,当然只是警告,但是按我们的尿性,要搞就搞完美的。
我们直接在udis86.c最上面几个#include的后面放上 #include<memory.h> ,编译OK,

 

写下demo看看: 随便找一段汇编hex,然后翻译看看

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
#include<stdio.h>
#include<string.h>
#include"udis86.h"
 
uint8_t dump[] = {
    0x550x8B0xEC0x510x8B0x450x080x50,
    0xE80xC60x050xFE0xFF0x830xC40x04,
    0x850xC00x740x090xC70x450xFC0x00,
    0x000x000x000xEB0x070xC70x450xFC,
    0xFF0xFF0xFF0xFF0x8B0x450xFC0x8B,
    0xE50x5D0xC3
};
 
int main(int argc,char *argv[])
{
    ud_t ud_obj;
 
    ud_init(&ud_obj);
    ud_set_input_buffer(&ud_obj, dump,sizeof(dump));
    ud_set_pc(&ud_obj, 0x420170);
    ud_set_mode(&ud_obj, 32);
    ud_set_syntax(&ud_obj, UD_SYN_INTEL);
 
    while (ud_disassemble(&ud_obj)) {
        printf("%llX >> %-16s |  %s\n", ud_insn_off(&ud_obj), ud_insn_hex(&ud_obj),ud_insn_asm(&ud_obj));
    }
 
    return 0;
}

编译运行,效果如下:

 

完美翻译,只是那个-1和0xffffffff看着有差异,这个肯定是可以改的,这个就不多说了,具体看官方文档吧。他支持的x86体系扩展指令有:

MMX, FPU (x87), AMD 3DNow, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AES, AMD-V, INTEL-VMX, SMX

 

速度超级快,代码也不臃肿,那么久决定他本身实现的一些功能可能就少了,有些不能直接用,还要自己把一些函数组合起来,一些寄存器跟踪操作也不太好操作,比如mov eax,ebx 这条指令执行后我们的oprand 1(eax)是发生了什么事,这个就要处理这个mnemonic是什么等等。

beaengine" class="anchor" href="#二、beaengine">二、BeaEngine

A.失败的尝试:

在github中下载beaengine-master.zip后解压到当前目录得到beaengine-master文件夹,然后用cmake生成一下工程

1
2
3
4
> cd beaengine-master
\beaengine-master>mkdir build
\beaengine-master>cd build
\beaengine-master\build>cmake ..

打开cmd,进入刚刚的目录,然后进入我们刚刚解压的目录,然后执行上面的命令,构建官方工程,注意如果你的VS有安装了驱动环境,可能会构建失败,成功如图:

 

然后我们进入到刚刚创建的build文件里面去,直接双击BeaEngine.sln打开VS,然后点击生成,成功。

 

警告我们就忽略吧。然后我们在刚刚的build目录开始路径 build\lib\Windows.msvc.Debug\Debug下得到BeaEngine_s_d_l.lib静态链接库,等下我们会用到。

 

A.我们新建一个过程 命名为BeaEngine_demo,然后我们提取一些必要文件到工程目录下,如下图:

把我们刚刚BeaEngine_s_d_l.lib放到lib文件夹下,然后headers直接复制刚刚解压的beaengine-master目录下的headers过来就行。不需要设置什么,然后在BeaEngine_demo.cpp写下测试代码:

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
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "headers/BeaEngine.h"
 
#pragma comment(lib,"lib/BeaEngine_s_d_l.lib")
 
int main(int argc,char *argv[])
{
    uint8_t temp[] = {
        0x550x8B0xEC0x510x8B0x450x080x50,
        0xE80xC60x050xFE0xFF0x830xC40x04,
        0x850xC00x740x090xC70x450xFC0x00,
        0x000x000x000xEB0x070xC70x450xFC,
        0xFF0xFF0xFF0xFF0x8B0x450xFC0x8B,
        0xE50x5D0xC3
    };
    DISASM infos;
    int len= 0;
 
    //puts(BeaEngineVersion());
    (void)memset(&infos, 0, sizeof(DISASM));
    infos.Archi = 1;
    infos.EIP = (UInt64)temp;
    infos.VirtualAddr = 0x420170;
 
    while ((infos.Error == 0)&&(infos.EIP-(UInt64)temp)<sizeof(temp)) {
        len = Disasm(&infos);
        if (infos.Error != UNKNOWN_OPCODE) {
            printf("%llx >> %s\n",infos.VirtualAddr,infos.CompleteInstr);
            infos.EIP += len;
            infos.VirtualAddr+=len;
        }
    }
    return 0;
}

编译,发现提示

严重性 代码 说明 项目 文件 行 禁止显示状态
错误 LNK2019 无法解析的外部符号 imp Disasm,该符号在函数 _main 中被引用 BeaEngine_demo E:\Test Project Vs2017\BeaEngine_demo\BeaEngine_demo\BeaEngine_demo.obj 1

 

意思是 Disasm这个函数有了说明,但是我们没有找到他的实现,我们无法解析他。这就很奇怪了我们不是已经链接上lib静态库了吗?我们返回到build工程。

查看这里,如果BEA_ENGINE_STAIC宏被定义,则BEA_API不被赋宏,你也可以理解为NULL,不定义函数导入或者导出。那么我们看看工程设置。

把BEA_ENGINE_STATIC去掉,添加BUILD_BEA_ENGINE_DLL,这个时候BEA_API应该指向的是bea_api_export__ 编译生成,然后把这个文件放到我们demo的lib下,demo编译生成,还是一样的错误。回到GitHub,看到如下,编译为动态连接库(DLL)用吧,如果有知道原因的请告知一下,谢谢:

编译为动态链接库的cmake选项,

B.正确的尝试

我们重新解压beaengine-master,右键解压到beaengine-master注意目录结构,然后我们命令行

1
>cmake -DoptBUILD_DLL=ON beaengin-master

成功后差不多是这个样子

然后用我们的vs2017打开这个.sln,编译生成OK。
在E:\Test Project Vs2017\beaengine-master\lib\Windows.msvc.Debug\Debug找到导出表BeaEngine_d_l.lib,我们可以跟A中尝试错误的那个对比一下

按道理那么我们A中的逻辑应该是没错的,用dumpbin看一下这个导出表lib的导出函数,OK,和之前我们看到的要导出的3个函数是吻合的,这里怎么多了_

用dumpbin去看那个1.388KB的那个lib,静态链接库,没看到导出函数,不知道是不是我用法错了,函数这个LIB没搞对。把刚刚得到的BeaEngine_d_l.lib导出表放到我们的demo文件下的lib里面去,然后把代码连接的lib改一下:

1
2
3
#pragma comment(lib,"lib/BeaEngine_s_d_l.lib")
改为
#pragma comment(lib,"lib/BeaEngine_d_l.lib")

编译生成通过,运行提示没有DLL,OK把DLL也是复制过来,在文件夹

1
E:\Test Project Vs2017\beaengine-master\bin\Windows.msvc.Debug\Debug

下找到DLL(BeaEngine_d_l.dll),复制一份到我们的demo的debug文件夹下

运行比较一下结果:

这里有看雪stanford前辈总结的BeaEngine库的反汇编参数和函数说明:https://bbs.pediy.com/thread-177301.htm.这里就不多说明了。

zydisasm" class="anchor" href="#三、zydisasm">三、Zydisasm

​ 这个可能不怎么听过,如果说x64dbg那你可能就我靠原来是他,没错x64dbg调试器的反汇编就是用的他,各方面都比较全,他和BeaEngine最大的区别,就我目前而言,我发现他比较控制入微,比如说的mov ax,0x11 那么他可以解析到是ax寄存器,而BeaEngine只能解析到是eax,用其他的辅助的话也能知道是ax,但是反过来在代码层面上Zydisasm对这条汇编,如果你只要ax是eax你只要知道他是eax寄存器就行了,不管他是ax,al,ah,那么他可能就有点难受了,但是前者的BeaEngine就能直接分辨出是eax,这样对于vmp这样的混淆来说,BaeEngine直接操作可能舒服点。官网:https://zydis.re/,其官网主页最下面有个及时反汇编,可以让读者感受一下他的魅力:

 

OK,不能在水了,直接github下载源码,解压,注意dependencies文件夹下的zycore文件有没下到,我是直接下那个他是没帮我下这个Zycore的,要在github里面,点击这个dependencies文件夹下跟上去,然后自己下这个zycore,看名字就知道这个很重要了,Zydis 核心是吧 。下载好后,把他按github上排列的文件夹结构给他搞好,解压到当前文件得到zydis-master文件夹,创建一个文件夹generZydis,然后把刚刚的zydis-master放到里面去,把刚刚下的zycore-master解压到当前文件夹,得到zycore........文件夹,重命名为zycore,然后把这个文件夹zycore放到generZydis/Zydis-master/dependencies文件夹下,覆盖之前的zycore,OK,然后打开cmd窗口

1
2
>cd generZydis
generZydis>cmake zydis-master

就可以了,成功如图:

 

有个个什么失败了,不需要管他,是啥,我不知道额。生成后gener文件夹下如图:

 

用VS2017去打开这个Zydis.slh即可,编译生成,看到第一个例子:Formatter01

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
int main(void)
{
    if (ZydisGetVersion() != ZYDIS_VERSION)
    {
        fputs("Invalid zydis version\n", ZYAN_STDERR);
        return EXIT_FAILURE;
    }
 
    ZyanU8 data[] =
    {
        0x48, 0x8B, 0x05, 0x39, 0x00, 0x13, 0x00, // mov rax, qword ptr ds:[<SomeModule.SomeData>]
        0x50,                                     // push rax
        0xFF, 0x15, 0xF2, 0x10, 0x00, 0x00,       // call qword ptr ds:[<SomeModule.SomeFunction>]
        0x85, 0xC0,                               // test eax, eax
        0x0F, 0x84, 0x00, 0x00, 0x00, 0x00,       // jz 0x007FFFFFFF400016
        0xE9, 0xE5, 0x0F, 0x00, 0x00              // jmp <SomeModule.EntryPoint>
    };
 
    ZydisDecoder decoder;
    ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64);
 
    DisassembleBuffer(&decoder, &data[0], sizeof(data));
 
    return 0;
}

在结合,官方文档不难看出他的逻辑,然后我们写下我们的demo:

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
#include <stdio.h>
#include <inttypes.h>
#include <Zydis/Zydis.h>
 
int main(int argc,char *argv[])
{
 
    uint8_t dump[] = {
        0x550x8B0xEC0x510x8B0x450x080x50,
        0xE80xC60x050xFE0xFF0x830xC40x04,
        0x850xC00x740x090xC70x450xFC0x00,
        0x000x000x000xEB0x070xC70x450xFC,
        0xFF0xFF0xFF0xFF0x8B0x450xFC0x8B,
        0xE50x5D0xC3
    };
 
    // Initialize decoder context
    ZydisDecoder decoder;
    ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_COMPAT_32, ZYDIS_ADDRESS_WIDTH_32);
 
    // Initialize formatter. Only required when you actually plan to do instruction
    // formatting ("disassembling"), like we do here
    ZydisFormatter formatter;
    ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
    ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SEGMENT, ZYAN_TRUE);
    ZydisFormatterSetProperty(&formatter, ZYDIS_FORMATTER_PROP_FORCE_SIZE, ZYAN_TRUE);
 
    // Loop over the instructions in our buffer.
    // The runtime-address (instruction pointer) is chosen arbitrary here in order to better
    // visualize relative addressing
    ZyanU32 runtime_address = 0x00401000;
    ZyanUSize offset = 0;
    const ZyanUSize length = sizeof(dump);
    ZydisDecodedInstruction instruction;
    while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, dump + offset, length - offset,
        &instruction)))
    {
        // Print current instruction pointer.
        printf("%08" PRIX32 ">>  ", runtime_address);
 
        // Format & print the binary instruction structure to human readable format
        char buffer[256];
        ZydisFormatterFormatInstruction(&formatter, &instruction, buffer, sizeof(buffer),
            runtime_address);
 
        puts(buffer);
 
        offset += instruction.length;
        runtime_address += instruction.length;
    }
}

当然直接这样肯定不行,我们还要设置一下环境是吧。怎么设置环境呢,我们可以参照Formatter01这个例子中对lib和include等的设置,

 

刚刚我解压的不是应该在temp下吗,怎么到了Test Poject目录去了,而且Project还是拼错的,这个是我之前的,我直接就用了这个图了。然后官方有文档什么的,而且这些函数名字根据名字应该也是能猜出大概的意思就不多解释了。之前不是说他不能简单的直接知道一条汇编中寄存器操作的是EAX或者EBX,不管是ax还是al都是eax,当然这个目前只是针对32位的来说,64位的话我们就应该说的是rax了,然后我自己给他加了个成员:即这个 ZydisRegistrerGroup

 

在他解析汇编出寄存器的位置判断这个寄存器属于哪个组,然后赋值完事。

四、有什么用,这里主要是简单讲一下vmp指令的去混淆

写着这个好像没啥用啊,感觉是吧我们来看看用beaengine写的个简单的vmp指令反混淆处理前后图:

明显我们可以看出来这是一个 vm_popReg32 是吧。那么是怎么去混淆的这个,当然这里说的是针对vmp的vm指令,首先找到vm环境,还记得之前的文章吗。vmp3指令分析浅析,vm环境有5个寄存器:

1
2
3
4
5
6
7
8
9
10
typedef struct vm_envir {
 
    __int32 vm_esp;//
    __int32 vm_reg;//寄存器基址 一般是esp
    __int32 vm_jbase;//跳转基址
    __int32 vm_key;//解密因子
    __int32 vm_bytes;//vm码
 
 
}VM_ENVIR;

那么这个VM环境是怎么得到的,那得从vmp入口 push + call 中的call里面分析得到,这里不做分析,然后得到这个环境就好做了,判断与这些环境相关的汇编指令,保存下来就是去混淆后的代码,当然里面也后很多细节,这里不做多讨论,配合收集的一些特征,动态的去得到一些位置的信息,那么就能知道 是vmpopReg32 vm_reg+0xXX 了 ,但是遇到的最大的问题是 如果vm代码太多的话,可能效率上有问题,还可能卡死,当然还是分支什么的,这里当做抛砖引玉吧。

 

后记。本想写个vmp3分析插件的,但是期间有遇到很多问题,就不知道怎么写了,但是怎么说呢,写这篇文章算是给自己一个交代吧,期间也是体会到了,有想法的人很多,但是能把脑中想法写下来是需要毅力的。。。。。。而我没有


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-5-17 21:57 被zuoshang编辑 ,原因: 忘发原文了
上传的附件:
收藏
点赞6
打赏
分享
最新回复 (19)
雪    币: 4024
活跃值: (5843)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
fjqisba 2021-5-17 09:42
2
0
感觉capstone最专业
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_xvslrxfw 2021-5-17 10:04
3
0
雪    币: 6677
活跃值: (3295)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
htpidk 2021-5-17 10:13
4
0
反向 汇编转机器码 用什么
雪    币: 3733
活跃值: (3743)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Mxixihaha 2021-5-17 10:21
5
0
fjqisba 感觉capstone最专业[em_13]
但也是效率最慢的
雪    币: 1524
活跃值: (3320)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
小希希 2021-5-17 10:48
6
0
写的不错
雪    币: 1110
活跃值: (1025)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
PeterZheng 2021-5-17 10:53
7
1

Hi, 可以评估一下Bitdefender开源的反汇编引擎:https://github.com/bitdefender/bddisasm,看起来还不错。

最后于 2021-5-17 10:55 被PeterZheng编辑 ,原因:
雪    币: 453
活跃值: (129)
能力值: (RANK:0 )
在线值:
发帖
回帖
粉丝
同志们好啊 2021-5-17 11:08
8
0
到底谁最好呢,推荐用哪个呢?
其实搞个表格,挨个打分,就统计出来了.
比如,代码漏洞多不多,功能全不全,文档好不好,许可友不友好?根据几个点,一下就分析出来了,该用哪个代码库了.
雪    币: 160
活跃值: (2288)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Yecate 2021-5-17 12:36
9
0
htpidk 反向 汇编转机器码 用什么
https://www.keystone-engine.org/
雪    币: 1042
活跃值: (455)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Rookietp 2021-5-17 15:47
10
0
所以 BeaEngine 还是只能静态链接嘛。
雪    币: 7074
活跃值: (3468)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
bxc 6 2021-5-17 18:06
11
0
capstone engine No.1
雪    币: 6677
活跃值: (3295)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
htpidk 2021-5-17 19:13
12
0
Yecate https://www.keystone-engine.org/
谢谢,抽空去试试
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:52
13
0
我这边用的是动态链接库 显示调用
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:52
14
0
Rookietp 所以 BeaEngine 还是只能静态链接嘛。
我这边用的是动态链接库 显示调用 
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:53
15
1
同志们好啊 到底谁最好呢,推荐用哪个呢? 其实搞个表格,挨个打分,就统计出来了. 比如,代码漏洞多不多,功能全不全,文档好不好,许可友不友好?根据几个点,一下就分析出来了,该用哪个代码库了.
个人目前感觉Zydis好一点 代码命名很好看
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:54
16
0
PeterZheng Hi,&nbsp;可以评估一下Bitdefender开源的反汇编引擎:https://github.com/bitdefender/bddisasm,看起来还不错。
嗯 回头我试试哈
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:55
17
0
小希希 写的不错[em_63]
感谢 点赞
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:57
18
0
htpidk 反向 汇编转机器码 用什么
这个目前汇编器我没试过 可以考虑下9楼的介意 
雪    币: 1846
活跃值: (1842)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
zuoshang 1 2021-5-17 21:57
19
0
雪    币: 220
活跃值: (493)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lolikon 2021-5-17 21:59
20
0
我测过zydis和udis86, 前者要快一点, 后者实在太老了,多少年前的东西了
游客
登录 | 注册 方可回帖
返回