李沉舟 发表于 2016-8-17 09:53

偶然间想到一种无需DLL跨进程HOOK的方法

这个方法的原理就是:
①首先在API头部写一个CC断点
②中断的时候程序收到消息,并且读出参数

需要注意的地方只有修正EIP寄存器,和读取参数的问题。
因为API采用STDCALL的方式调用,所以参数是从右向左压进堆栈的。
还有就是API的开头是
mov edi,edi
push ebp
mov ebp,esp

我们要注意,因为我们是在头部断下的,还没有执行push ebp,而执行了push ebp之后esp会减4,也就是说,我们在头部中断的时候要把esp-4才能拿ESP来读取参数!

还要注意的就是,如何不执行API直接跳转到返回地址,一般ESP储存的地址指向的就是返回地址,所以我们只要把EIP修改就好,如果你要设置返回值,那么就修改EAX为返回值。(记得修正ESP)

如何单步?这也是一个问题,我们可以在第一条指令中断之后,在第二条指令下CC断点,同时还原第一条指令,等到第二条指令触发中断事件时,我们再在第一条指令上下CC断点,再还原第二条指令,这就模拟了一次单步。

我把代码贴出来,使用VC2010编译

#include <stdio.h>
#include <windows.h>

#define SE_DEBUG_PRIVILEGE 20
typedefDWORD(WINAPI *PRtlAdjustPrivilege)   ///未文档化函数声明
(
ULONG    Privilege,
BOOLEAN Enable,
BOOLEAN CurrentThread,
PBOOLEAN Enabled
);

void WINAPI AdjustPrivilege()         ///ntdll中的提权函数
{
        BOOLEAN Enabled;
        PRtlAdjustPrivilege RtlAdjustPrivilege = (PRtlAdjustPrivilege)GetProcAddress(LoadLibrary((LPCSTR)"ntdll.dll"), "RtlAdjustPrivilege");
        RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &Enabled);
}

int main(void)
{
    DWORD pid;
    DWORD lpapi;
    BYTE code;
    BYTE _code;
    BYTE cc = 0xCC;
    PDWORD p;
    HANDLE hProcess;
    HANDLE hThread;
    HMODULE hMod;
    int yes = TRUE;
    DEBUG_EVENT dbg;
    CONTEXT ct;

    int wType;
    char text;
    char caption;
    int hwin;
    DWORD pt;

    char ch;

    AdjustPrivilege(); ///提升进程权限到DEBUG
    printf("请输入要HOOK的进程ID:____\b\b\b\b");
    scanf("%d", &pid); ///获取要HOOK的进程的ID
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    hMod = GetModuleHandle("user32.dll"); ///获取模块句柄
    if (!hMod)
      hMod = LoadLibrary("user32.dll"); ///如果没有获取到模块句柄,则加载模块
    lpapi = (DWORD)GetProcAddress(hMod, "MessageBoxA"); ///取API地址
    ReadProcessMemory(hProcess, (LPVOID)lpapi, code, 2, NULL); ///获取API头部两字节数据,即第一条指令
    ReadProcessMemory(hProcess, (LPVOID)(lpapi + 2), &_code, 1, NULL); ///获取API第二条指令第一字节数据
    WriteProcessMemory(hProcess, (LPVOID)lpapi, &cc, 1, NULL);
    DebugActiveProcess(pid); ///开启调试会话
    while (yes)
    {
      if(WaitForDebugEvent(&dbg, 20) == 0) ///如果当前还没有触发调试信息
      {
            Sleep(50);
            continue;
      }
      if(dbg.dwDebugEventCode == EXCEPTION_DEBUG_EVENT) ///如果是调试事件
      {
            if(dbg.u.Exception.ExceptionRecord.ExceptionCode != EXCEPTION_BREAKPOINT) ///如果不是中断事件
            {
                ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_EXCEPTION_NOT_HANDLED); ///继续调试
                continue;
            }
            if((DWORD)dbg.u.Exception.ExceptionRecord.ExceptionAddress == lpapi)
            {
                hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dbg.dwThreadId); ///打开线程
                ct.ContextFlags = CONTEXT_ALL;
                SuspendThread(hThread); ///暂停线程,否则不可以读取线程寄存器信息
                GetThreadContext(hThread, &ct); ///读取线程寄存器信息
                printf("拦截到进程MessageBoxA调用!\n");
                ///这里为什么要-4?因为函数头部是push ebp,mov ebp,esp,而push了ebp之后,esp会-4,我们下CC断点的地方是函数头部
                ///所以此时ESP还未-4,所以我们读取的时候要把ESP-4
                ReadProcessMemory(hProcess, (LPVOID)(ct.Esp + 0x10), &hwin, 4, NULL); ///读取HWND参数
                ReadProcessMemory(hProcess, (LPVOID)(ct.Esp + 0xC), &pt, 4, NULL); ///读取Text参数的指针
                ReadProcessMemory(hProcess, (LPVOID)pt, text, 255, NULL); ///读取Text参数
                ReadProcessMemory(hProcess, (LPVOID)(ct.Esp + 0x8), &pt, 4, NULL); ///读取Caption参数的指针
                ReadProcessMemory(hProcess, (LPVOID)pt, caption, 255, NULL); ///读取Caption参数
                ReadProcessMemory(hProcess, (LPVOID)(ct.Esp + 0x4), &wType, 4, NULL); ///读取wType参数
                ReadProcessMemory(hProcess, (LPVOID)(ct.Esp), &pt, 4, NULL); ///读取返回地址
                printf("hwnd:%d\nlpText:%s\nlpCaption:%s\nwType:%d\n", hwin, text, caption, wType);
                printf("返回地址:%#x\n", pt);
                printf("输入Y允许执行,输入N不允许执行,输入其他则退出:_\b");
                fflush(stdin);
                scanf("%c", &ch);
                if(ch == 'Y' || ch == 'y')
                {
                  ///允许执行,模拟OD单步事件
                  WriteProcessMemory(hProcess, (LPVOID)(lpapi + 2), &cc, 1, NULL); ///在API的第二条指令下断点
                  WriteProcessMemory(hProcess, (LPVOID)lpapi, code, 2, NULL); ///还原第一条指令
                  ct.Eip--; ///修改EIP寄存器,使其-1,重新指向API头第一字节
                  SetThreadContext(hThread, &ct);
                  ResumeThread(hThread);
                  CloseHandle(hThread);
                }
                else if(ch == 'N' || ch == 'n')
                {
                  ///不允许执行
                  ct.Esp+= 0x14; ///修正堆栈
                  ct.Eip = pt; ///修正EIP到返回地址
                  SetThreadContext(hThread, &ct);
                  ResumeThread(hThread);
                  CloseHandle(hThread);
                }
                else
                {
                  ///退出
                  ct.Eip--;
                  SetThreadContext(hThread, &ct);
                  WriteProcessMemory(hProcess, (LPVOID)lpapi, code, 2, NULL); ///还原API头部信息
                  WriteProcessMemory(hProcess, (LPVOID)(lpapi + 2), &_code, 1, NULL);
                  ResumeThread(hThread);
                  CloseHandle(hThread);
                  yes = FALSE;
                }
            }
            else if((DWORD)dbg.u.Exception.ExceptionRecord.ExceptionAddress == (lpapi + 2)) ///如果是中断在第二条指令
            {
                hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dbg.dwThreadId); ///打开线程
                ct.ContextFlags = CONTEXT_ALL;
                SuspendThread(hThread); ///暂停线程,否则不可以读取线程寄存器信息
                GetThreadContext(hThread, &ct); ///读取线程寄存器信息
                WriteProcessMemory(hProcess, (LPVOID)lpapi, &cc, 1, NULL); ///重新在API第一条指令上设置CC断点
                WriteProcessMemory(hProcess, (LPVOID)(lpapi + 2), &_code, 1, NULL); ///还原第二条指令
                ct.Eip--; ///修正EIP
                SetThreadContext(hThread, &ct);
                ResumeThread(hThread);
                CloseHandle(hThread);
            }



            ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE);
      }
      ContinueDebugEvent(dbg.dwProcessId, dbg.dwThreadId, DBG_CONTINUE);
    }
    DebugActiveProcessStop(pid);
    return 0;
}









代码下载:




tianzhiya 发表于 2016-8-17 10:43

多谢楼主分享!

今晚打老虎 发表于 2016-8-17 11:27

看不懂 好高深

酷爱 发表于 2016-8-17 19:53

我也看不懂,还是感谢LZ分享

84484884 发表于 2018-12-17 14:33

这个高明{:7_236:}

永远 发表于 2021-3-26 08:20


多谢楼主分享!

平淡 发表于 2021-7-11 15:44

这个利用调试来实现感觉有点没必要,如果进程检测调试,整个HOOK就没用了。。。。
页: [1]
查看完整版本: 偶然间想到一种无需DLL跨进程HOOK的方法