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

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 5007|回復: 67

[VC] [vc]代碼虛擬機設計

  [復制鏈接]
舒默哦 發表于 2020-7-14 18:23
本帖最后由 舒默哦 于 2020-7-14 18:35 編輯




0x00
具體可以參考《加密與解密》第四版 第二十章。這章內容有點抽象,可以畫堆棧圖,來輔助分析。



0x01
代碼虛擬化,我的理解就是把一條指令分解,變形膨脹,本質還是代碼混淆。只要代碼進入虛擬機,就是八仙過海,各顯神通了。
舉個例子:
[Asm] 純文本查看 復制代碼
void _declspec(naked) code_vm_test()
{
    _asm {

        mov eax,10h
        mov ebx,30h
        add eax,ebx
        mov g_num,eax
        retn
    }
}

上面這個函數加上VM,沒加垃圾指令,VM后的代碼如下:
[Asm] 純文本查看 復制代碼
mov eax, 0x0
 shl eax, 0x2
 mov eax, dword ptr ds : [eax + edi] 
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x10
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, dword ptr ss : [esp + 0x4] 
 mov ebx, dword ptr ss : [esp] 
 mov eax, ebx
 add esp, 0x8
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x0
 pop dword ptr ds : [edi + eax * 4]
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x3
 shl eax, 0x2
 mov eax, dword ptr ds : [eax + edi] 
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x30
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, dword ptr ss : [esp + 0x4] 
 mov ebx, dword ptr ss : [esp] 
 mov eax, ebx
 add esp, 0x8
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x3
 pop dword ptr ds : [edi + eax * 4] 
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x0
 shl eax, 0x2
 mov eax, dword ptr ds : [eax + edi] 
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x3
 shl eax, 0x2
 mov eax, dword ptr ds : [eax + edi] 
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, dword ptr ss : [esp + 0x4] 
 mov ebx, dword ptr ss : [esp] 
 add eax, ebx
 add esp, 0x8
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x0
 pop dword ptr ds : [edi + eax * 4] 
 mov eax, filenew.0040A500
 jmp eax
 mov eax, filenew.00406030
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x0
 shl eax, 0x2
 mov eax, dword ptr ds : [eax + edi] 
 push eax
 mov eax, filenew.0040A500
 jmp eax
 mov eax, dword ptr ss : [esp + 0x4]
 mov ebx, dword ptr ss : [esp] 
 mov dword ptr ds : [eax] , ebx
 add esp, 0x8
 mov eax, filenew.0040A500
 jmp eax
 mov eax, 0x0
 mov ebx, dword ptr ss : [ebp] 
 add ebp, eax
 push ebx
 push ebp
 push dword ptr ds : [edi + 0x20]
 push dword ptr ds : [edi + 0x1C]
 push dword ptr ds : [edi + 0x18]
 push dword ptr ds : [edi + 0x14]
 push dword ptr ds : [edi + 0xC]
 push dword ptr ds : [edi + 0x8]
 push dword ptr ds : [edi + 0x4] 
 push dword ptr ds : [edi] 
 pop eax
 pop ecx
 pop edx
 pop ebx
 pop ebp
 pop esi
 pop edi
 popfd
 pop esp
 retn




0x03
編寫流程,可以圍繞三個handler來寫(普通handler、操作數handler、pop目的操作數的handler),普通handler就是指令handler,操作數handler包括目的操作數handler和源操作數handler。
虛擬機并不完善,因為時間原因,只完成了第一步,沒有處理JCC指令、call指令、特殊指令、以及一些改變標志位的指令,以后有時間處理好了我會發布在github上。
測試exe是code_vm_test.exe,源文件:
[Asm] 純文本查看 復制代碼
#include <iostream>
#include <Windows.h>
using namespace std;
int g_num = 0;

void _declspec(naked) code_vm_test()
{
    //MessageBoxA(NULL, 0, 0, 0);
    _asm {

        mov eax,10h
        mov ebx,30h
        add eax,ebx
        mov g_num,eax
        retn
    }
}

int main()
{
    code_vm_test();
    std::cout << g_num << endl;
    system("pause");
}





項目源碼傳上來了,整個加VM的流程放到一個CPP中:
[Asm] 純文本查看 復制代碼
#include <iostream>
#include <list>
#include "vmtest.h"
#include "ASM/disasm.h"
#include "Common/header.h"

//存放wmcode的代碼的鏈表
list<VHandler> g_vHandlerList;

int g_total = 0;//記錄有多少個handler;
int g_retnaddr = 0;//記錄返回地址

//編譯生成的Handler代碼,并添加到Handler
BOOL CompileHandler(char* asmtext)
{
    char Code[CODEMAXLEN] = { 0 };//暫存機器碼
    int codelen = 0;
    char errtext[256] = { 0 };
    t_asmmodel am;
    char linestr[TEXTLEN] = { 0 };
    int len = strlen(asmtext);
    for (int i = 0; i < len;)
    {
        CString str =asmtext;
        int calc = str.LeftFindChar(asmtext+i, '\n');      
        memset(linestr, 0, TEXTLEN);
        memcpy(linestr, asmtext+i, calc - 1);
        Assemble(linestr, 0, &am, 0, 4, errtext);
        memcpy(Code + codelen, am.code, am.length);
        codelen += am.length;
        i += calc;
    }
    VHandler temp_vhandler;
    memcpy(temp_vhandler.AssembleCode, Code, codelen);
    temp_vhandler.CodeLen = codelen;
    
    g_vHandlerList.push_back(temp_vhandler);
}

int GetScalestr(int bit, OUT char* scalestr)
{
    int sizeidx = 0;
    if (bit == 8)
    {
        sizeidx = 0;
        strcpy_s(scalestr, 6, "byte");
    }
    else if (bit == 16)
    {
        sizeidx = 1;
        strcpy_s(scalestr, 6, "word");
    }
    else if (bit == 32)
    {
        sizeidx = 2;
        strcpy_s(scalestr, 6, "dword");
    }
    return sizeidx;
}

//處理普通handler
void GeneralHandler(t_disasm disasm,int tab_index,int opt_num)
{
    
    CString str_general;
    char optnum[32] = { 0 };
    if (opt_num == 2)//雙操作數
    {
        str_general = "mov eax, [esp + 4]\n";
        str_general += "mov ebx, [esp]\n";
        str_general += vmtable[tab_index].strInstruction;
        if (vmtable[tab_index].optype[0]== MEMTYPE)//如果第一個操作數是內存地址
        {           
            int sizeidx = GetScalestr(vmtable[tab_index].bitnum[0], optnum);
            if (0 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [eax],bl\n";
            }
            if (1 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [eax],bx\n";
            }
            if (2 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [eax],ebx\n";
            }
            str_general += "add esp, 8\n";
        }
        else if (vmtable[tab_index].optype[1] == MEMTYPE)//如果第二個操作數是內存地址
        {
            int sizeidx = GetScalestr(vmtable[tab_index].bitnum[0], optnum);
            if (0 == sizeidx)
            {
                str_general = str_general + " bl,"+ optnum+" ptr [eax]\n";
            }
            if (1 == sizeidx)
            {
                str_general = str_general + " bx," + optnum + " ptr [eax]\n";
            }
            if (2 == sizeidx)
            {
                str_general = str_general + " ebx," + optnum + " ptr [eax]\n";
            }
            str_general += "add esp, 8\n";
            str_general += "push ebx\n";
        }
        else
        {
            str_general += " eax,ebx \n"; 
            str_general += "add esp, 8\n";
            str_general += "push eax\n";
        }

    }
    if (opt_num == 1)//單操作數
    {
        str_general = "mov ebx, [esp]\n";
        str_general += vmtable[tab_index].strInstruction;
        if ( vmtable[tab_index].optype[0] == MEMTYPE)
        {
            int sizeidx = GetScalestr(vmtable[tab_index].bitnum[0], optnum);
            if (0 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [ebx]\n";
            }
            if (1 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [ebx]\n";
            }
            if (2 == sizeidx)
            {
                str_general = str_general + " " + optnum + " ptr [ebx]\n";
            }
        }
        else
        {
            str_general += " ebx\n";
        }
       
        str_general += "add esp, 4\n";
        if (vmtable[tab_index].optype[0] == REGTYPE)
        {
            str_general += "push ebx\n";
        }
    }

    if (opt_num == 0)//沒有操作數
    {
        //判斷是不是retn
        if (_stricmp("retn", vmtable[tab_index].strInstruction)==0)
        {
            int retnlength = GetFunSize(vRetn);
            AllocMemory _alloc;
            char* tempbuff = _alloc.auto_malloc<char*>(retnlength);

            memcpy(tempbuff, vRetn, retnlength);

            //修改vRetn的操作數
            *(DWORD*)(tempbuff + 1) = disasm.immconst;

            VHandler temp_vhandler;
            temp_vhandler.CodeLen = retnlength;
            memcpy(temp_vhandler.AssembleCode, tempbuff, retnlength);


            //添加到鏈表
            g_vHandlerList.push_back(temp_vhandler);

            return;
        }
        else
        {
            str_general = vmtable[tab_index].strInstruction;
        }

    }
    str_general += "\nmov eax, 0x66668888\njmp eax\n";

    //編譯生成的Handler代碼,并添加到vHandlerList鏈表
    CompileHandler(str_general.GetString());
}

//pop目的操作數的handler
void PopHandler(t_disasm disasm, int tab_index, int opt_index)
{
    CString str_pop;
    //1、處理輔助handler
    int t1 = vmtable[tab_index].optype[opt_index];
    switch (t1)
    {
    case NONETYPE: //無操作數

        break;
    case IMMTYPE: //立即數
    {

    }

        break;
    case REGTYPE: //寄存器
    {
        unsigned int regnum = disasm.reg[0];
        str_pop = str_pop+ "mov eax," + disasm.reg[0] + "\n";

        if (vmtable[tab_index].bitnum[0] == 8)//操作數為8位
        {
            //這兒判斷高位(ah)還是低位(al)
            if (true == disasm.highbit[0])
            {
                str_pop += "xor ebx,ebx\n";
                str_pop += "mov bx,word ptr [edi+eax]\n";
                str_pop += "and bx, 0xFF00\n";
                str_pop += "mov word ptr[edi + eax], 0\n";
                str_pop += "pop dword ptr [edi+eax*4]\n";
                str_pop += "or dword ptr [edi+eax],ebx\n";
            }
            else
            {
                str_pop += "xor ebx,ebx\n";
                str_pop += "mov bx,word ptr [edi+eax]\n";
                str_pop += "and bx, 0xFF\n";
                str_pop += "mov word ptr[edi + eax], 0\n";
                str_pop += "pop dword ptr [edi+eax*4]\n";
                str_pop += "or dword ptr [edi+eax],ebx\n";
            }
        }
        if (vmtable[tab_index].bitnum[0] == 16)//操作數為16位
        {
            str_pop += "pop word ptr[edi + eax*4]\n";
        }
        if (vmtable[tab_index].bitnum[0] == 32)//操作數為32位
        {
            str_pop += "pop dword ptr[edi + eax*4]\n";
        }
        str_pop += "mov eax, 0x66668888\njmp eax\n";
    }
    break;
    case MEMTYPE: //內存(包括段寄存器)
        //2、判斷是否是段寄存器
        if (vmtable[tab_index].Segment != 0)
        {
           
        }
        else
        {

        }

        break;

    default:
        break;
    }
    //編譯生成的Handler代碼,并添加到vHandlerList鏈表
    CompileHandler(str_pop.GetString());
}

//處理輔助handler
void AssistHandler(t_disasm disasm,int tab_index,int opt_index)
{
    //1、處理輔助handler
    CString str_assist;
    int t1 = vmtable[tab_index].optype[opt_index];
    switch (t1)
    {
    case NONETYPE: //無操作數

        break;
    case IMMTYPE: //立即數
    {
        str_assist = "mov eax,";
        char cstr[56];
        sprintf_s(cstr, 56, "%X", disasm.immconst);//十進制數字轉換為十六進制字符串
        str_assist += cstr;
    }

        break;
    case REGTYPE: //寄存器
    {
        str_assist = "mov eax,";

        str_assist = str_assist +disasm.reg[opt_index]+"\nshl eax,2\n";

        if (vmtable[tab_index].bitnum[opt_index]==8)//操作數為8位
        {
            //這兒判斷高位(ah)還是低位(al)
            if (true == disasm.highbit[opt_index])
            {
                str_assist += "mov ah,byte ptr[edi+eax]";
            }
            else
            {
                str_assist += "mov al,byte ptr[edi+eax]";
            }
        }
        if (vmtable[tab_index].bitnum[opt_index] == 16)//操作數為16位
        {
            str_assist += "mov ax,word ptr[edi+eax]";
        }
        if (vmtable[tab_index].bitnum[opt_index] == 32)//操作數為32位
        {
            str_assist += "mov eax,dword ptr[edi+eax]";
        }

    }
    break;
    case MEMTYPE: //內存(包括段寄存器)
        //2、判斷是否是段寄存器
        if (vmtable[tab_index].Segment != -1)
        {
            //段寄存器不用查看操作數的位數,直接翻譯
            int index = vmtable[tab_index].optype[opt_index];
            const char* reg = vregname[2][index + 8];
            
            //構造輔助handler
            str_assist = "mov eax,";
            int decnumb = disasm.adrconst;
            char cstr[56];       
            sprintf_s(cstr, 56, "%X", decnumb);//十進制數字轉換位十六進制字符串
            str_assist = str_assist + reg + ":["+ cstr +"]";   
        }
        else
        {
            str_assist = "mov eax,";
            int decnumb = disasm.adrconst;
            char cstr[56];
            sprintf_s(cstr, 56, "%X", decnumb);//十進制數字轉換位十六進制字符串
            str_assist = str_assist + cstr;
        }

        break;
       
    default:
        break;
    }

    str_assist += "\npush eax\nmov eax,0x66668888\njmp eax\n";
    printf("%s\n", str_assist.GetString());

    //編譯生成的Handler代碼,并添加到vHandlerList鏈表
    CompileHandler(str_assist.GetString());
}

void testvmp(char* TargetCode,_Out_ int &CodeLength)
{
    /*char TargetCode[] = {
        0xC2,0x08,0x00,
         0xFE ,0x05  ,0x00  ,0x10  ,0x40  ,0x00,
        0xBB,0x20,0x00,0x00,0x00,
        0x8A,0x24,0x24,
        0x8B,0xC3,
        0x64 ,0xA1 ,0x30 ,0x00 ,0x00 ,0x00,
        0x66, 0xA1 ,0x00 ,0x10 ,0x40 ,0x00
    };*/
    t_disasm disasm;
    Disasm(TargetCode, 20, (ulong)TargetCode, &disasm, 3);
    CodeLength = disasm.codelen;
    int index = -1;

    for (int i = 0; i < VMTABLEMAXLEN; i++)
    {   
        if (0 == strcmp(vmtable[i].VMInstrName, disasm.vm_name))
        {
            printf("%s\n", vmtable[i].strInstruction);
            if (_stricmp(vmtable[i].strInstruction,"retn")==0)
            {
                GeneralHandler(disasm, i, 0);
                return;
            }
            index = i;
            break;
        }        
    }
    if (-1 != index)
    {
        //1、構造輔助handler
        
        if (-1 != vmtable[index].optype[0])//目的操作數
        {
            AssistHandler(disasm, index, 0);
        }
       
        if (-1 != vmtable[index].optype[1])//源操作數
        {
            AssistHandler(disasm, index, 1);
        }

        //2、普通handler處理
        int opt_num = (vmtable[index].optype[0] ? 1 : 0) + (vmtable[index].optype[1] ? 1 : 0);
        GeneralHandler(disasm,index, opt_num);

        //3、pop到目的操作數
        if (opt_num)//如果有目的操作數才處理
        {
            PopHandler(disasm, index, 0);
        }
       
    }
}

#define VMSTART (0x2000)    //.vmp1節的前0x2000個字節留給VStartVM函數和其他需要處理的東西
#define VMTABLE (0x1000)    //.vmp1節的0x1000地址處存放跳轉表
#define VMDISPATCH (0x500) //.vmp1節的0x500地址處存放調度器
int g_handleraddr_calc = 0;//記錄handler存放到哪里了
int g_table_calc = 0;//記錄跳轉表存放到哪里了
//處理g_vHandlerList鏈表
void SolutionWmcode(char* filebuff,PEInfo peinfo)
{
    PE pe;
    ULONG_PTR ulSizeOfImage =peinfo.ImageBase + pe.AlignSize(peinfo.SizeofImage, peinfo.SectionAlignment);//.vmp1節的開始地址
    ULONG_PTR vHandlerAddr = (ULONG_PTR)(filebuff + VMSTART+ g_handleraddr_calc);//handler存放的地址
    ULONG_PTR vJmpTable = (ULONG_PTR)(filebuff + VMTABLE+ g_table_calc);  //跳轉表
    ULONG_PTR vMmDispatcher = ulSizeOfImage + VMDISPATCH;//調度器地址
    list<VHandler>::iterator iter = g_vHandlerList.begin();
    for (int i = 0; i < g_vHandlerList.size(); i++)
    {
        int codelength = (*iter).CodeLen;
        char* assemblecode = (*iter).AssembleCode;
 
        //修復handler跳轉到調度器的地址
        *(DWORD*)(assemblecode + codelength - 6) = vMmDispatcher;

        //把handler的地址存入跳轉表
        *(WORD*)vJmpTable = g_handleraddr_calc;

        //存入handler代碼
        memcpy((char*)vHandlerAddr, assemblecode, codelength);

        vHandlerAddr += codelength;
        vJmpTable += 2;
        g_table_calc += 2;
        g_handleraddr_calc += codelength;
        ++iter;
        ++g_total;//記錄handler的個數
    }
    g_vHandlerList.clear();
}


#define path_in "C:\\Users\\86188\\Desktop\\code_vm_test.exe"
#define path_out "C:\\Users\\86188\\Desktop\\加了VM的code_vm_test.exe"
//加載文件,加vm
void LoadFile(char* path)
{
    AllocMemory _alloc;
    PEInfo peinfo;
    PE pe;
    pe.GetPEInformation_(path, &peinfo);

    //去掉隨機基址
    peinfo.OptionalHeader->DllCharacteristics = 0;

    //拉伸文件
    ULONG_PTR StretchAddr = pe.StretchFile(peinfo.FileBuffer, peinfo.SizeofImage);
    //更新peinfo信息
    pe.GetPEInformation_1((char*)StretchAddr, &peinfo, peinfo.FileSize);

    //申請內存
    int newsectionsize = 0x1000 * 20;//.vmp1所在節的大小,設置為80k
    char* Vmp1_Buff = _alloc.auto_malloc<char*>(newsectionsize);

    //選擇要vm的起始和結束地址
    ULONG_PTR StartAddr = 0x401110 - peinfo.ImageBase + StretchAddr;
    ULONG_PTR EndAddr = 0x401122 - peinfo.ImageBase + StretchAddr;
    int CodeLength = 0;
    int TotelLength = EndAddr - StartAddr;
    for (int i = 0; i < TotelLength;)
    {
        testvmp((char*)StartAddr+i, CodeLength);//把code轉換為WMcode
        SolutionWmcode(Vmp1_Buff, peinfo);//處理g_vHandlerList鏈表
        i += CodeLength;
    }
 
//#define VMSTART (0x2000)    //.vmp1節的前0x2000個字節留給VStartVM函數和其他需要處理的東西
//#define VMTABLE (0x1000)    //.vmp1節的0x1000地址處存放跳轉表
//#define VMDISPATCH (0x500) //.vmp1節的0x500地址處存放調度器

    ULONG_PTR ulSizeOfImage = peinfo.ImageBase + pe.AlignSize(peinfo.SizeofImage, peinfo.SectionAlignment);//.vmp1節的開始地址

    ULONG_PTR ulDispatcherAddr = (ULONG_PTR)Vmp1_Buff + VMDISPATCH;

    //填充VStartVM
    int start_len = GetFunSize(VStartVM);
    memcpy(Vmp1_Buff, VStartVM, start_len);
    //修復VMstartVM里的數據
    *(ULONG_PTR*)(Vmp1_Buff + start_len - 6) = ulSizeOfImage + VMDISPATCH;
    *(ULONG_PTR*)(Vmp1_Buff + start_len - 11) = ulSizeOfImage + VMTABLE;

    //填充調度器
    int dispatcher_len = GetFunSize(VMDispatcher);
    memcpy(Vmp1_Buff+ VMDISPATCH, VMDispatcher, dispatcher_len);
    //修復調度器里數據
    *(ULONG_PTR*)(Vmp1_Buff + VMDISPATCH + 1) = ulSizeOfImage + VMSTART;

    //抹掉目標文件里加了vm的代碼
    for (int i = 0; i < TotelLength; i++)
    {
        *(char*)(StartAddr + i) = 0x90;
    }

    //在目標文件添加跳轉地址 
    int jump_len = GetFunSize(JumpToVmp);
    memcpy((char*)StartAddr, JumpToVmp, jump_len);
    //修復數據
    *(ULONG_PTR*)(StartAddr + 1) = ulSizeOfImage;

    //還原成源文件大小
    char* filebuff = pe.ImageBuff_To_FileBuff((char*)StretchAddr, peinfo.FileSize);

    //添加新節
    pe.addSeciton((ULONG_PTR)filebuff, newsectionsize, (char*)".vmp1");
 
    //申請內存,合并節
    char* NewFileBuff = _alloc.auto_malloc<char*>(newsectionsize + peinfo.FileSize);
    memcpy(NewFileBuff, filebuff, peinfo.FileSize);
    memcpy(NewFileBuff + peinfo.FileSize, Vmp1_Buff, newsectionsize);

    //保存文件
    FileOperation fileopt;
    fileopt.SaveFile_(NewFileBuff, peinfo.FileSize + newsectionsize, (char*)path_out);
}

