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

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 7226|回复: 53
收起左侧

[原创] 新手学习Vmp之控制流程图生成

  [复制链接]
fjqisba 发表于 2023-6-17 16:14
本帖最后由 fjqisba 于 2023-6-17 16:20 编辑

新手学习Vmp之控制流程图生成

控制流程图的生成对于反混淆分析来说是非常重要的一步,这里记录一下我研究的过程,以Vmp2为例子。

这里我的环境准备如下:

Visual Studio + IDA SDK + Capstone + Unicorn + Graphviz

IDA SDK插件环境,主要是有一些API可以调用,方便编写代码,X64Dbg插件环境可以替代之。

Capstone,一个很不错的反汇编引擎,IDA自带的反汇编引擎不太好用,用这个替代之。

Unicorn,指令模拟执行,用来跟踪指令。

Graphviz,一个绘图工具,可以将控制流程图可视化。

要生成流程图,首先使用unicron引擎对指令进行跟踪,大致步骤如下:

1、使用uc_mem_map和uc_mem_write函数填充内存区域和堆栈

2、uc_hook_add设置监视函数,每次执行指令前检查退出条件,例如当前指令位于text区段且上一条指令是ret的时候,基本上就是vmp结束的时候了。

3、uc_emu_start进行trace,拿到所有的指令跟踪数组。

之后是根据这些地址动态生成控制流程图,这里需要了解一下基本块这个概念。

核心逻辑如下:

bool VmpTraceFlowGraph::GenerateBasicFlowData(std::vector<ea_t>& traceList)
{
        if (!traceList.size()) {
                return false;
        }
        cs_insn* curIns;
        VmpTraceFlowNode* currentNode = createNode(traceList[0]);;
        for (unsigned int n = 0; n < traceList.size(); ++n) {
                const ea_t& curAddr = traceList[n];
                if (!DisasmManager::DecodeInstruction(curAddr, curIns)) {
                        return false;
                }
                //不管是什么指令,都立即追加到当前基本块
                if (!currentNode->bTraced) {
                        currentNode->addrList.push_back(curAddr);
                        updateInstructionToBlockMap(curAddr, currentNode);
                }
                //判断是否为终止指令
                if (isEndIns(curIns)) {
                        //检查是否为最后一条指令
                        if (n + 1 >= traceList.size()) {
                                break;
                        }
                        currentNode->bTraced = true;
                        //这里开始进行核心判断
                        ea_t nextNodeAddr = traceList[n + 1];
                        VmpTraceFlowNode* nextNode = instructionToBlockMap[nextNodeAddr];
                        linkEdge(curAddr, nextNodeAddr);
                        //下一个节点是新节点
                        if (!nextNode) {
                                currentNode = createNode(nextNodeAddr);
                        }
                        //已访问过该节点,且节点指向Block头部
                        else if (nextNode->nodeEntry == nextNodeAddr) {
                                currentNode = nextNode;
                        }
                        else {
                                //节点指向已有区块其它地址,需要对区块进行分割
                                currentNode = splitBlock(nextNode, nextNodeAddr);
                        }
                }
        }
        return true;
}

再进行节点合并优化,核心代码是这样的:

void VmpTraceFlowGraph::MergeNodes()
{
        //已确定无法合并的节点
        std::set<ea_t> badNodeList;
        bool bUpdateNode;
        do
        {
                bUpdateNode = false;
                std::map<ea_t, VmpTraceFlowNode>::iterator it = nodeMap.begin();
                while (it != nodeMap.end()) {
                        ea_t nodeAddr = it->first;
                        if (badNodeList.count(nodeAddr)) {
                                it++;
                                continue;
                        }
                        //判断合并条件
                        //条件1,指向子节点的边只有1条
                        if (toEdges[nodeAddr].size() == 1) {
                                ea_t fromAddr = *toEdges[nodeAddr].begin();
                                VmpTraceFlowNode* fatherNode = instructionToBlockMap[fromAddr];
                                //条件2,父节点指向的边也只有1条
                                if (fromEdges[fromAddr].size() == 1) {
                                        //条件3,子节点不能指向父节点
                                        if (!fromEdges[nodeAddr].count(fatherNode->addrList[fatherNode->addrList.size() - 1])) {
                                                executeMerge(fatherNode, &it->second);
                                                bUpdateNode = true;
                                                it = nodeMap.erase(it);
                                                continue;
                                        }
                                }
                        }
                        badNodeList.insert(nodeAddr);
                        it++;
                }
        } while (bUpdateNode);
}

