吾愛破解 - LCG - LSG |安卓破解|病毒分析|001aa.com

 找回密碼
 注冊[Register]

QQ登錄

时时彩平台只需一步,快速開始

搜索
查看: 2415|回復: 16

[原創] 如何將VM的opcode嵌入匯編源碼中

  [復制鏈接]
樓主
Rimao 發表于 2020-5-12 13:59 回帖獎勵
本帖最后由 Rimao 于 2020-5-12 17:43 編輯

這次來一個關于VM的混淆辦法,可能只是個小trick,僅僅來自胡思亂想
也許你會覺得很奇怪,一個VM能有啥新鮮的,對,單純來說VM保護源代碼已經非常的成熟了,所以在這里只做最基本的介紹,而且這次的重點不在于VM。
VM就是Virtual Machine(虛擬機器),這里和vmware不是一樣的,這里指的是自己寫的引擎和opcode類型,比如Java就是基于JVM的語言,通過將代碼編譯成字節碼,再運行在JVM上。
所以我們對于一個加密算法可以先翻譯成opcode,再運行在我們自己寫的引擎上,這樣就大大增加逆向成本。
                上圖就是一個典型的VM執行引擎結構圖(有點像控制流平坦化 ).....
再來個源碼樣式:

那么介紹完了VM,我們了解到VM保護的逆向一般都得先找到opcode,再分析引擎,再寫parser,再人肉看,我們從后三個方向都容易讓人惡心,這里介紹一種從Opcode這里惡心人的操作。
我們知道CTF的VM一般將opcode單獨放到一個字節數組里,有一個指針慢慢的掃描過去來進行解釋執行,完全是虛擬化的環境,但是有沒有想過,這里就導致了opcode極其容易dump出來,要是能夠讓opcode和匯編指令混在一起就好了
所以這里說的就是如何將Opcode和匯編指令放在一起,混雜著執行就很惡心了。
首先,你的靜態分析會炸掉,因為混雜了不可反匯編的指令,可能會因為上下文使得這一塊的代碼和數據類型區分不開甚至指令分析錯誤,F5鐵定是廢了,再者,Opcode提取比較困難,你可能得手工提取那些穿插著Opcode。
其實還有一種好處,涉及到原理才能知道為什么了。
這里我們使用windows下的環境,使用的是windows 強大的調試API,具體的實現邏輯是:運行時碰到非法指令,會觸發異常,通過調試器調試子進程截獲異常,獲取上下文進行操作
所以我們得先創造子進程,子進程被調試,本身則相當于系統處理不了的字節碼的解釋器(有點類似linux下基于信號的VM,大佬敏銳的見解)

        STARTUPINFO si;
        memset(&si,0,sizeof(si));
        si.cb=sizeof(si);
        PROCESS_INFORMATION pi;
        HMODULE h=GetModuleHandleW(0);
        CHAR Filename[260];
        GetModuleFileNameA(h,(LPSTR)&Filename,260);
        BOOL result=CreateProcessA(NULL,(LPSTR)&Filename,NULL,NULL,FALSE,DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,NULL,NULL,(LPSTARTUPINFOA)&si,&pi);

這里就是創建子進程的代碼,而且是以調試模式啟動創建,所以會擁有許多權限。
這里實現VM最重要的API就是SetThreadContext,因為可以實現寄存器等的獲取與修改,還有一個就是ReadProcessMemory和WriteProcessMemory,因為需要對內存進行讀寫。
最后再來個while(true)里面隨時截獲異常消息進行處理,處理完之后再SetThreadContext將目前執行完的寄存器的值設置。
时时彩平台 還有一點細節,就是寫個IsDebuggerPresent來判斷自己是被調試的還是父進程,切勿陷入死循環,瘋狂CreateProcess,那CPU直接暴斃。

void CreateSubProcess()
{
        STARTUPINFO si;
        memset(&si,0,sizeof(si));
        si.cb=sizeof(si);
        PROCESS_INFORMATION pi;
        HMODULE h=GetModuleHandleW(0);
        CHAR Filename[260];
        GetModuleFileNameA(h,(LPSTR)&Filename,260);
        BOOL result=CreateProcessA(NULL,(LPSTR)&Filename,NULL,NULL,FALSE,DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,NULL,NULL,(LPSTARTUPINFOA)&si,&pi);
        VMState vms;
        if(result)
        {
                //ShowWindow(GetConsoleWindow(),SW_HIDE);
                DEBUG_EVENT de;
                while(WaitForDebugEvent(&de,INFINITE)!=0)
                {
                        if(de.dwDebugEventCode==EXCEPTION_DEBUG_EVENT)
                        {
                                EXCEPTION_DEBUG_INFO di=de.u.Exception;
                                if(di.ExceptionRecord.ExceptionCode==EXCEPTION_ILLEGAL_INSTRUCTION)
                                {
                                        CONTEXT context;
                                        memset(&context,0,sizeof(context));
                                        context.ContextFlags=CONTEXT_FULL;
                                        GetThreadContext(pi.hThread,&context);
                                        if(ReadRemoteByte(pi.hProcess,context.Eip)==0xC7)
                                        {
                                                vms.context=&context;
                                                vms.pi=π
                                                RunVM(vms);
                                                SetThreadContext(pi.hThread,&context);
                                        }
                                        else
                                        {
                                                TerminateProcess(pi.hProcess,0);
                                                exit(0);
                                        }

                                }
                        }
                        if(de.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT)
                                exit(0);
                        ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE);
                }
        }
        return;
}

所以主要代碼就在這里了,然后完善下RunVM函數就可以了。如下就是完整代碼,有個Opcode.h沒給出不過問題不大,我們不在于寫VM而在于怎么實現opcode和asm混雜
還有一個重要細節,就是每個VM的opcode之前必須要有個前綴,而且這個前綴必須不能對應任何的匯編指令,不然就不會觸發非法指令的異常,最后的EIP設置也記得要跳過最開頭這個字節,在代碼中我選取的是0xC7

// SEHVM.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
//

#include<cstdio>
#include<cstdlib>
#include<windows.h>
#include"Opcodes.h"
using namespace std;
struct VMState
{
        PCONTEXT context;
        PPROCESS_INFORMATION pi;
};
BYTE ReadRemoteByte(HANDLE hProcess,int addr)
{
        BYTE data;
        ReadProcessMemory(hProcess,(LPVOID)addr,&data,1,NULL);
        return data;
}
DWORD ReadRemoteDword(HANDLE hProcess,int addr)
{
        int data;
        ReadProcessMemory(hProcess,(LPVOID)addr,&data,4,NULL);
        return data;
}
void WriteRemoteDword(HANDLE hProcess,int addr,DWORD data)
{
        WriteProcessMemory(hProcess,(LPVOID)addr,(LPVOID)&data,4,NULL);
}
void pop(VMState vms,DWORD *val)
{
        *val=ReadRemoteDword(vms.pi->hProcess,vms.context->Esp);
        vms.context->Esp+=4;
}
void push(VMState vms,int val)
{
        vms.context->Esp-=4;
        WriteRemoteDword(vms.pi->hProcess,vms.context->Esp,val);
}
DWORD *getReg(VMState vms,BYTE x)
{
        if(x==Reg1)
                return &vms.context->Eax;
        if(x==Reg2)
                return &vms.context->Ebx;
        if(x==Flag)
                return &vms.context->Ecx;
        if(x==LoopReg)
                return &vms.context->Edx;
        printf("Unknow reg id: 0x%X\n",x);
        exit(0);
}
void RunVM(VMState vms)
{
        DWORD *eip=&(vms.context->Eip);
        BYTE op=ReadRemoteByte(vms.pi->hProcess,*eip+1);
        int arg1;
        BYTE arg2;
        switch(op)
        {
                case PushImm:
                        arg1=ReadRemoteDword(vms.pi->hProcess,*eip+2);
                        push(vms,arg1);
                        vms.context->Eip+=6;
                        break;
                case PopReg:
                        arg2=ReadRemoteByte(vms.pi->hProcess,*eip+2);
                        pop(vms,getReg(vms,arg2));
                        vms.context->Eip+=3;
                        break;
                default:
                        TerminateProcess(vms.pi->hProcess,0);
                        exit(0);
                        break;
        }
}
void CreateSubProcess()
{
        STARTUPINFO si;
        memset(&si,0,sizeof(si));
        si.cb=sizeof(si);
        PROCESS_INFORMATION pi;
        HMODULE h=GetModuleHandleW(0);
        CHAR Filename[260];
        GetModuleFileNameA(h,(LPSTR)&Filename,260);
        BOOL result=CreateProcessA(NULL,(LPSTR)&Filename,NULL,NULL,FALSE,DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS,NULL,NULL,(LPSTARTUPINFOA)&si,&pi);
        VMState vms;
        if(result)
        {
                //ShowWindow(GetConsoleWindow(),SW_HIDE);
                DEBUG_EVENT de;
                while(WaitForDebugEvent(&de,INFINITE)!=0)
                {
                        if(de.dwDebugEventCode==EXCEPTION_DEBUG_EVENT)
                        {
                                EXCEPTION_DEBUG_INFO di=de.u.Exception;
                                if(di.ExceptionRecord.ExceptionCode==EXCEPTION_ILLEGAL_INSTRUCTION)
                                {
                                        CONTEXT context;
                                        memset(&context,0,sizeof(context));
                                        context.ContextFlags=CONTEXT_FULL;
                                        GetThreadContext(pi.hThread,&context);
                                        if(ReadRemoteByte(pi.hProcess,context.Eip)==0xC7)
                                        {
                                                vms.context=&context;
                                                vms.pi=π
                                                RunVM(vms);
                                                SetThreadContext(pi.hThread,&context);
                                        }
                                        else
                                        {
                                                TerminateProcess(pi.hProcess,0);
                                                exit(0);
                                        }

                                }
                        }
                        if(de.dwDebugEventCode==EXIT_PROCESS_DEBUG_EVENT)
                                exit(0);
                        ContinueDebugEvent(de.dwProcessId,de.dwThreadId,DBG_CONTINUE);
                }
        }
        return;
}
int main()
{
        if(!IsDebuggerPresent())
        {
                CreateSubProcess();
                return 0;
        }
        printf("I am sub process\n");
        int t=0;
        _asm
        {
                _emit 0xC7 //pushImm 0xDDCCBBAA
                _emit 0x17
                _emit 0xAA
                _emit 0xBB
                _emit 0xCC
                _emit 0xDD
                _emit 0xC7 //pushImm 0xEECCBBAA
                _emit 0x17
                _emit 0xAA
                _emit 0xBB
                _emit 0xCC
                _emit 0xEE
                pop eax
                pop ebx
                mov t,eax
        }
        printf("the value of eax: %X\n",t); //eax=0xEECCBBAA
        MessageBoxA(NULL,"GOT","YES",MB_OK);
        return 0;
}

這里的VM只實現了最簡單的兩條指令,所以等著大佬們完善,這里主要是闡述一種操作,只是個雛形罷了。
最后再說一下在反調試處的作用,由于程序分為了調試進程與非調試進程,那么當你用IDA等調試器執行時則會使得程序誤認為自己為子進程。
就會開始按正常操作執行代碼,當執行到那個無法識別的指令時,則會觸發異常,傳遞給ida,然而ida并不是flag父進程,不知道如何處理這些opcode
时时彩平台 所以則會執行失敗,使得做題人無法理解發生了啥,所以一定要將創建子進程和VM處理代碼藏起了,才可以發揮最大效果

免費評分

參與人數 8威望 +2 吾愛幣 +108 熱心值 +8 收起 理由
reread + 1 + 1 用心討論,共獲提升!
snatch2null + 1 + 1 tql
gaosld + 1 + 1 我很贊同!
二娃 + 2 + 1 我很贊同!
獨行風云 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
Hmily + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
女蘿巖 + 1 + 1 我很贊同!
klise + 1 + 1 用心討論,共獲提升!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
solly 發表于 2020-5-13 08:45
這個可以把vm_opcode當作花指令混在代碼中。。。。再混上一些真正的花指令。。。。當達到一定規模后,就很難人工處理了,自動花指令處理也很難區分真假花指令了。
推薦
YenKoc 發表于 2020-5-12 15:14
推薦
赤座燈里 發表于 2020-5-12 17:29
本帖最后由 赤座燈里 于 2020-5-12 17:54 編輯

不錯的思路,利用調試器捕捉異常。其實很多cm都用到了異常處理,不過這種為了防dump opcode的挺少見的
4#
斬風 發表于 2020-5-12 19:19
路過學習下,
6#
 樓主| Rimao 發表于 2020-5-13 09:50 |樓主
solly 發表于 2020-5-13 08:45
這個可以把vm_opcode當作花指令混在代碼中。。。。再混上一些真正的花指令。。。。當達到一定規模后,就很 ...

大佬說得對, 或者是實現多種樣式的opcode,一起混雜在一起分析難度大增
7#
龐曉曉 發表于 2020-5-13 11:35
我想起tp,就是故意拋異常。如果被處理了就說你有非法工具
8#
斬風 發表于 2020-5-13 17:16
路過學習了
9#
斬風 發表于 2020-5-14 17:40
路過學習下
10#
cocjsuyu 發表于 2020-5-14 20:53
看不懂,不過感謝大神分享
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

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

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( )

GMT+8, 2020-5-25 05:11

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表
时时彩平台官网-欢迎您 时时彩平台注册-爱问知识人 时时彩平台app-互动百科 时时彩平台投注-百科词条 超级快三-搜霸天下 时时彩平台邀请码-即可搜索 时时彩平台开户-新浪爱彩 彩神大发快三-一定牛 彩神大发快三官网-360云盘 彩神大发快三注册-百度耨米