首页
社区
课程
招聘
[原创]R3 下常用Hook技术
发表于: 2021-12-23 10:55 36939

[原创]R3 下常用Hook技术

2021-12-23 10:55
36939

​ 最近看了《加密与解密》,跟着大佬们的思路学习了Hook相关知识,如理解有误请不吝赐教,以免误导他人。

​ API函数都保存在操作系统提供的DLL文件中,当在程序中调用某个API函数并运行程序后,程序会隐式地将API函数所在的DLL文件加载入内存中,这样,程序就会像调用自己的函数一样调用API。Inline Hook这种方法是在程序流程中直接进行嵌入jmp指令来改变流程的。

​ 简而言之,就是将函数开头修改为jmp指令,跳转到我们自定义的函数上去。

首先用CreateProcessA API写一个测试程序,功能很简单,程序启动后,按下任意键,调用CreateProcessA创建进程,为了直观这里是直接弹一个计算器:

下面就HOOK CreateProcessA:

将生成的EXE拖到Xdbg中,定位到CreateProceessA这里,在调用CreateProcessA前有一段汇编代码:

mov edi edi,

push ebp

mov ebp,esp

16进制:8B FF 55 8B EC

image-20211217113006811

HOOK的话肯定要准备一个自定义的函数, call addr ,假如它的地址为12345678,那么我们HOOK的操作就是将上面的命令替换为:JMP 12345678,也就是e9 addr。

解除的HOOK的话就是将替换的字节恢复。

Inline Hook流程

代码如下,关键步骤已注释:

效果如下:

image-20211220091318443

​ IAT Hook是 Address Hook的一种方式,顾名思义就是通过修改函数的地址进行Hook。

​ IAT(Import Address Table,输入表)是PE中的一种结构,如图:

image-20211220162556261

再用一张图来理解导入表结构:

导入表.png

需要注意的是:因为IAT具体指某个PE模块的IAT,所以他的作用范围只针对被Hook的模块,且必须在以静态链接的方式调用API时才会被Hook,所以它的作用范围只针对被Hook的模块,且必须以静态链接的方式调用API时才会被Hook,在使用Loadlibrary或GetProcAddress进行动态调用时不受影响。要想对已加载的所有模块起作用,就必须遍历进程内的所有模块,对目标API进行Hook。

现在开始Hook,这里是通过注入来Hook其他的程序,将代码写在DLL里面

为了保证原来的函数不受影响,我们先将要被Hook的函数的地址保存下来:

然后就是解析PE文件,获取函数导入表中的函数地址表,并替换:

解析PE文件前面已经学习过了,下面过程直接走一遍:

pDosHeader->e_lfanew的数据类型为DWORD,前面定义了pDosHeader的数据类型为PIMAGE_DOS_HEADER,要和pDosHeader相加需将pDosHeader类型转换为DWORD,最后再将得到的结果转换为PIMAGE_NT_HEADERS

image-20211220164816840

获取扩展头

从扩展头中获取数据目录表中的导入表

获取导入表前,要先找到它的偏移:

获取导入表:

有多个导入表结构,所以要遍历每个导入表:

因为导入表是依靠一个全零的结构来判断结束的,所以我们就采取对比pImportTable->Characteristics为0和pImportTable->FirstThunk为NULL时,来判断结束,然后在其中判断函数地址是否与我们所得到的原函数地址一致,如果一致说明找到了:

最后,为了保证程序的稳定,我们需要构造与被 HOOK 的函数一样结构的函数,同时为了保证原函数功能的正常运行,再定义一个函数指针,在自己的功能执行完成后,调用原来程序正常的功能:

​ 要HOOK的API是MessageBoxA (X32),写一个简单的程序:

要达到的目的是当测试程序运行时,注入DLL,Hook住MessageBoxA,使其指向CreateProcessA api:

正常运行:

image-20211221135030995

Hook后:

image-20211221135121759

