Windows x86 x64使用SetThreadContext注入shellcode的方式加載DLL


一、前言

  注入DLL的方式有很多,在R3就有遠程線程CreateRemoteThread、SetWindowsHookEx、QueueUserApc、SetThreadContext

  在R0可以使用apc或者使用KeUserModeCallBack

  關於本文是在32位和64位下使用SetThreadContext注入DLL,32位下注入shellcode加載dll參考 創建進程時注入DLL64位下shellcode通過編寫asm匯編文件,使用windbg的attach調試獲得。

 

二、編程思路

  我們先打開目標進程,枚舉目標線程采用的是系統快照的方式,比較線程所屬的進程是否是我們的目標進程,SuspendThread掛起線程,GetThreadContext獲得eip/rip,在目標進程空間寫入Shellcode,SetThreadContext將eip/rip設置為我們shellcode的地址,shellcode執行load dll的工作,最后跳轉回之前的eip繼續執行。

  1、32位下shellcode

BYTE ShellCode[64]=
{
    0x60,
    0x9c,
    0x68,               //push
    0xaa,0xbb,0xcc,0xdd,//dll path  +3  dll最目標進程中的地址
    0xff,0x15,          //call     這里感覺有點亂,我在64下直接call 相對地址 
    0xdd,0xcc,0xbb,0xaa,//+9 LoadLibrary Addr  Addr
    0x9d,
    0x61,
    0xff,0x25,          //jmp
    0xaa,0xbb,0xcc,0xdd,// +17  jmp  eip
    0xaa,0xaa,0xaa,0xaa,// loadlibrary addr
    0xaa,0xaa,0xaa,0xaa//  jmpaddr  +25

    //  +29
}; 

  我們在代碼中對於shellcode中填充內容,這樣避免shellcode重定位的問題

    strcpy((char*)DllPath,"D:\\Dllx86.dll");//這里是要注入的DLL名字
    *(DWORD*)(ShellCode+3)=(DWORD)LpAddr+29;
    ////////////////
    *(DWORD*)(ShellCode+21)=LoadDllAAddr;   //loadlibrary地址放入shellcode中
    *(DWORD*)(ShellCode+9)=(DWORD)LpAddr+21;//修改call 之后的地址 為目標空間存放 loaddlladdr的地址
    //////////////////////////////////
    *(DWORD*)(ShellCode+25)=ctx.Eip;
    *(DWORD*)(ShellCode+17)=(DWORD)LpAddr+25;//修改jmp 之后為原來eip的地址

  最后翻譯成匯編

/*
{
00973689 >    60                PUSHAD
0097368A      9C                PUSHFD
0097368B      68 50369700       PUSH notepad.00973650
00973690      FF15 70369700     CALL DWORD PTR DS:[973670]
00973696      9D                POPFD
00973697      61                POPAD
00973698    - FF25 30369700     JMP DWORD PTR DS:[973630]
}
*/

  然后需要注意的是 操作之前要 掛起, 結束后一定要 恢復,32位比較簡單

 

  2.64位下的shellcode

BYTE ShellCode64[64]=
{
    0x48,0x83,0xEC,0x28,  // sub rsp ,28h

    0x48,0x8D,0x0d,       // [+4] lea rcx,
    0xaa,0xbb,0xcc,0xdd,  // [+7] dll path offset =  TargetAddress- Current(0x48)[+4] -7 

    0x48, 0xB8,
    0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
    0xff, 0xd0,

    0x48,0x83,0xc4,0x28,  // [+16] add rsp,28h
    //0xcc, 調試時斷下來的int 3 正常運行的時候非常傻逼的沒有請掉...難怪一直死
    0xff,0x25,            // [+20]
    0xaa,0xbb,0xcc,0xdd,  // [+22] jmp rip offset  = TargetAddress - Current(0xff)[+20] - 6

    0xaa,0xbb,0xcc,0xdd,  //+26
    0xaa,0xbb,0xcc,0xdd   
    //+34
};

 

  64位下,尋址都是相對尋址,函數調用參數傳遞是rcx,rdx,r8,r9,超過4參數采用棧傳遞

  首先需要sub rsp,28h,為5*8 = 28h ,4個寄存器+返回地址

  然后將dll名稱地址給rcx 

  在調用loadlibrary,最后跳回rip

DllPath=ShellCode64+41;
    strcpy((char*)DllPath,"Dllx64.dll");//這里是要注入的DLL名字
    DWORD DllNameOffset = 30;// ((BYTE*)LpAddr+34) -((BYTE*)LpAddr+4) -7 這個指令7個字節
    *(DWORD*)(ShellCode64+7)=(DWORD)DllNameOffset;
    ////////////////
    DWORD64 LoadDllAddroffset = (DWORD64)LoadDllAAddr;// - ((BYTE*)LpAddr + 11) -5;  //這個指令5個字節e8 + 4addroffset
    *(DWORD64*)(ShellCode64+13)=LoadDllAddroffset;
    //////////////////////////////////
    
    
    *(DWORD64*)(ShellCode64+33)=ctx.Rip; //64下為rip
    *(DWORD*)(ShellCode64+29)= (DWORD)0; //我將地址放在+29的地方,相對offset為0

  

