X64--线程劫持-Suspend-Inject-Resume

X64–线程劫持-Suspend-Inject-Resume


  • X64与X86 的原理是一样的。这里不做多解释
#include <stdio.h>
#include <Windows.h>
#include <string.h>
#include <Psapi.h>
#include <TlHelp32.h>
#include <tchar.h>
EXTERN_C VOID asm_code();
#define     RIP_PARAM   0x1234567812345679
#define     RIP_TARGET  0x1234567812345670
#define     RIP_PRE     0x1234567812345678
#define     RIP_END     0x78563412
static DWORD asm_code_size = 0;
PBYTE GetStealthStubPtr()
{
    PBYTE Ptr = (PBYTE)asm_code;

    if (*Ptr == 0xE9)
    {
        Ptr += *((PINT)(Ptr + 1)) + 5;
    }

    return Ptr;
}
ULONG32 GetStealthStubSize()
{
    PUCHAR      Ptr = NULL;
    PUCHAR      BasePtr = NULL;
    ULONG32     Index = 0;
    ULONG32     Signature = 0;

    if (asm_code_size != 0)
    {
        return asm_code_size;
    }

    BasePtr = Ptr = GetStealthStubPtr();

    for (Index = 0; Index < 2000; Index++)
    {
        Signature = *((PULONG)Ptr);

        if (Signature == RIP_END)
        {
            asm_code_size = (ULONG)(Ptr - BasePtr);

            return asm_code_size;
        }

        Ptr++;
    }
    printf("Error:没有找到:%p\n", RIP_END);
    return 0;
}
VOID suspendInjectResume(HANDLE hHandle, LPVOID loadLibAddr, LPVOID dllPathAddr) {

    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    HANDLE thread = NULL;
    THREADENTRY32   te;
    CONTEXT         ctx;
    DWORD firstThread = 0;

    LPVOID scAddr;

    int i;

    unsigned char sc[0x100];
    memcpy(sc, GetStealthStubPtr(), GetStealthStubSize());

    te.dwSize = sizeof(THREADENTRY32);
    ctx.ContextFlags = CONTEXT_FULL;

    // Suspend Threads
    if (Thread32First(hSnapshot, &te)) {
        do {
            if (te.th32OwnerProcessID == GetProcessId(hHandle)) {

                thread = OpenThread(THREAD_ALL_ACCESS | THREAD_GET_CONTEXT, FALSE, te.th32ThreadID);
                if (thread != NULL) {
                    printf("\t[+] Suspending Thread 0x%08x\n", te.th32ThreadID);
                    SuspendThread(thread);
                    break;
                }
                else {
                    printf("\t[+] Could not open thread!\n");
                }
            }
        } while (Thread32Next(hSnapshot, &te));
    }
    else {
        printf("\t[+] Could not Thread32First! [%d]\n", GetLastError());
        CloseHandle(hSnapshot);
        exit(-1);
    }
    CloseHandle(hSnapshot);

    if (GetThreadContext(thread, &ctx) == 0)
        printf("[!] GetThreadContext Failed!\n");
    ULONG_PTR   ptr = (ULONG_PTR)sc;
    for (int Index = 0; Index < GetStealthStubSize(); Index++)
    {

#pragma warning(disable:4311)   // 关闭截断警告
        if (*(PULONG_PTR)ptr == RIP_PARAM)
        {
            *(PULONG_PTR)ptr = (ULONG_PTR)dllPathAddr;

        }
        else if (*(PULONG_PTR)ptr == RIP_PRE)   // NETEntry
        {
            *(PULONG_PTR)ptr = (ULONG_PTR)ctx.Rip;

        }
        else if (*(PULONG_PTR)ptr == RIP_TARGET)    // NETEntry
        {
            *(PULONG_PTR)ptr = (ULONG_PTR)loadLibAddr;
        }
        ptr++;
    }
    scAddr = VirtualAllocEx(hHandle, NULL, GetStealthStubSize(), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    WriteProcessMemory(hHandle, scAddr, (LPCVOID)sc, GetStealthStubSize(), NULL);

    ctx.Rip = (DWORD64)scAddr;
    printf("new RIP:%p\n", scAddr);
    if (SetThreadContext(thread, &ctx) == 0)
        printf("[!] SetThreadContext Failed!\n");
    getchar();
    getchar();
    ResumeThread(thread);
    getchar();
    getchar();
}

#define SE_DEBUG_PRIVILEGE  20
typedef long(__fastcall *pfnRtlAdjustPrivilege64)(ULONG, ULONG, ULONG, PVOID);
pfnRtlAdjustPrivilege64 RtlAdjustPrivilege;
BOOL EnableDebugPrivilegeX64()
{
    DWORD dwRetVal;
    RtlAdjustPrivilege = (pfnRtlAdjustPrivilege64)GetProcAddress((HMODULE)(GetModuleHandle(_T("ntdll.dll"))), "RtlAdjustPrivilege");
    if (RtlAdjustPrivilege == NULL)
    {
        return FALSE;
    }
    /**
    .常量 SE_BACKUP_PRIVILEGE, "17", 公开
    .常量 SE_RESTORE_PRIVILEGE, "18", 公开
    .常量 SE_SHUTDOWN_PRIVILEGE, "19", 公开
    .常量 SE_DEBUG_PRIVILEGE, "20", 公开
    */
    return RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, 1, 0, &dwRetVal);
}