​ 在代码编译为程序后,虚函数表就是一个固定的表了,它位于PE的.data段。在对虚函数表进行Hook时,虽然原理也是查找原函数的位置,修改页面属性,写入Detour函数这样的过程,但是虚函数有些特殊。

​ 同样是Hook地址,它不能像IAT Hook那样直接定义一个函数来替换目标函数,而必须把它定义为类的成员函数。我们知道面向对象的三要素:封装、继承、多态 。在多态里有类特殊的是虚函数(以virtual修饰), 32位系统下,对象里有4个字节保存虚表的数组,其值为每一项虚函数的地址。

​ 针对虚函数的HOOK就是通过保存对象中的虚表的值,针对每一项进行替换。

这次虚函数的Hook就在程序本身执行了,dll注入的方式需要对程序进行逆向分析,暂时放一下:

效果如下:

image-20211221151239817

热补丁Hook是微软提供的一种安全的Hook的机制,也是将函数开头修改为jmp指令,跳转到自定义的函数地址执行,和IAT Hook类似却又有不同。

以IAT Hook中的测试程序为例:

我们可以看到CreateProcessA函数的首字节为 mov edi,edi(88 FF),这句汇编意思就是将edi的值放入edi,实际上并没有什么用

image-20211222103323379

我们还看到在这个API上边有大段的int3 中断。这就给了我们一种新的Hook思路,即将前两个字节改为短跳转指令(EB E9),使其跳到函数上边五字节处,这五个字节的int3中断实际上就是一段空闲空间:

image-20211222104041451

然后再将这五个字节改为长跳转指令(E9 xxxxxxxx)。这样,即使Hook失败,也不影响函数的继续执行。

image-20211222104213787

这样,hook函数的时候,先是一个短跳跳到自定义的函数然后执行。如果要恢原流程,则找到函数地址并+2,直接跳过E8 F9 ,从push ebp开始执行。

image-20211222104340714

然后开始写代码,有了其他Hook方式的经验,这次直接写一个通用的Hook:

还是以这个测试程序为例:

image-20211222142242294

将生成的dll注入,hook后,当执行到MessageboxA时会劫持住它原来流程去执行CreateProcessA,如图弹出了notepad.exe:

image-20211222141549469

补充:并不是所有的api都能使用HotPatch的方式进行Hook,比如CreateProcess,当然或许是我代码写错了:)

简单介绍一下windows的异常机制:

Intel在386开始的IA-32家族处理器中引入了异常中断。中断是指外部硬件设备或异步事件引发的,而异常是由内部事件产生的,又可分为故障,陷阱和终止三类。故障和陷阱是可恢复的,终止是不可恢复的,如果出现了了终止异常,则需要重启操作系统解决。

Windows中主要的异常处理机制:VEH、SEH、C++EH。

SEH:结构化异常处理。就是平时用的__try __finally __try __except,是对c的扩展。

VEH:向量异常处理。一般来说用AddVectoredExceptionHandler去添加一个异常处理函数,可以通过第一个参数决定是否将VEH函数插入到VEH链表头,插入到链表头的函数先执行,如果为1,则会最优先执行。

C++EH是C++提供的异常处理方式,执行顺序将排在最后。

在用户模式下发生异常时,异常处理分发函数在内部会先调用遍历 VEH 记录链表的函数, 如果没有找到可以处理异常的注册函数,再开始遍历 SEH 注册链表。

主要区分一下SEH和VEH:

然后开始写代码,主要流程如下:

在调试模式下,当MessageBox被调用时,会触发int3异常:

image-20211223094346111

将项目编译为PE文件再次运行,已经成功Hook:

image-20211223094536678

代码如下:

如果要实现Hook其他程序,只需将函数写到dll文件中然后进行注入:

 
 
#include <windows.h>
#include <stdio.h>
#include "createprocess.h"
 
#define EXE_PATH "C:\\Windows\\System32\\calc.exe"
 
