新手学习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

最后得到的结果是这样的
 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: var num1 = 10; var num2 = 20; var result = num1 - num2; console.log(result); 答案: console.log(num1 - num2); ### 回答2: VMP(Virtual Machine Program)是一种用于模拟虚拟机的编程语言,通常用于实现一些简单的计算功能。下面是一个用VMP程序演示JS减法的代码: ```vmp .var operand1 .var operand2 .var result .read operand1 .read operand2 .sub result, operand1, operand2 .print result ``` 以上代码首先声明了三个变量:`operand1`,`operand2`和`result`,分别用于存储运算的两个操作数和结果。 接着使用`.read`指令分别从输入中读取两个操作数的值。 然后使用`.sub`指令进行减法运算,将`operand1`减去`operand2`的结果存入`result`中。 最后使用`.print`指令输出结果。 在实际运行该VMP程序时,可以将它与一个VMP虚拟机一起使用,该虚拟机可以解释和执行VMP程序。通过输入两个操作数的值,虚拟机将会返回两个操作数的差作为输出结果。 需要注意的是,以上代码是一个简化的示例,实际的VMP程序可能会更加复杂,包含更多的指令和处理逻辑,以实现更复杂的功能。 ### 回答3: VMP(Virtual Machine Program)是一种虚拟机程序,用于模拟计算机或处理器的行为,并执行特定的指令集。下面是一个使用VMP实现一段JS减法的演示代码: ``` // 假设我们要计算 10 - 5 的结果 // 定义VMP指令集 const VMP_INSTRUCTIONS = [ { name: 'LOAD', // 加载指令 operation: function (registers, operand) { registers['R0'] = operand; } }, { name: 'SUBTRACT', // 减法指令 operation: function (registers, operand) { registers['R0'] -= operand; } }, { name: 'PRINT', // 打印指令 operation: function (registers) { console.log(registers['R0']); } } ]; // 定义VMP虚拟机程序 function runVMPProgram(program) { const registers = { 'R0': 0 // 定义寄存器R0,用于存储运算结果 }; program.forEach(instruction => { const opcode = instruction[0]; // 指令的操作码 const operand = instruction[1]; // 指令的操作数 // 根据操作码找到对应的指令 const vmpInstruction = VMP_INSTRUCTIONS.find(inst => inst.name === opcode); // 执行指令的操作 vmpInstruction.operation(registers, operand); }); } // 定义JS减法的VMP程序 const jsMinusVMPProgram = [ ['LOAD', 10], // 将操作数10加载到寄存器R0中 ['SUBTRACT', 5], // 从寄存器R0中减去操作数5 ['PRINT'] // 打印寄存器R0中的结果 ]; // 执行VMP程序 runVMPProgram(jsMinusVMPProgram); ``` 以上代码演示了如何使用VMP虚拟机程序模拟JS减法操作。首先定义了一组VMP指令集,其中包含LOAD、SUBTRACT和PRINT三种指令。然后定义了一个运行VMP程序的函数,其中通过循环执行指令集中的指令,并传入相应的操作数。最后定义了一个JS减法的VMP程序,将两个操作数10和5分别加载到寄存器R0中,并执行减法操作,最后打印结果。运行VMP程序之后,控制台会输出结果为5。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜栩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值