最后是将流程图转换成dot语言,核心代码如下:

std::string VmpTraceFlowGraph::DumpGraph()
{
        std::stringstream ss;
        ss << "strict digraph \"hello world\"{\n";
        cs_insn* tmpIns = 0x0;

        char addrBuffer[0x10];
        for (std::map<ea_t, VmpTraceFlowNode>::iterator it = nodeMap.begin(); it != nodeMap.end(); ++it) {
                VmpTraceFlowNode& node = it->second;
                sprintf_s(addrBuffer, sizeof(addrBuffer), "%08X", it->first);
                ss << "\"" << addrBuffer << "\"[label=\"";
                for (unsigned int n = 0; n < node.addrList.size(); ++n) {
                        //测试代码
                        if (n > 20 && (n != node.addrList.size() - 1)) {
                                continue;
                        }
                        DisasmManager::DecodeInstruction(node.addrList[n], tmpIns);
                        sprintf_s(addrBuffer, sizeof(addrBuffer), "%08X", node.addrList[n]);
                        ss << addrBuffer << "\t" << tmpIns->mnemonic << " " << tmpIns->op_str << "\\n";
                }
                ss << "\"];\n";
        }

        for(std::map<ea_t, std::unordered_set<ea_t>>::iterator it = fromEdges.begin(); it != fromEdges.end(); ++it){
                std::unordered_set<ea_t>& edgeList = it->second;
                for (std::unordered_set<ea_t>::iterator edegIt = edgeList.begin(); edegIt != edgeList.end(); ++edegIt) {
                        VmpTraceFlowNode* fromBlock = instructionToBlockMap[it->first];
                        sprintf_s(addrBuffer, sizeof(addrBuffer), "%08X", fromBlock->nodeEntry);
                        ss << "\"" << addrBuffer << "\" -> ";
                        sprintf_s(addrBuffer, sizeof(addrBuffer), "%08X", *edegIt);
                        ss << "\"" << addrBuffer << "\";\n";
                }
        }
        ss << "\n}";
        return ss.str();
}

得到文件后,调用dot命令行打印出流程图

dot graph.txt -T png -o vmp2.png

最后得到的结果是这样的
vmp2.png
最后是参考代码
TestVmp.7z (4.7 KB, 下载次数: 64)

免费评分

参与人数 26威望 +2 吾爱币 +128 热心值 +23 收起 理由
mp228by + 1 + 1 谢谢@Thanks!
crysky7ye + 1 + 1 用心讨论,共获提升!
b12312312 + 1 + 1 我很赞同!
笙若 + 1 + 1 谢谢@Thanks!
sunzhitong_1987 + 1 我很赞同!
djsz + 1 用心讨论,共获提升!
gaosld + 1 + 1 谢谢@Thanks!
Qfrost + 2 + 1 感谢分享
yellowchristmas + 1 谢谢@Thanks!
Lumine + 1 + 1 谢谢@Thanks!
D3M1 + 1 + 1 谢谢@Thanks!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Anonymous、 + 2 + 1 谢谢@Thanks!
yanecc + 1 + 1 我很赞同!
Tonyha7 + 2 + 1 用心讨论,共获提升!
alanhays + 2 + 1 用心讨论,共获提升!
脱下小可爱 + 1 + 1 我很赞同!
superworker2022 + 1 + 1 我很赞同!
Cofei430 + 1 + 1 谢谢@Thanks!
lsq665 + 1 + 1 我很赞同!
Hmily + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Bob5230 + 1 我很赞同!
sunlei658 + 1 + 1 谢谢@Thanks!
m1n9yu3 + 1 + 1 谢谢@Thanks!
Lemon1001 + 1 谢谢@Thanks!
mayl8822 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

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

gly198752 发表于 2023-6-17 16:43
学习了,大佬大佬
sec-u 发表于 2023-6-17 18:42
空欢 发表于 2023-6-17 19:18
Lemon1001 发表于 2023-6-17 20:11
谢谢分享,学习一下
moruye 发表于 2023-6-17 22:48
楼主辛苦,刚好下载学习研究下
zjh889 发表于 2023-6-18 02:03
谢谢大师分享,俺学习看看!
至尊Cracker 发表于 2023-6-18 09:14
编程写流程图,可以的
zhiaipojie0313 发表于 2023-6-18 11:35
支持支持
头像被屏蔽
我去年买了各表 发表于 2023-6-18 18:04
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册[Register]

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

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

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

GMT+8, 2024-4-28 04:26

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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