BOOL CreateProcessR(char* szExePath) {
    SECURITY_ATTRIBUTES psa = { 0 };
    SECURITY_ATTRIBUTES tsa = { 0 };
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    BOOL Ret;
    Ret = CreateProcessA(szExePath, NULL, &psa, &tsa, false, 0, NULL, NULL, &si, &pi);
    //TerminateProcess(pi.hProcess, 0);  //结束进程
    return Ret;
}
int main(int argc,char *argv[]) {
 
    system("pause");
    CreateProcessR(EXE_PATH);
 
    return 0;
}
#include <windows.h>
#include <stdio.h>
#include "createprocess.h"
 
#define EXE_PATH "C:\\Windows\\System32\\calc.exe"
 
BOOL CreateProcessR(char* szExePath) {
    SECURITY_ATTRIBUTES psa = { 0 };
    SECURITY_ATTRIBUTES tsa = { 0 };
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    BOOL Ret;
    Ret = CreateProcessA(szExePath, NULL, &psa, &tsa, false, 0, NULL, NULL, &si, &pi);
    //TerminateProcess(pi.hProcess, 0);  //结束进程
    return Ret;
}
int main(int argc,char *argv[]) {
 
    system("pause");
    CreateProcessR(EXE_PATH);
 
    return 0;
}
 
 
 
 
 
 
 
 
 
//Myhook.cpp
#include "Myhook.h"
 
CHOOK::CHOOK()
{
    MyFuncaAddress = NULL;
    memset(MyOldBytes, 0, 5);
    memset(MyNewBytes, 0, 5);
}
 
CHOOK::~CHOOK()
{   
    UnHOOK();
    MyFuncaAddress = NULL;
    memset(MyOldBytes, 0, 5);
    memset(MyNewBytes, 0, 5);
}
 
BOOL CHOOK::Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{   
    HMODULE hModule = GetModuleHandle(pszModuleName);
    MyFuncaAddress = (PROC)GetProcAddress(hModule, pszFuncName);
    if (MyFuncaAddress == NULL) {
        return FALSE;
    }
    //读地址   将原来的5个字节的数据保存
    ReadProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyOldBytes, 5, 0);
    //JMP ADDRESS JMP 123456789 E9
    MyNewBytes[0] = '\xE9';
    *(DWORD*)(MyNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)MyFuncaAddress - 5;
    WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyNewBytes, 5, 0);
    return TRUE;
}
VOID CHOOK::UnHOOK()
{   
    if (MyFuncaAddress != NULL) {
        WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyOldBytes, 5, 0);
    }
    return VOID();
}
BOOL CHOOK::ReHook()
{
    if (MyFuncaAddress != NULL) {
        WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyNewBytes, 5, 0);
    }
    return 0;
}
//Myhook.cpp
#include "Myhook.h"
 
CHOOK::CHOOK()
{
    MyFuncaAddress = NULL;
    memset(MyOldBytes, 0, 5);
    memset(MyNewBytes, 0, 5);
}
 
CHOOK::~CHOOK()
{   
    UnHOOK();
    MyFuncaAddress = NULL;
    memset(MyOldBytes, 0, 5);
    memset(MyNewBytes, 0, 5);
}
 
BOOL CHOOK::Hook(LPSTR pszModuleName, LPSTR pszFuncName, PROC pfnHookFunc)
{   
    HMODULE hModule = GetModuleHandle(pszModuleName);
    MyFuncaAddress = (PROC)GetProcAddress(hModule, pszFuncName);
    if (MyFuncaAddress == NULL) {
        return FALSE;
    }
    //读地址   将原来的5个字节的数据保存
    ReadProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyOldBytes, 5, 0);
    //JMP ADDRESS JMP 123456789 E9
    MyNewBytes[0] = '\xE9';
    *(DWORD*)(MyNewBytes + 1) = (DWORD)pfnHookFunc - (DWORD)MyFuncaAddress - 5;
    WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyNewBytes, 5, 0);
    return TRUE;
}
VOID CHOOK::UnHOOK()
{   
    if (MyFuncaAddress != NULL) {
        WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyOldBytes, 5, 0);
    }
    return VOID();
}
BOOL CHOOK::ReHook()
{
    if (MyFuncaAddress != NULL) {
        WriteProcessMemory(GetCurrentProcess(), MyFuncaAddress, MyNewBytes, 5, 0);
    }
    return 0;
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。
 
#include "Myhook.h"
 
CHOOK MyHookObject;
BOOL
WINAPI
MyCreateProcessA(
    _In_opt_ LPCSTR lpApplicationName,
    _Inout_opt_ LPSTR lpCommandLine,
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOA lpStartupInfo,
    _Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
    if (MessageBox(NULL, "是否拦截", "Notice", MB_YESNO) == IDYES)
    {
        MessageBox(NULL, "程序已拦截", "Notice", MB_OK);
    }
    else
    {
        MyHookObject.UnHOOK();
        CreateProcessA(
            lpApplicationName,
            lpCommandLine,
            lpProcessAttributes,
            lpThreadAttributes,
            bInheritHandles,
            dwCreationFlags,
            lpEnvironment,
            lpCurrentDirectory,
            lpStartupInfo,
            lpProcessInformation
        );
        MyHookObject.ReHook();
    }
    return true;
}
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MyHookObject.Hook((LPSTR)"Kernel32.dll", (LPSTR)"CreateProcessA",(PROC)MyCreateProcessA);
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        MyHookObject.UnHOOK();
        break;
    }
    return TRUE;
}
// dllmain.cpp : 定义 DLL 应用程序的入口点。
 
#include "Myhook.h"
 
CHOOK MyHookObject;
BOOL
WINAPI
MyCreateProcessA(
    _In_opt_ LPCSTR lpApplicationName,
    _Inout_opt_ LPSTR lpCommandLine,
    _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
    _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
    _In_ BOOL bInheritHandles,
    _In_ DWORD dwCreationFlags,
    _In_opt_ LPVOID lpEnvironment,
    _In_opt_ LPCSTR lpCurrentDirectory,
    _In_ LPSTARTUPINFOA lpStartupInfo,
    _Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
    if (MessageBox(NULL, "是否拦截", "Notice", MB_YESNO) == IDYES)
    {
        MessageBox(NULL, "程序已拦截", "Notice", MB_OK);
    }
    else
    {
        MyHookObject.UnHOOK();
        CreateProcessA(
            lpApplicationName,
            lpCommandLine,
            lpProcessAttributes,
            lpThreadAttributes,
            bInheritHandles,
            dwCreationFlags,
            lpEnvironment,
            lpCurrentDirectory,
            lpStartupInfo,
            lpProcessInformation
        );
        MyHookObject.ReHook();
    }
    return true;
}
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MyHookObject.Hook((LPSTR)"Kernel32.dll", (LPSTR)"CreateProcessA",(PROC)MyCreateProcessA);
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        MyHookObject.UnHOOK();
        break;
    }
    return TRUE;
}
 
 
 
 
 
 
 
 
OldMesageBoxA = (FncMessageBoxA)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
OldMesageBoxA = (FncMessageBoxA)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
 
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleA(NULL);
//当传入参数为NULL时,获取的是PE文件的imagebase,通过类型强制转换就获取到了dos_header
  PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandleA(NULL);
//当传入参数为NULL时,获取的是PE文件的imagebase,通过类型强制转换就获取到了dos_header
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
 
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
//扩展头是NT头的一个成员
PIMAGE_OPTIONAL_HEADER pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&pNtHeader->OptionalHeader;
//扩展头是NT头的一个成员
DWORD dwImportTableOffset = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; //获取导入表偏移
DWORD dwImportTableOffset = pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; //获取导入表偏移
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(DWORD)pDosHeader + dwImportTableOffset;
PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)(DWORD)pDosHeader + dwImportTableOffset;
 
DWORD* pFirstThunk;
    //遍历导入表结构
    while (pImprotTable->Characteristics && pImprotTable->FirstThunk != NULL)
    {
        pFirstThunk = (DWORD*)(pImprotTable->FirstThunk + (DWORD)pDosHeader);
        while (*(DWORD*)pFirstThunk != NULL)
        {
            //如果相当了,就说明当前的数组元素就是我们要找的函数地址表中的函数地址
            if (*(DWORD*)pFirstThunk == (DWORD)OldMesageBoxA)
            {
                DWORD oldProtected;
                VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
                DWORD dwFuncAddr = (DWORD)MyMessageBoxA;
                memcpy(pFirstThunk, (DWORD*)&dwFuncAddr, 4);
                VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
            }
            pFirstThunk++;
        }
        pImprotTable++;
    }
DWORD* pFirstThunk;
    //遍历导入表结构
    while (pImprotTable->Characteristics && pImprotTable->FirstThunk != NULL)
    {
        pFirstThunk = (DWORD*)(pImprotTable->FirstThunk + (DWORD)pDosHeader);
        while (*(DWORD*)pFirstThunk != NULL)
        {
            //如果相当了,就说明当前的数组元素就是我们要找的函数地址表中的函数地址
            if (*(DWORD*)pFirstThunk == (DWORD)OldMesageBoxA)
            {
                DWORD oldProtected;
                VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
                DWORD dwFuncAddr = (DWORD)MyMessageBoxA;
                memcpy(pFirstThunk, (DWORD*)&dwFuncAddr, 4);
                VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
            }
            pFirstThunk++;
        }
        pImprotTable++;
    }
typedef int
(WINAPI*
    FncMessageBoxA)(
        _In_opt_ HWND hWnd,
        _In_opt_ LPCSTR lpText,
        _In_opt_ LPCSTR lpCaption,
        _In_ UINT uType);
FncMessageBoxA OldMesageBoxA = NULL;
int
WINAPI MyMessageBoxA(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCSTR lpText,
    _In_opt_ LPCSTR lpCaption,
    _In_ UINT uType)
{
    SECURITY_ATTRIBUTES psa = { 0 };
    SECURITY_ATTRIBUTES tsa = { 0 };
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    CreateProcessA(EXE_PATH, NULL, &psa, &tsa, false, 0, NULL, NULL, &si, &pi);
    return 0;
}
typedef int
(WINAPI*
    FncMessageBoxA)(
        _In_opt_ HWND hWnd,
        _In_opt_ LPCSTR lpText,
        _In_opt_ LPCSTR lpCaption,
        _In_ UINT uType);
FncMessageBoxA OldMesageBoxA = NULL;
int
WINAPI MyMessageBoxA(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCSTR lpText,
    _In_opt_ LPCSTR lpCaption,
    _In_ UINT uType)
{
    SECURITY_ATTRIBUTES psa = { 0 };
    SECURITY_ATTRIBUTES tsa = { 0 };
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    CreateProcessA(EXE_PATH, NULL, &psa, &tsa, false, 0, NULL, NULL, &si, &pi);
    return 0;
}
#include <windows.h>
int main() {
    system("pause");
    MessageBoxA(0, 0, 0, 0);
    system("pause");
    return 0;
}
#include <windows.h>
int main() {
    system("pause");
    MessageBoxA(0, 0, 0, 0);
    system("pause");
    return 0;
}
 
 
 
 
 
 
 
#include <stdio.h>
#include <windows.h>
 
class MyClass
{
public:
    MyClass();
    ~MyClass();
    virtual void print();
private:
 
};
 
MyClass::MyClass()
{
}
 
MyClass::~MyClass()
{
}
 
void MyClass::print()
{
    printf("hello\r\n");
}
 
void Myfunc() {
    MessageBoxA(NULL, "hello", "title", NULL);
}
int main() {
    MyClass obj;
    MyClass& vobj = obj;
    vobj.print();
    //寻找虚表指针,虚表指针通常情况下位于对象的头4字节上
    int nAddr = *(int*)&obj;
    //更改内存属性
    DWORD dwOldProtect = 0;
    VirtualProtect((void*)nAddr, 0x100, PAGE_EXECUTE_READWRITE,&dwOldProtect);
    //将自己的函数地址替换过去,前面说到了数组的值就是虚函数的地址,我们写的测试例子中就写了一个虚函数,所以其地址就是第一个值
    (*(int*)nAddr) = (int)Myfunc;
    VirtualProtect((void*)nAddr, 0x100, dwOldProtect,&dwOldProtect);
    vobj.print();
    system("pause");
}
#include <stdio.h>

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-12-23 11:10 被soloz编辑 ,原因:
收藏
免费 16
支持
分享
最新回复 (23)
雪    币: 2121
活跃值: (1346)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
讲的很清楚,优秀
2021-12-23 16:18
0
雪    币: 3658
活跃值: (4222)
能力值: (RANK:215 )
在线值:
发帖
回帖
粉丝
3

内容详细,建议传一份WORD或者PDF版收藏,谢谢。

最后于 2021-12-24 08:53 被china编辑 ,原因:
2021-12-24 08:53
0
雪    币: 627
活跃值: (1568)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
china 内容详细,建议传一份WORD或者PDF版收藏,谢谢。
师傅过誉了,用md格式写的,如果要保存可以贴下来自行保存为.md或者转为pdf都是可以的   :)
2021-12-24 09:31
0
雪    币: 627
活跃值: (1568)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
5
Hannibal_962828 讲的很清楚,优秀
2021-12-24 09:31
0
雪    币: 3658
活跃值: (4222)
能力值: (RANK:215 )
在线值:
发帖
回帖
粉丝
6
soloz 师傅过誉了,用md格式写的,如果要保存可以贴下来自行保存为.md或者转为pdf都是可以的 :)
感谢大牛的答复。
2021-12-24 10:44
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
楼主能发个完整项目的github连接吗,想跟着跑跑还有楼主的ddl注入用的是啥额
2021-12-24 19:05
0
雪    币: 22
活跃值: (443)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2021-12-25 12:10
0
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
整理用心,感谢分享
2021-12-26 17:07
0
雪    币: 627
活跃值: (1568)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
10

1

最后于 2021-12-27 09:26 被soloz编辑 ,原因:
2021-12-27 09:26
0
雪    币: 627
活跃值: (1568)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
11
mb_oexecrwb 楼主能发个完整项目的github连接吗,想跟着跑跑还有楼主的ddl注入用的是啥额
开发环境是vs2019 代码基本上已经给全了,修改一些项目属性就行,注入器是网上找的,或者直接写一个简单的注入器也行
2021-12-27 09:27
0
雪    币: 261
活跃值: (547)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
12
为何导入表那个不用函数名字比较,用地址比较 
2021-12-30 18:45
0
雪    币: 6084
活跃值: (5490)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
13
感谢分享,写的不错!
2022-2-11 16:41
0
雪    币: 1281
活跃值: (4540)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
14
VEH+硬断好用些
2022-2-19 18:38
0
雪    币: 6213
活跃值: (4216)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
15
讲的很不错 跟着学习一下
2022-2-20 00:28
0
雪    币: 1802
活跃值: (4000)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
mark
2022-3-21 21:38
0
雪    币: 6307
活跃值: (3837)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
问一下,inlinehook不需要改变内存保护属性吗?
2022-3-22 15:06
0
雪    币: 299
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
发文章希望的是给全source,感谢楼主。
2022-5-29 15:53
0
雪    币: 433
活跃值: (2524)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
20
mark
2022-6-1 14:28
0
雪    币: 65
活跃值: (437)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
21
支持一下
2022-6-14 10:25
0
雪    币: 420
活跃值: (2638)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
支持一下。
2022-7-29 11:12
0
雪    币: 42
活跃值: (208)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
方案确实可行,但还是会修改一字节,过不了crc32检测,把还不如直接硬件断点
2022-12-15 20:02
0
雪    币: 300
活跃值: (2452)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
mark
2022-12-15 20:53
0
游客
登录 | 注册 方可回帖
返回
//