BOOL Inject(DWORD ProcessId, LPCSTR szPar)
{
    DWORD   dwOldProtect;
    CONST PVOID StartPoint = (PVOID)GetStealthStubPtr();

    EnableDebugPrivilegeX64();
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
    if (hProcess == NULL)
    {
        printf("Error :OPenProcess\n");
        return FALSE;
    }

    PVOID   funRemote = 0;
    if (!(funRemote = (PVOID)GetProcAddress(LoadLibrary(_T("kernel32.dll")), "LoadLibraryA")))//"User32.dll")),"MessageBoxA")))
    {
        printf("Error :GetProcAddress\n");
        return FALSE;
    }

    PVOID   strRemote = NULL;
    if (!(strRemote = VirtualAllocEx(hProcess, NULL, (size_t)(strlen(szPar) + 1) * sizeof(char), MEM_COMMIT, PAGE_READWRITE)))
    {
        printf("Error :VirtualAllocEx\n");
        return FALSE;
    }

    if (!WriteProcessMemory(hProcess, strRemote, szPar, (strlen(szPar) + 1) * sizeof(char), NULL))
    {
        printf("Error :WriteProcessMemory\n");
        return FALSE;
    }
    suspendInjectResume(hProcess, funRemote, strRemote);
    return TRUE;
}

int main()
{
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    PROCESSENTRY32      pe = { 0 };
    pe.dwSize = sizeof(pe);
    if (Process32First(hSnapshot, &pe)) {
        while (Process32Next(hSnapshot, &pe))
        {
            if (!_stricmp(pe.szExeFile, "showfour.exe"))
            {
                CHAR*           szDll = "\\SimpleDll.dll";
                CHAR            szFullPath[MAX_PATH] = { 0 };
                GetCurrentDirectory(MAX_PATH, szFullPath);
                strcat_s(szFullPath, szDll);
                if (Inject(pe.th32ProcessID, szFullPath))
                {
                    //ShowMessage(_T("注入成功"));
                }
                else
                {
                    printf("注入失败\n");
                }
                break;
            }
        }
    }
    else {
        printf("\t[+] Could not ProcessFirst! [%d]\n", GetLastError());
        CloseHandle(hSnapshot);
        exit(-1);
    }
    CloseHandle(hSnapshot);

    return 0;
}
  • 核心的汇编代码
.CODE
public asm_code
asm_code PROC
    push rax
    push r12
    push rcx
    pushfq


    mov r12,rsp
    and r12,15
    test r12 , r12
    jne ok
    sub rsp , 8
ok:
    sub rsp , 4 * 8 + 8
    mov         rcx, 1234567812345679h
    mov         rax, 1234567812345670h
    call        rax
    test r12 , r12
    jne next
    add rsp , 8
next:
    add rsp , 4 * 8 + 8

    popfq       ; POPFQ pops 64 bits from the stack, loads the lower 32 bits into RFLAGS, and zero extends the upper bits of RFLAGS.
    pop     rcx
    pop     r12

    sub     rsp,8
    pop     rax
    pop     rax
    mov     qword ptr [rsp-16],rax  ;因为要用rax ,将它放到栈外面的位置了

    mov     rax,1234567812345678h   ;设置返回值
    push    rax
    mov     qword ptr rax,[rsp - 8] ;恢复一下rax

    ret
    ; 硬编码 获得ASM-CODE 长度

    db 12h
    db 34h
    db 56h
    db 78h
asm_code ENDP
END

       这里需要注意的是,在调用ResumeThread 之后,线程的部分寄存器与GetThreadContext 获得到的值不一样,但是运行是没有出错的,不知道是什么原因,如果为了稳定的话,应该在外面保存整个Context 结构,在申请代码空间的时候同样的为需要存储的寄存器存储空间并将所得到的地址传递给汇编代码。这样才可以保证在执行我们的代码前后目标线程的执行环境不变。这里不给出实现了,后面介绍esayhook 的代码的时候会有这样的实现。

截图展示了线程暂停阶段与开始执行之后的区别

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值