吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 27498|回复: 74
收起左侧

[系统底层] DLL巧妙的绕过被VMP壳HOOK的ZwProtectVirtualMemory

    [复制链接]
你与明日 发表于 2020-4-17 21:25
本帖最后由 你与明日 于 2020-4-17 21:37 编辑

被VMP HOOK的ZwProtectVirtualMemory介绍

ZwProtectVirtualMemory,是一个修改内存输入的API函数,VirtualProtect和VirtualProtectEx修改内存属性都会通过ZwProtectVirtualMemory这个API函数.

在VMP壳的程序中,注入DLL进行一定的内存修改,但会发现,对WINAPI或者程序的代码段等进行WriteProcessMemory操作,会发现修改错误,错误提示是没有修改权限.

而WriteProcessMemory在调用更底层的ZwWriteVirtualMemory前会先调用ZwProtectVirtualMemory修改原有程序的内存属性.

但VMP HOOK了ZwProtectVirtualMemory,就导致了无法随意修改内存属性

778D0068     E9 A2FFA088     jmp 002E000F                             ; 被HOOK的ZwProtectVirtualMemory
778D006D     33C9            xor ecx,ecx
778D006F     8D5424 04       lea edx,dword ptr ss:[esp+0x4]
778D0073     64:FF15 C000000>call dword ptr fs:[0xC0]
778D007A     83C4 04         add esp,0x4
778D007D     C2 1400         retn 0x14
778D0068     B8 4D000000     mov eax,0x4D                         ; 没被HOOK前的ZwProtectVirtualMemory
778D006D     33C9            xor ecx,ecx
778D006F     8D5424 04       lea edx,dword ptr ss:[esp+0x4]
778D0073     64:FF15 C000000>call dword ptr fs:[0xC0]                
778D007A     83C4 04         add esp,0x4
778D007D     C2 1400         retn 0x14

从上面的代码可以看出,被修改的5字节是SSDT操作码,我们需要精准的知道原操作码是多少,但在WIN7下ZwProtectVirtualMemory的操作码是0x4D,而WIN10以及XP,WIN8又是不同的操作码,怎么样可以准确的获取到当前系统的操作码,并且在不修改HOOK的前提下调用ZwProtectVirtualMemory修改内存属性就是本贴的主题.

思路

首先想获取操作码有很多方法.
1.写驱动在驱动层获取SSDT操作码.
首先驱动就不写了.......复杂...而且x64驱动没签名...放弃....
2.读取其他进程的前5字节.
从其他进程读前5字节是可以考虑的,但电脑安装了某些安全软件,他们的防护逻辑是全局DLL注入HOOK....所以你读出来的也可能是被HOOK后的opcode........这个方法也不考虑....放弃
3.从ntdll.dll文件内获取操作码.
这个方法相对而言是最好的...因为你电脑的ntdll.dll被修改的机率还是非常非常低的(反正我没见过),就这个方法了吧,盘他.

详解

1.获取文件数据

首先把ntdll.dll读入到内存中,然后才可以进行下一步操作,直接贴代码好了,没什么难度

        HANDLE hFile = CreateFile(_T("C:\\Windows\\System32\\ntdll.dll"), GENERIC_READ, FILE_SHARE_READ, NULL,
                                                   OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
                MessageBox(0, _T("CreateFileError"), _T("error"), MB_OK);
                return;
        }
        SIZE_T ntdllAddr = (SIZE_T)LoadLibrary(_T("ntdll.dll"));
        SIZE_T apiAddr = (SIZE_T)GetProcAddress((HMODULE)ntdllAddr, "ZwProtectVirtualMemory");
        if (apiAddr == NULL || ntdllAddr == NULL)
        {
                MessageBox(0, _T("LoadLibrary|GetProcAddress,Error"), _T("error"), MB_OK);
                CloseHandle(hFile);
                return;
        }

        //把Ntdll读入进内存
        DWORD dwHighSize = 0;
        DWORD dwLowSize = GetFileSize(hFile, &dwHighSize);
        BYTE* pBuff = new BYTE[dwLowSize + dwHighSize]();
        DWORD  dwFileSize = 0;
        ReadFile(hFile, pBuff, dwLowSize + dwHighSize, &dwFileSize, NULL);

2.获取ZwProtectVirtualMemory在文件中的位置

ZwProtectVirtualMemory地址 - ntdll地址 = RVA

先通过RVA找到所在区段,再用RVA - 当前区段RVA + 当前区段文件偏移 = F0A

最后用F0A找到相对的opcode

//寻找所在区段
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
        PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + ((SIZE_T)pDosHeader));
        PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((SIZE_T)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS32));
        SIZE_T Deviation = 0;
        //ZwProtectVirtualMemory 地址 - ntdll地址 = RVA
        SIZE_T Rva = apiAddr - ntdllAddr;

        // 先通过RVA找到所在区段,再用RVA-当前区段RVA+当前区段文件偏移 = F0A
        // 最后用F0A找到相对的opcode,复制到新内存执行再跳转
        for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
        {
                if (Rva > pSectionHeader[i].VirtualAddress && Rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData)
                {//通过RVA找到所在区段,再用RVA-当前区段RVA+文件偏移 = F0A
                        Deviation = Rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
                        break;
                }
        }

        if (Deviation == 0)
        {
                MessageBoxA(0, "计算段内偏移错误", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }

3.绕过HOOK

得到函数在文件内的位置后,可以获取原opcode进行调用,然后再跳转回没被HOOK的位置,还是来个图片好解释
...应该

        // 最后用段内偏移找到相对的opcode
        g_pAddr = (BYTE*)VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//申请一段堆空间执行被修改的汇编代码
        if (g_pAddr == NULL)
        {
                MessageBoxA(0, "VirtualAlloc Error", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }
        memcpy(g_pAddr, pBuff + Deviation, 5);//x32下VMP修改了前5字节,把文件内的原5字节拷贝过去
        g_pAddr[5] = 0x68;//push
        apiAddr += 5;
        memcpy(g_pAddr + 6, &apiAddr, 4);//push的地址是ZwProtectVirtualMemory+5的地方
        g_pAddr[10] = 0xc3;//使用retn过去
        delete[] pBuff;
        CloseHandle(hFile);

代码

上面的代码都是一个函数被拆分上传了.x64的也有,和x32不同的地方就只有x64不能直接push太长的地址,所以用了下面的方式来跳转

        mov rax ,地址   48 b8 00 00 00 00 00 00 00 00   len == 9
        push rax        50                              len == 10
        mov r10,rcx
        mov eax,操作码
        retn

下面是x32和x64完整的代码

#define DefineFuncPtr(name,base) decltype(name) *My_##name = (decltype(name)*)base
#include "windows.h"
#include "tchar.h"
BYTE* g_pAddr = NULL;//ZwProtectVirtualMemory的调用函数
BYTE* g_pHookAddr = NULL;

typedef SIZE_T(CALLBACK* PZwProtectVirtualMemory)(
        HANDLE ProcessHandle,
        PVOID* BaseAddress,
        SIZE_T* NumberOfBytesToProtect,
        ULONG NewAccessProtection,
        PULONG OldAccessProtection);
PZwProtectVirtualMemory pZwProtectVirtualMemory;

void Init64()
{
        if (g_pAddr != NULL)
        {
                return;
        }
        HANDLE hFile = CreateFile(_T("C:\\Windows\\System32\\ntdll.dll"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
                MessageBox(0, _T("CreateFileError"), _T("error"), MB_OK);
                return;
        }
        SIZE_T ntdllAddr = (SIZE_T)LoadLibrary(_T("ntdll.dll"));
        SIZE_T apiAddr = (SIZE_T)GetProcAddress((HMODULE)ntdllAddr, "ZwProtectVirtualMemory");
        if (apiAddr == NULL || ntdllAddr == NULL)
        {
                MessageBox(0, _T("LoadLibrary|GetProcAddress,Error"), _T("error"), MB_OK);
                CloseHandle(hFile);
                return;
        }

        //把Ntdll读入进内存
        DWORD dwHighSize = 0;
        DWORD dwLowSize = GetFileSize(hFile, &dwHighSize);
        BYTE* pBuff = new BYTE[dwLowSize + dwHighSize]();
        DWORD  dwFileSize = 0;
        ReadFile(hFile, pBuff, dwLowSize + dwHighSize, &dwFileSize, NULL);

        //寻找所在区段
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
        PIMAGE_NT_HEADERS64 pNtHeader = (PIMAGE_NT_HEADERS64)(pDosHeader->e_lfanew + ((SIZE_T)pDosHeader));
        PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((SIZE_T)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS64));
        SIZE_T Deviation = 0;
        //ZwProtectVirtualMemory 地址 - ntdll地址 = RVA
        SIZE_T Rva = apiAddr - ntdllAddr;
        // 先通过RVA找到所在区段,再用RVA-当前区段RVA+当前区段文件偏移 = F0A
        // 最后用F0A找到相对的opcode,复制到新内存执行再跳转
        for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
        {
                if (Rva > pSectionHeader[i].VirtualAddress && Rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData)
                {//通过RVA找到所在区段,再用RVA-当前区段RVA+文件偏移 = F0A
                        Deviation = Rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
                        break;
                }
        }

        if (Deviation == 0)
        {
                MessageBoxA(0, "计算段内偏移错误", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }
        // 最后用段内偏移找到相对的opcode
        g_pAddr = (BYTE*)VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//申请一段堆空间执行被修改的汇编代码
        if (g_pAddr == NULL)
        {
                MessageBoxA(0, "VirtualAlloc Error", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }
        /*
        mov rax ,地址   48 b8 00 00 00 00 00 00 00 00   len == 9
        push rax        50                              len == 10
        mov r10,rcx
        mov eax,操作码
        retn
        */
        g_pAddr[0] = 0x48;
        g_pAddr[1] = 0xb8;
        apiAddr += 8;
        memcpy(g_pAddr + 2, &apiAddr, 8);//地址是ZwProtectVirtualMemory+7

        g_pAddr[10] = 0x50;//push rax

        memcpy(g_pAddr + 11, pBuff + Deviation, 8);//x64下VMP修改了前8字节,把文件内的原8字节拷贝过去

        g_pAddr[19] = 0xc3;//retn过去

        delete[] pBuff;
        CloseHandle(hFile);
}
void Init32()
{
        if (g_pAddr != NULL)
        {
                return;
        }
        HANDLE hFile = CreateFile(_T("C:\\Windows\\System32\\ntdll.dll"), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
                MessageBox(0, _T("CreateFileError"), _T("error"), MB_OK);
                return;
        }
        SIZE_T ntdllAddr = (SIZE_T)LoadLibrary(_T("ntdll.dll"));
        SIZE_T apiAddr = (SIZE_T)GetProcAddress((HMODULE)ntdllAddr, "ZwProtectVirtualMemory");
        if (apiAddr == NULL || ntdllAddr == NULL)
        {
                MessageBox(0, _T("LoadLibrary|GetProcAddress,Error"), _T("error"), MB_OK);
                CloseHandle(hFile);
                return;
        }

        //把Ntdll读入进内存
        DWORD dwHighSize = 0;
        DWORD dwLowSize = GetFileSize(hFile, &dwHighSize);
        BYTE* pBuff = new BYTE[dwLowSize + dwHighSize]();
        DWORD  dwFileSize = 0;
        ReadFile(hFile, pBuff, dwLowSize + dwHighSize, &dwFileSize, NULL);

        //寻找所在区段
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pBuff;
        PIMAGE_NT_HEADERS32 pNtHeader = (PIMAGE_NT_HEADERS32)(pDosHeader->e_lfanew + ((SIZE_T)pDosHeader));
        PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(((SIZE_T)(pNtHeader)) + sizeof(IMAGE_NT_HEADERS32));
        SIZE_T Deviation = 0;
        //ZwProtectVirtualMemory 地址 - ntdll地址 = RVA
        SIZE_T Rva = apiAddr - ntdllAddr;

        // 先通过RVA找到所在区段,再用RVA-当前区段RVA+当前区段文件偏移 = F0A
        // 最后用F0A找到相对的opcode,复制到新内存执行再跳转
        for (WORD i = 0; i < pNtHeader->FileHeader.NumberOfSections; ++i)
        {
                if (Rva > pSectionHeader[i].VirtualAddress && Rva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData)
                {//通过RVA找到所在区段,再用RVA-当前区段RVA+文件偏移 = F0A
                        Deviation = Rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;
                        break;
                }
        }

        if (Deviation == 0)
        {
                MessageBoxA(0, "计算段内偏移错误", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }
        // 最后用段内偏移找到相对的opcode
        g_pAddr = (BYTE*)VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//申请一段堆空间执行被修改的汇编代码
        if (g_pAddr == NULL)
        {
                MessageBoxA(0, "VirtualAlloc Error", "error", MB_OK);
                delete[] pBuff;
                CloseHandle(hFile);
                return;
        }
        memcpy(g_pAddr, pBuff + Deviation, 5);//x32下VMP修改了前5字节,把文件内的原5字节拷贝过去
        g_pAddr[5] = 0x68;//push
        apiAddr += 5;
        memcpy(g_pAddr + 6, &apiAddr, 4);//push的地址是ZwProtectVirtualMemory+5的地方
        g_pAddr[10] = 0xc3;//使用retn过去
        delete[] pBuff;
        CloseHandle(hFile);
}
SIZE_T MyZwProtectVirtualMemory(
        HANDLE ProcessHandle, //进程句柄
        PVOID* BaseAddress,//地址的指针
        SIZE_T* NumberOfBytesToProtect,//修改的大小,函数调用后会改成成功修改的大小
        ULONG NewAccessProtection,//新的内存属性
        PULONG OldAccessProtection)//旧的内存属性
{

#ifdef _WIN64
        Init64();
#else
        Init32();
#endif
        pZwProtectVirtualMemory = (PZwProtectVirtualMemory)g_pAddr;
        return pZwProtectVirtualMemory(ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection);

}

验证下效果

1.程序介绍

一个加了VMP 3.2壳的易语言程序.

已知程序是获取CPU序号的程序,并且这个程序只调用了CPUID这个汇编代码获取了CPU序号,无法对相关的API进行HOOK,而且是个加了VMP壳的程序,只能让其代码段解密后修改,但想精准的修改还需在代码没运行前进行拦截,这里可以通过DLL进行API HOOK判断代码段是否解码,然后再HOOK拦截修改EAX或者EDX

2.寻找代码段解码后一个调用的函数


先看连接器信息,10.00,可能是一个vs2010连接器生成的EXE,vs2010连接器生成的代码在运行main函数之前第一个调用的API是GetSystemTimeAsFileTime

通过测试发现,GetSystemTimeAsFileTime 这个API第一次调用的时候,0x0040112B已经解密完成,并且对其下断能正常断下,可以使用这个API进行代码段是否解码完成.

3.HOOK GetSystemTimeAsFileTime

这里就没什么好说的,相信大家都对HOOK很熟悉了,调用上面我写好的MyZwProtectVirtualMemory函数来修改内存属性.
HOOK GetSystemTimeAsFileTime-> 判断代码是否解码->修改内存属性,然后HOOK 0040112B->稍微修改下CPUID调用后的EAX

case DLL_PROCESS_ATTACH:
        {
        // HOOK GetSystemTimeAsFileTime来判断代码段是否解密

        //保存GetSystemTimeAsFileTime前5字节,并且push  retn跳到GetSystemTimeAsFileTime + 5 位置
                g_pHookAddr = (BYTE*)VirtualAlloc(0, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);//GetSystemTimeAsFileTime调用接口
                PVOID addr = (PVOID)GetProcAddress(LoadLibraryA("KernelBase.dll"), "GetSystemTimeAsFileTime");
                memcpy(g_pHookAddr, addr, 5);//拷贝原5字节
                g_pHookAddr[5] = 0x68;//psuh  GetSystemTimeAsFileTime + 5 
                SIZE_T TempAddr = ((SIZE_T)addr) + 5;//GetSystemTimeAsFileTime + 5 
                memcpy(g_pHookAddr + 6, &TempAddr, 4);
                g_pHookAddr[10] = 0xc3;//retn

                SIZE_T size = 5;//e9 00 00 00 00 size
                ULONG OldProtect;
                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, PAGE_EXECUTE_READWRITE, &OldProtect);//修改内存属性
                addr = (PVOID)GetProcAddress(LoadLibraryA("KernelBase.dll"), "GetSystemTimeAsFileTime");
                BYTE opcode[8] = {};
                opcode[0] = 0xe9;//jmp
                SIZE_T MyApiAddr = (((SIZE_T)MyGetSystemTimeAsFileTime) - ((SIZE_T)addr) - 5);
                memcpy(opcode + 1, &MyApiAddr, 4);
                memcpy(addr, opcode, 5);//HOOK GetSystemTimeAsFileTime这个函数,并且JMP到MyGetSystemTimeAsFileTime

                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, OldProtect, &OldProtect);//恢复内存属性

                break;
        }

判断代码是否解码

VOID WINAPI MyGetSystemTimeAsFileTime(
        _Out_ LPFILETIME lpSystemTimeAsFileTime)
{

        WORD opcode;
        memcpy(&opcode, (PVOID)(((SIZE_T)GetModuleHandleA(nullptr)) + 0x112B), 2);
        if (opcode == 0xA20F)//EXEtest.vmp.exe + 0x112B
        {
                PVOID addr = (PVOID)(((SIZE_T)GetModuleHandleA(nullptr)) + 0x112B);
                SIZE_T size = 5;
                SIZE_T OldProtect;
                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, PAGE_EXECUTE_READWRITE, &OldProtect);//修改属性
                addr = (PVOID)(((SIZE_T)GetModuleHandleA(nullptr)) + 0x112B);
                memcpy(addr, new BYTE[5]{ 0xE9, 0x54 , 0x9E , 0x09 , 0x00 }, 5);
                size = 5;
                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, OldProtect, &OldProtect);//恢复属性

                addr = (PVOID)(((SIZE_T)GetModuleHandleA(nullptr)) + 0x9AF84);
                size = 15;
                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, PAGE_EXECUTE_READWRITE, &OldProtect);//修改属性
                addr = (PVOID)(((SIZE_T)GetModuleHandleA(nullptr)) + 0x9AF84);
                size = 15;
                memcpy(addr, new BYTE[15]{ 0x0F ,0xA2 ,0x89 ,0x55 ,0xFC ,0xB8 ,0x66 ,0x66 ,0x66 ,0x66 ,0xE9 ,0x9D ,0x61 ,0xF6 ,0xFF }, 15);
                MyZwProtectVirtualMemory((HANDLE)-1, &addr, &size, OldProtect, &OldProtect);//恢复属性
        }

        DefineFuncPtr(GetSystemTimeAsFileTime, g_pHookAddr);
        return My_GetSystemTimeAsFileTime(lpSystemTimeAsFileTime);
}

HOOK后对0x0040112B的处理

0040112B   E9 549E0900     jmp EXEtest_.0049AF84

0049AF84    0FA2            cpuid
0049AF86    8955 FC         mov dword ptr ss:[ebp-0x4],edx
0049AF89    B8 66666666     mov eax,0x66666666
0049AF8E    E9 9D61F6FF     jmp EXEtest_.00401130

完整的DLL代码在附件内,同测试程序同时上传吧.

WIN10测试图片:




# 附录

文件解压密码:52pojie

DLL源码: dllmain.zip (2.77 KB, 下载次数: 293)

测试程序下载:太大了,上传蓝奏吧:https://lanzouj.com/ibjcj7i   解压密码:52pojie

提示:测试用的exe是易语言编写,并且关闭了随机机制,还有修改oep的代码来调用LoadLibrary,详情可以去看没加壳前的exe.

DLL用注入器注入到程序内需要修改下GetSystemTimeAsFileTime的HOOK,因为他只有在开始的时候调用了一次,如果程序已经运行起来再注入是不会生效的.

加壳的测试程序可用<SharpOD 反反调试插件 v0.6b> + <吾爱破解OD>  进行调试观察.

免费评分

参与人数 31吾爱币 +38 热心值 +30 收起 理由
9152pojie + 1 + 1 用心讨论,共获提升!
harvey + 1 我很赞同!
dabenlong + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
chkds + 1 + 1 用心讨论,共获提升!
azcolf + 1 + 1 用心讨论,共获提升!
余弦 + 1 + 1 谢谢@Thanks!
gunxsword + 1 + 1 谢谢@Thanks!
yoza + 3 + 1 谢谢@Thanks!
zhh4827 + 1 + 1 热心回复!
chomosuke + 1 + 1 用心讨论,共获提升!
yixi + 1 + 1 我很赞同!
poisonbcat + 1 + 1 谢谢@Thanks!
独行风云 + 1 + 1 我很赞同!
jgs + 1 + 1 谢谢@Thanks!
女萝岩 + 1 + 1 我很赞同!
gaosld + 1 + 1 谢谢@Thanks!
hszt + 1 + 1 源码还可以修改成防hook的api调用,类似se
610100 + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xiong_online + 1 + 1 用心讨论,共获提升!
friendan + 1 + 1 我很赞同!
不爱everyone + 1 + 1 用心讨论,共获提升!
qaz003 + 1 + 1 谢谢分享,好久没看到这么底层的好贴了。。
5omggx + 1 + 1 用心讨论,共获提升!
asq56747277 + 1 + 1 给大佬递枸杞
寒枫雨雪 + 1 + 1 谢谢@Thanks!
CrazyNut + 3 + 1 前排膜拜大佬
朱朱你堕落了 + 1 你为何这么吊?
chinasmu + 2 + 1 NB!NB!NB!感谢大佬无私分享
dangducluan + 1 + 1 谢谢@Thanks!
cdj68765 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
pwp + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

