轉(zhuǎn)帖|使用教程|編輯:胡濤|2022-04-12 14:28:07.260|閱讀 476 次
概述:本文主要向您介紹VMProtect虛擬機(jī)保護(hù)分析入門,歡迎查閱!
# 界面/圖表報(bào)表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
以前在逆向分析的時(shí)候,遇見VMP的代碼就束手無策,只能跳過。最近在分析的時(shí)候又遇見vmp,準(zhǔn)備研究一下。我這次遇見的VMP用查殼工具看是VMProtect(1.60-2.05)[-]。所以本次選用的殼版本是VMP1.8,也可前往下載最新版試用!
VMP全稱VMProtect,號(hào)稱目前軟件保護(hù)最扣一道防線。為了防止逆向分析人員對(duì)軟件的逆向分析,VMP最主要的是對(duì)指定關(guān)鍵代碼進(jìn)行虛擬化,同時(shí)再加一些亂序跳轉(zhuǎn)和大量的廢指令,反調(diào)試,內(nèi)存保護(hù),導(dǎo)入表保護(hù),使逆向分析人員無法分析執(zhí)行的代碼,經(jīng)過VMP虛擬機(jī)的代碼被膨脹好多倍。本次學(xué)習(xí)只研究VMP最關(guān)鍵和最難的部分:虛擬化
我在visual stdio里寫了下面代碼,并對(duì)加殼時(shí)TestVmpFunc函數(shù)選擇虛擬化。本都得使用的調(diào)試器是x64dbg
#include <iostream> _declspec(naked) void TestVmpFunc() { __asm { mov eax,0x100 mov ebx,0x1000 add eax,ebx retn } } int main() { //下面這是特征碼,用于在調(diào)試器里定位自己的這段代碼 __asm { mov eax,eax mov eax,eax } while (true) { __asm { pushad mov eax, TestVmpFunc call eax popad } system("pause"); } std::cout << "完成了" << std::endl; return 0; }
用調(diào)試器附加觀察原來只有四條匯編指令:
被虛擬化后成這樣:
代碼被虛擬化之后,假如在調(diào)試器中單步執(zhí)行會(huì)跳來跳去,一條匯編會(huì)變成成百上千條指令,無法判斷他在干什么。
經(jīng)過一番查資料,知道本質(zhì)來講VMP是一個(gè)基于堆棧機(jī)的intel指令模擬器,對(duì)過編譯把原來的intel指令編譯成精心設(shè)計(jì)的一組虛擬指令,然后用自己的一套引擎來解釋執(zhí)行。VMP加殼后,他會(huì)將原來的代碼進(jìn)行刪除,導(dǎo)致基本完全無法進(jìn)行還原。
VMP是防止別人逆向分析自己的代碼,逆向分析的目的是分析代碼,了解代碼邏輯和代碼的目的,然后加以利用。看樣子,目前只能通過對(duì)虛擬機(jī)引擎的分析,來搞懂虛擬機(jī)引擎,然后理清代碼流程,達(dá)到逆向分析的目的。
定義寄存器和內(nèi)存
這里第8個(gè)寄存器為指令指針寄存器類似x86的eip
uint32_t g_regs[8];//8個(gè)寄存器 uint32_t g_mem[1000];//1000個(gè)內(nèi)存空間
這里為了簡單,規(guī)定每條指令都有三個(gè)操作數(shù)(哪怕某一條指令用不到三個(gè)參數(shù))
指令格式為:OPCODE r,s,t
//指令操作數(shù) struct Instruct { uint32_t opcode; uint32_t r; uint32_t s; uint32_t t; };
聲明OPCode
enum OP_CODE { opSTOP,/*停止執(zhí)行 忽略r,s,t參數(shù)*/ opIN,/*讀入一個(gè)值放到reg[r]里*/ opOUT,/*將reg[r]的值輸入*/ opADD,/*regs[r] = regs[s] + regs[t]*/ opLD,//regs[r]=dmem[regs[s] + t] opST,//dmem[regs[s] + t] = regs[r] opLDA,//regs[r]= regs[s]+t opLDC,//regs[r]=t };
std::vector<instruct> g_instruct_list;//指令列表
初始化
void Init() { memset(g_regs, 0, sizeof(g_regs)); g_instruct_list.clear(); }加載代碼
void LoadCode(const std::string & file_name) { //代碼文件為txt文件 //每行模式為opcode,r,s,t //例如:1,0,0,0 std::ifstream file(file_name); if (!file.is_open()) { return; } auto GetOneInstruct = [&file](Instruct & instruct) { char elem; uint32_t values[4] = { 0 }; bool success = true; for (int i = 0; i < 4 ; i++) { file >> values[i]; if (file.fail()) { success = false; break; } if (i < 4 - 1) { file >> elem; } } if (!success) { return false; } instruct = { values[0],values[1],values[2],values[3] }; return true; }; Instruct instruct; while (GetOneInstruct(instruct)) { g_instruct_list.push_back(instruct); } }
運(yùn)行指令
bool RunInstruct(const Instruct& instruct) { switch (instruct.opcode) { case opSTOP: return false; case opIN: Handle_opIN(instruct); break; case opOUT: Handle_opOUT(instruct); break; case opADD: Handle_opADD(instruct); break; default: throw std::logic_error("Invalid Op Code:" + std::to_string(instruct.opcode)); break; } return true; } void RunCode() { while (true) { uint32_t eip = g_regs[7]; if (eip > g_instruct_list.size() - 1) { break; } const Instruct& instruct = g_instruct_list.at(eip); if (!RunInstruct(instruct)) { break; } g_regs[7]++; } } // handle處理 void Handle_opIN(const Instruct& instruct); void Handle_opOUT(const Instruct& instruct); void Handle_opADD(const Instruct& instruct); void Handle_opLD(const Instruct& instruct); void Handle_opST(const Instruct& instruct); void Handle_opLDA(const Instruct& instruct); void Handle_opLDC(const Instruct& instruct);
測試
int main() { Init(); LoadCode("asm.txt"); RunCode(); return -1; }
注:本文轉(zhuǎn)自博客園/ 作者:張東升 / 如涉及侵權(quán),請聯(lián)系刪除!
查閱→VMProtect虛擬機(jī)保護(hù)分析入門(下)
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉(zhuǎn)載自: