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

news/2024/5/18 23:53:38 标签: 学习, 流程图, 数学建模

学习vmp之控制流程图生成">新手学习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[zxsq-anti-bbcode-0]);;

        for (unsigned int n = 0; n < traceList.size(); ++n) {

                const ea_t& curAddr = traceList[zxsq-anti-bbcode-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[zxsq-anti-bbcode-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[zxsq-anti-bbcode-nodeAddr].size() == 1) {

                                ea_t fromAddr = *toEdges[zxsq-anti-bbcode-nodeAddr].begin();

                                VmpTraceFlowNode* fatherNode = instructionToBlockMap[zxsq-anti-bbcode-fromAddr];

                                //条件2,父节点指向的边也只有1条

                                if (fromEdges[zxsq-anti-bbcode-fromAddr].size() == 1) {

                                        //条件3,子节点不能指向父节点

                                        if (!fromEdges[zxsq-anti-bbcode-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[zxsq-anti-bbcode-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[zxsq-anti-bbcode-n], tmpIns);

                        sprintf_s(addrBuffer, sizeof(addrBuffer), "%08X", node.addrList[zxsq-anti-bbcode-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

最后得到的结果是这样的
 


 


http://www.niftyadmin.cn/n/442706.html

相关文章

为什么 kubernetes 环境要求开启 bridge-nf-call-iptables ?

文章目录 背景基于网桥的容器网络Service 同节点通信问题开启 bridge-nf-call-iptables我的环境netshoot 工具 参考 背景 Kubernetes 环境中,很多时候都要求节点内核参数开启 bridge-nf-call-iptables: sysctl -w net.bridge.bridge-nf-call-iptables1 参考官方文…

微信公众号每天定时发送消息给女朋友

前言 这是我在这个网站整理的笔记,关注我,接下来还会持续更新。 作者:RodmaChen 每天定时发送消息给女朋友 一. 环境准备二. 代码拉取和配置三. 项目部署3.1 直接运行3.2 后台运行 四. 效果图 参考地址:https://github.com/limoes…

使用python制作一个批量查询搜索排名的SEO免费工具

💂 个人网站:【海拥】【摸鱼游戏】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 搭建背景 最近工作中需要用…

23 前馈神经网络——Feedforward Neural Network

文章目录 23 前馈神经网络——Feedforward Neural Network23.1 神经网络背景23.2 解决非线性问题的三种方法 23 前馈神经网络——Feedforward Neural Network 23.1 神经网络背景 神经网络也是从机器学习中拓展而出。回顾之前的机器学习,我们可以将内容如下划分&am…

Python案例——采集专栏文章保存成pdf

前言 嗨喽,大家好呀~这里是爱看美女的茜茜呐 环境使用: python 3.8 >>>>>> 运行代码 pycharm 2022.3 >>>>>> 辅助敲代码 wkhtmltopdf 软件 找助理邀课老师获取 模块使用: 内置模块 re >>>正则表达式 第三方模…

4.4 超简单文书编辑器:nano

在Linux系统当中有非常多的文书编辑器存在,其中最重要的是vim。 nano使用很简单,可以直接加上文件名就能够打开一个旧文件或新文件。打开一个叫text.txt的文件名来看看: [ctrl]-G:取得线上说明(help)&…

LwIP RAW API 实现UDP多播收发

LwIP RAW API 实现UDP多播收发实现 1、初始化 static struct udp_pcb *multicast_pcb NULL; static ip_addr_t mutlcast_send_ip; static ip_addr_t mutlcast_recv_ip;static void udp_recv_multicast(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *add…

高考状元、通用语言和轰趴-UMLChina建模知识竞赛第4赛季第4轮

DDD领域驱动设计批评文集 欢迎加入“软件方法建模师”群 《软件方法》各章合集 参考潘加宇在《软件方法》和UMLChina公众号文章中发表的内容作答。在本文下留言回答。 只要最先答对前3题,即可获得本轮优胜。第4题为附加题,对错不影响获奖&#xff0c…