//主函數
int main()
{
    //加載文件
    LoadFile((char*)path_in);

    std::cout << "Hello World!\n";
    system("pause");
}

代碼虛擬機設計.7z

500彩票邀请码1.06 MB, 下載次數: 86, 下載積分: 吾愛幣 -1 CB

免費評分

參與人數 29威望 +2 吾愛幣 +125 熱心值 +27 收起 理由
陽光下的少年 + 1 很優秀
不愿鞠躬車馬前 + 1 + 1 謝謝@Thanks!學到了!
wmslecz + 1 + 1 謝謝@Thanks!
Suk-Lees + 1 + 1 我很贊同!
kuletco + 1 謝謝@Thanks!
hjj134 + 1 + 1 謝謝@Thanks!
Conngas + 1 + 1 夠硬核,我喜歡
Earrow + 1 + 1 很厲害的樣子,俺得好好學習了
快樂的小星豬 + 1 謝謝@Thanks!
劉罐罐 + 1 我很贊同!
whoisboss + 1 + 1 熱心回復!
liuguang123456 + 1 + 1 我很贊同!
zctsir + 1 謝謝@Thanks!
woyucheng + 1 + 1 我很贊同!
luochunyan + 1 + 1 謝謝@Thanks!
石碎大胸口 + 1 + 1 謝謝@Thanks!
煩煩煩 + 1 + 1 謝謝@Thanks!
fengbolee + 1 + 1 用心討論,共獲提升!
gaosld + 1 + 1 用心討論,共獲提升!
獨行風云 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
qq3bot + 1 + 1 謝謝@Thanks!
shenjiyuan2hao + 1 + 1 熱心回復!
Lixinist + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
莫流云 + 1 + 1 用心討論,共獲提升!
xhp1976 + 1 + 1 用心討論,共獲提升!
yixi + 1 + 1 謝謝@Thanks!
dongzi0712 + 1 + 1 用心討論,共獲提升!
蘇紫方璇 + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
Ravey + 1 + 1 謝謝@Thanks!

查看全部評分

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

renny 發表于 2020-7-15 19:50
學習了,很深奧
andersgong 發表于 2020-8-5 08:25
fancotse 發表于 2020-8-5 08:56
prontosil 發表于 2020-8-3 18:31
贊一個,原來可以這么設計虛擬機
親愛的靳萌萌 發表于 2020-8-3 13:38
學到了 樓主厲害  感謝分享
不凡_不懂 發表于 2020-8-1 09:38
學到了學到了,樓主牛逼
wori 發表于 2020-8-2 08:50
不懂學習中
coder014 發表于 2020-8-1 09:47
樓主厲害 學習了
xueli 發表于 2020-7-31 10:22
謝謝分享
李斯隆 發表于 2020-7-30 22:07
好好學習!。
chboy 發表于 2020-7-14 19:33
擔心違規 內容已刪除
Mirana 發表于 2020-7-14 20:19
支持一下
 樓主| 舒默哦 發表于 2020-7-14 20:50
chboy 發表于 2020-7-14 19:33
擔心違規 內容已刪除

要不得老鐵
二娃 發表于 2020-7-14 22:22
我這幾天剛好也在寫這東西
頭都裂開了
 樓主| 舒默哦 發表于 2020-7-14 23:15
二娃 發表于 2020-7-14 22:22
我這幾天剛好也在寫這東西
頭都裂開了

思路理清就很容易了,就那幾個handler,細節有問題,可以邊調試邊改&#129300;
二娃 發表于 2020-7-15 00:40
舒默哦 發表于 2020-7-14 23:15
思路理清就很容易了,就那幾個handler,細節有問題,可以邊調試邊改&#129300;

要考慮的東西挺多的
chboy 發表于 2020-7-15 09:11

我準備說你一發帖就置頂了有點牛逼 但是發出后擔心違規就刪除了
caicaiwuguo 發表于 2020-7-15 12:42
高深莫測,云里霧里
Lixinist 發表于 2020-7-15 15:11
我在寫代碼變異,比vm好寫點

免費評分

參與人數 1吾愛幣 +1 熱心值 +1 收起 理由
馬廣順 + 1 + 1 我很贊同!

查看全部評分

您需要登錄后才可以回帖 登錄 | 注冊[Register]

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

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

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

GMT+8, 2020-8-7 04:41

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表

500彩票邀請碼-彩經網