千载难逢 发表于 2020-4-17 22:25
不用那么复杂好吧 直接读取到jmp 跟进去读偏移-4就是操作码了
jgs 发表于 2020-4-19 08:58
学习收藏,不定什么时候会用上,谢谢楼主提供。
pizazzboy 发表于 2020-4-17 21:48
大佬牛批!                              
skm415889471109 发表于 2020-4-17 22:22
加强学习,谢谢分享
三枫大神 发表于 2020-4-17 22:23
没看懂还是感觉好牛!!!
冥界3大法王 发表于 2020-4-17 22:24
SharpOD 我可不敢用了,兰屏杀手,硬盘都有坏道了。
 楼主| 你与明日 发表于 2020-4-17 22:41
千载难逢 发表于 2020-4-17 22:25
不用那么复杂好吧 直接读取到jmp 跟进去读偏移-4就是操作码了

我知道,跟进去-5,但我只看了3.2和3.0.1,而且也不局限于VMP壳,有代码在这,稍微改改还可以自己手动进0环,从函数头往下找到C3停止,全复制出来
拉玛西亚 发表于 2020-4-17 22:43
我想到了SE授权了,好像也是这个道理
无闻无问 发表于 2020-4-17 22:45
冥界3大法王 发表于 2020-4-17 22:24
SharpOD 我可不敢用了,兰屏杀手,硬盘都有坏道了。

为什么我没发现?难道我用的是假的?
kf9974831 发表于 2020-4-18 00:04
谢谢分享 感谢有你
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:56

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表