三、完整代碼

#include "stdafx.h"

#include <iostream>
using namespace std;
#include <windows.h>
#include "tlhelp32.h"
BYTE ShellCode[64]=
{
    0x60,
    0x9c,
    0x68,               //push
    0xaa,0xbb,0xcc,0xdd,//dll path  +3  dll最目標進程中的地址
    0xff,0x15,          //call     這里感覺有點亂,我在64下直接call 相對地址 
    0xdd,0xcc,0xbb,0xaa,//+9 LoadLibrary Addr  Addr
    0x9d,
    0x61,
    0xff,0x25,          //jmp
    0xaa,0xbb,0xcc,0xdd,// +17  jmp  eip
    0xaa,0xaa,0xaa,0xaa,// loadlibrary addr
    0xaa,0xaa,0xaa,0xaa//  jmpaddr  +25

    //  +29
}; 

/*
{
00973689 >    60                PUSHAD
0097368A      9C                PUSHFD
0097368B      68 50369700       PUSH notepad.00973650
00973690      FF15 70369700     CALL DWORD PTR DS:[973670]
00973696      9D                POPFD
00973697      61                POPAD
00973698    - FF25 30369700     JMP DWORD PTR DS:[973630]
}
*/

BYTE ShellCode64[64]=
{
  0x48,0x83,0xEC,0x28, // sub rsp ,28h

 
         

  0x48,0x8D,0x0d, // [+4] lea rcx,
  0xaa,0xbb,0xcc,0xdd, // [+7] dll path offset = TargetAddress- Current(0x48)[+4] -7

 
         

  0x48, 0xB8,
  0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
  0xff, 0xd0,

 
         

  0x48,0x83,0xc4,0x28, // [+16] add rsp,28h
  //0xcc, 調試時斷下來的int 3 正常運行的時候非常傻逼的沒有請掉...難怪一直死
  0xff,0x25, // [+20]
  0xaa,0xbb,0xcc,0xdd, // [+22] jmp rip offset = TargetAddress - Current(0xff)[+20] - 6

 
         

  0xaa,0xbb,0xcc,0xdd, //+26
  0xaa,0xbb,0xcc,0xdd
  //+34
};


BOOL EnableDebugPriv() ;
BOOL StartHook(HANDLE hProcess,HANDLE hThread);

int _tmain(int argc, _TCHAR* argv[])
{

    EnableDebugPriv() ;
    int ProcessId = 0;
    cin>>ProcessId;

    HANDLE Process = OpenProcess(PROCESS_ALL_ACCESS,NULL,ProcessId);

    // 定義線程信息結構  
    THREADENTRY32 te32 = {sizeof(THREADENTRY32)} ;  
    //創建系統線程快照  ss
    HANDLE hThreadSnap = CreateToolhelp32Snapshot ( TH32CS_SNAPTHREAD, 0 ) ;  
    if ( hThreadSnap == INVALID_HANDLE_VALUE )  
        return FALSE ;  

    // 循環枚舉線程信息  
    if ( Thread32First ( hThreadSnap, &te32 ) )  
    {  
        do{  

            if(te32.th32OwnerProcessID == ProcessId)
            {
                HANDLE Thread = OpenThread(THREAD_ALL_ACCESS,NULL,te32.th32ThreadID);

                SuspendThread(Thread);
                if (!StartHook(Process,Thread))
                {
                    TerminateProcess(Process,0);
                    printf("失敗\n");
                    getchar();
                    return 0;
                }
                CloseHandle(Process);
                CloseHandle(Thread);


            }
            
        }while ( Thread32Next ( hThreadSnap, &te32 ) ) ;  
    }  

    CloseHandle ( hThreadSnap ) ;  
}





BYTE *DllPath;
BOOL StartHook(HANDLE hProcess,HANDLE hThread)
{

#ifdef _WIN64 

    CONTEXT ctx;
    ctx.ContextFlags=CONTEXT_ALL;
    if (!GetThreadContext(hThread,&ctx))
    {
        printf("GetThreadContext Error\n");
        return FALSE;
    }
    LPVOID LpAddr=VirtualAllocEx(hProcess,NULL,64,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    if (LpAddr==NULL)
    {
        printf("VirtualAlloc Error\n");
        return FALSE;
    }
    DWORD64 LoadDllAAddr=(DWORD64)GetProcAddress(GetModuleHandle(L"kernel32.dll"),"LoadLibraryA");
    if (LoadDllAAddr==NULL)
    {
        printf("LoadDllAddr error\n");
        return FALSE;
    }
    /*

    0x48,0x83,0xEC,0x28,  //sub rsp ,28h

    0x48,0x8D,0x0d,       // [+4] lea rcx,
    0xaa,0xbb,0xcc,0xdd,  // [+7] dll path offset =  TargetAddress- Current(0x48)[+4] -7 


    0xe8,                 // [+11]
    0xdd,0xcc,0xbb,0xaa,  // [+12] call LoadLibrary offset = TargetAddress - Current(0xe8)[+11] -5

    0x48,0x83,0xc4,0x28,  // [+16] add rsp,28h

    0xff,0x25,            // [+20]
    0xaa,0xbb,0xcc,0xdd,  // [+22] jmp rip offset  = TargetAddress - Current(0xff)[+20] - 6

    0xaa,0xbb,0xcc,0xdd,  //+26
    0xaa,0xbb,0xcc,0xdd   
    //+34
    */

  DllPath=ShellCode64+41;
  strcpy((char*)DllPath,"Dllx64.dll");//這里是要注入的DLL名字
  DWORD DllNameOffset = 30;// ((BYTE*)LpAddr+34) -((BYTE*)LpAddr+4) -7 這個指令7個字節
  *(DWORD*)(ShellCode64+7)=(DWORD)DllNameOffset;
  ////////////////
  DWORD64 LoadDllAddroffset = (DWORD64)LoadDllAAddr;// - ((BYTE*)LpAddr + 11) -5; //這個指令5個字節e8 + 4addroffset
  *(DWORD64*)(ShellCode64+13)=LoadDllAddroffset;
  //////////////////////////////////


  *(DWORD64*)(ShellCode64+33)=ctx.Rip; //64下為rip
  *(DWORD*)(ShellCode64+29)= (DWORD)0; //我將地址放在+29的地方,相對offset為0

//  這里因為這樣寫跳轉不到目標地址,故x64 應該要中轉一次  相對尋址
//     DWORD Ds = (DWORD)ctx.SegDs;
//     DWORD RipOffset = (BYTE*)ctx.Rip - ((BYTE*)LpAddr+20) -6;
//     *(DWORD*)(ShellCode64+22)=(DWORD)ctx.Rip;

    ////////////////////////////////////
    if (!WriteProcessMemory(hProcess,LpAddr,ShellCode64,64,NULL))
    {
        printf("write Process Error\n");
        return FALSE;
    }
    ctx.Rip=(DWORD64)LpAddr;
    if (!SetThreadContext(hThread,&ctx))
    {
        printf("set thread context error\n");
        return FALSE;
    }
    ResumeThread(hThread);
    return TRUE;
    
#else
    CONTEXT ctx = {0};
    ctx.ContextFlags=CONTEXT_ALL;
    if (!GetThreadContext(hThread,&ctx))
    {
        int a = GetLastError();
        printf("GetThreadContext Error\n");
        return FALSE;
    }
    LPVOID LpAddr=VirtualAllocEx(hProcess,NULL,64,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
    if (LpAddr==NULL)
    {
        printf("VirtualAlloc Error\n");
        return FALSE;
    }
    DWORD LoadDllAAddr=(DWORD)GetProcAddress(GetModuleHandle(L"kernel32.dll"),"LoadLibraryA");
    if (LoadDllAAddr==NULL)
    {
        printf("LoadDllAddr error\n");
        return FALSE;
    }

    /////////////
    /*
    0x60,              PUSHAD
    0x9c,              PUSHFD
    0x68,              PUSH 
    0xaa,0xbb,0xcc,0xdd,//dll path  address  
    0xff,0x15,            CALL
    0xdd,0xcc,0xbb,0xaa,  offset  
    0x9d,                  POPFD
    0x61,                  POPAD
    0xff,0x25,             JMP 
    0xaa,0xbb,0xcc,0xdd,//  [xxxxx]
    0xaa,0xaa,0xaa,0xaa,// LoadLibrary Address
    0xaa,0xaa,0xaa,0xaa//  恢復的EIP  Address  
                         // +29  Dll名字
    */
    _asm mov esp,esp
    DllPath=ShellCode+29;
    strcpy((char*)DllPath,"D:\\Dllx86.dll");//這里是要注入的DLL名字
    *(DWORD*)(ShellCode+3)=(DWORD)LpAddr+29;
    ////////////////
    *(DWORD*)(ShellCode+21)=LoadDllAAddr;   //loadlibrary地址放入shellcode中
    *(DWORD*)(ShellCode+9)=(DWORD)LpAddr+21;//修改call 之后的地址 為目標空間存放 loaddlladdr的地址
    //////////////////////////////////
    *(DWORD*)(ShellCode+25)=ctx.Eip;
    *(DWORD*)(ShellCode+17)=(DWORD)LpAddr+25;//修改jmp 之后為原來eip的地址
    ////////////////////////////////////
    if (!WriteProcessMemory(hProcess,LpAddr,ShellCode,64,NULL))
    {
        printf("write Process Error\n");
        return FALSE;
    }
    ctx.Eip=(DWORD)LpAddr;
    if (!SetThreadContext(hThread,&ctx))
    {
        printf("set thread context error\n");
        return FALSE;
    }
    ResumeThread(hThread);
    return TRUE;
#endif
    
};




BOOL EnableDebugPriv() 
{
    HANDLE   hToken; 
    LUID   sedebugnameValue; 
    TOKEN_PRIVILEGES   tkp;
    if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken)) 
    { 
        return   FALSE; 
    } 

    if(!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&sedebugnameValue)) 
    { 
        CloseHandle(hToken); 
        return   FALSE; 
    } 
    tkp.PrivilegeCount   =   1; 
    tkp.Privileges[0].Luid   =   sedebugnameValue; 
    tkp.Privileges[0].Attributes   =   SE_PRIVILEGE_ENABLED; 

    if(!AdjustTokenPrivileges(hToken,FALSE,&tkp,sizeof(tkp),NULL,NULL)) 
    { 
        return   FALSE; 
    }   
    CloseHandle(hToken); 
    return TRUE;

} 

 

 四、64位Shellcode的編寫過程

  1.編寫內聯匯編

IncludeLib User32.Lib
;導入定義
EXTERN LoadLibraryA:PROC

;初始化數據定義
.DATA
szPath     BYTE  "D:\\Dll.dll",0

.CODE
  FUNC PROC 
  sub rsp,28H     ;分配堆棧,四個參數+返回值,十進制40(5*8)為16進制28H
  lea rcx,szPath  ;消息文本 
  call LoadLibraryA ;調用消息函數
  add rsp,28H      ;平衡堆棧,四個參數+返回值,十進制40為16進制28H 
  ret
  FUNC ENDP
 END
// HelloPE.cpp : 定義控制台應用程序的入口點。
//

#include "stdafx.h"
#include<windows.h>

extern "C" int _cdecl FUNC();

int _tmain(int argc, _TCHAR* argv[])
{
    
    //LoadLibraryA("D:\\Dll.dll");
    int a  = 3;
    int b  = 4;
    int c = FUNC();
    printf("%d",c);
    return 0;
}

  

  2.使用Windbg中Attach到程序,查看對應的機器碼

HelloPE!FUNC:
00000001`3f5a1090 4883ec28        sub     rsp,28h
00000001`3f5a1094 488d0d877f0000  lea     rcx,[HelloPE!szPath (00000001`3f5a9022)]
00000001`3f5a109b e80a000000      call    HelloPE!LoadLibraryA (00000001`3f5a10aa)
00000001`3f5a10a0 4883c428        add     rsp,28h
00000001`3f5a10a4 c3              ret
00000001`3f5a10a5 cc              int     3
00000001`3f5a10a6 cc              int     3
00000001`3f5a10a7 cc              int     3
00000001`3f5a10a8 cc              int     3
00000001`3f5a10a9 cc              int     3


HelloPE!LoadLibraryA:
00000001`3f5a10aa ff25d8a20000    jmp     qword ptr [HelloPE!_imp_LoadLibraryA (00000001`3f5ab388)]

 

  3.根據字節碼,編寫shellcode

BYTE ShellCode64[64]=
{
    0x48,0x83,0xEC,0x28,  // sub rsp ,28h

    0x48,0x8D,0x0d,       // [+4] lea rcx,
    0xaa,0xbb,0xcc,0xdd,  // [+7] dll path offset =  TargetAddress- Current(0x48)[+4] -7 

    0xe8,                 // [+11]
    0xdd,0xcc,0xbb,0xaa,  // [+12] call LoadLibrary offset = TargetAddress - Current(0xe8)[+11] -5

    0x48,0x83,0xc4,0x28,  // [+16] add rsp,28h
    //0xcc, 調試時斷下來的int 3 正常運行的時候非常傻逼的沒有清掉...難怪一直死
    0xff,0x25,            // [+20]
    0xaa,0xbb,0xcc,0xdd,  // [+22] jmp rip offset  = TargetAddress - Current(0xff)[+20] - 6

    0xaa,0xbb,0xcc,0xdd,  //+26
    0xaa,0xbb,0xcc,0xdd   
    //+34
};

 

  代碼下載 :InjectDllBySetThreadContextx64.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM