Dll注入-远线程注入

此篇实例来自于《逆向工程核心原理》一书,与《windows核心编程》等类似书籍上的例子相差无几,但是更为简洁,因为作为了解重点代码以备用来调试相关代码使用,因此删掉了不必要的代码

  1. 先贴源代码

#include "stdafx.h"
#include "windows.h"
#include "tchar.h"
BOOL InjectDll(DWORD dwPID,LPCTSTR szDllPath)
{
    HANDLE hProcess = NULL,hThread = NULL;
    HMODULE hMod = NULL;
    LPVOID pRemoteBuf = NULL;
    DWORD dwBufSize = (DWORD)(_tcslen(szDllPath)+1)*sizeof(TCHAR);
    LPTHREAD_START_ROUTINE pThreadProc;
    // 1 获取目标进程的句柄
    hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID);

    // 2 在目标进程中分配要注入的dll名字字符串+1的缓冲区
    pRemoteBuf = VirtualAllocEx(hProcess,NULL,dwBufSize,MEM_COMMIT,PAGE_READWRITE);

    // 3 将要注入的dll路径写入分配的内存
    WriteProcessMemory(hProcess,pRemoteBuf,(LPVOID)szDllPath,dwBufSize,NULL);

    // 4 获取LoadLibraryW()API的地址
    //kernel32.dll应该是目标进程的dll路径地址,
    //然而由于所有进程中的内核dll地址相同故不用单独给路径
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod,"LoadLibraryW");

    // 5 目标进程中创建远线程
    hThread = CreateRemoteThread(hProcess,NULL,0,
        pThreadProc,   //lpStartAddress
        pRemoteBuf,    //lpParameter
        0,NULL);

    //结束代码并非真正需要达到目的之后亦可不管
    WaitForSingleObject(hThread,INFINITE);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return TRUE;

}


int _tmain(int argc, _TCHAR* argv[])
{
    //此句也可以不要,因我对argc这个参数不熟悉 故写上它表示参数个数,argv表示参数数组
    if(argc!=3)
    {
        _tprintf(L"usage: %s pid dll_path\n",argv[0]);
        return 1;
    }
    //strol(把字符串转成长整形),_ tstol是宏替换的一种形式
    //就像_tcslen是一个宏,当定义了_UNICODE时,它被解释为wcslen,
    //如果没有定义_UNICODE时,它被解释为strlen。这是一个好的编程风格是代码的适应能力提高不少
    if(InjectDll((DWORD)_tstol(argv[1]),argv[2]))
    printf("success");
    else
        printf("failed");
    return 0;
}

2 思路简述

(1). 关键函数就一个

 CreateRemoteThread(
 hProcess,NULL,0,
***pThreadProc,   //lpStartAddress
pRemoteBuf,    //lpParameter***
        0,NULL);

(2)为了填充上面参数lpStartAddress同时因为Dll注入的目的,不得不获取LoadLibrary这个函数的地址,为了获取LoadLibrary这个函数的地址于是有了

hMod = GetModuleHandle(L"kernel32.dll");
pThreadProc =(LPTHREAD_START_ROUTINE)GetProcAddress(hMod,"LoadLibraryW");

(3)为了填充lpParameter这个参数,也就是传递给lpStartAddress(LoadLibrary的地址)的参数,又需要一个DLL(就是我们要注入的Dll)的全路径字符串,按道理自己定义一个字符串变量把地址指针拿过来用就行了,但是此处不行,因为我们的进程空间(也就是CreateRemoteThread函数所在进程空间)与我们要注入的进程空间不是同一个,它们的虚拟内存不能通用,于是就有了

// 2 在目标进程中分配要注入的dll名字字符串+1的缓冲区
pRemoteBuf = VirtualAllocEx(hProcess,NULL,dwBufSize,MEM_COMMIT,PAGE_READWRITE);
// 3 将要注入的dll路径写入分配的内存
  WriteProcessMemory(hProcess,pRemoteBuf,(LPVOID)szDllPath,dwBufSize,NULL);

这里补充一下,其实kernel32.dll、LoadLibrary这两字符串的地址也应该从目标进程中获取,但是此处就是没有,查资料发现原来这两地址位于内核区,对所有进程来说地址都是一样额,所以无所谓哪个进程空间
(4)同理,为了填充上面hProcess这个参数于是有了

// 1 获取目标进程的句柄
    hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPID);

(5)到此,全部必要函数填充完毕,剩下的参数需要的变量,也按照此方法去凑,另外如果为了写出稳定的代码,还得给每个函数后面加上试错代码,如GetLastError()

3.小结
照搬过来的代码,看似鸡肋,但是抄完自我感觉理解更深了一步
首先,对自下到上的编程思想又有了一次新的体会(如上所说的目的式编程思想,先找的达到目的的关键函数,再一个个去填充为它服务的函数、变量和数据结构)。
其次,本程序段在实际调试中遇到了一些问题,反复琢磨不得解,先把它放这里留待以后再来完善:
(1)该程序在WIN XP 32位系统上运行时,注入第一个DLL通过,但是在注入第一个dll成功的情况下再注入第二个dll失败,试想难道是系统对注入DLL有限制,还是Dll本身功能上排它性,此处没贴dll代码,我完全用的《逆向工程》23章中的myhanck.dll和myhack2.dll
(2)在使用myhack2.dll注入一次之后发现每次打开同样的程序的时候,myhack2.dll都自动注入了,检查了dll的内容发现有创建进程,但是我已经把进程关闭,并重启被注入过的进程,发现任然留在其中,不得解

以上问题因为目前对我目前工作还没有必要帮助,于是先放这留待后解,也希望看到的朋友不吝指教
最后附件本文编译好的exe以及Dll文件:
DLL远线程注入exe、Dll

2补充说明 (windows内核6.0DLL 注入方法)

上面的方法主要通过CreateRemoteThread创建远线程,此方法在内核6.0(windows Vista、7、8)以前是完全没有问题的,但是6.0以后注入系统进程就会创建失败(如图注入svchost.exe)
这里写图片描述
这是因为内核6.0以后的会话隔离机制造成的,它在创建一个进程之后并不立即运行,而是先挂起进程,在查看要运行的进程所在的会话层之后再决定是否恢复进程运行,而CreateRemoteThread在被发现向标志为系统会话的进程注入Dll的时候就会失败
这里写图片描述
为何出现这个情况,是因为CreateRemoteThread在不同内核版本的调用方式不同
这里写图片描述

从上图能够看出CreateRemoteThread函数在两种内核版本的调用方式的实际区别就是一个在最终调用ZwCreateThread,一个调用ZwCreateThreadEx,进过百度发现这两个API只有第七个参数 CreateSuspended不同,直接调用ZwCreateThreadEx时 CreateSuspended参数值为FALSEE(0),而在CreateRemoteThread内部调用时,CreateSuspended参数值为TRUE(1),导致线程创建完成后一直挂起无法恢复运行,这就是为什么DLL注入失败的原因。

typedef NTSTATUS (WINAPI *LPFUN_NtCreateThreadEx)
(
OUT PHANDLE hThread,
IN ACCESS_MASK DesiredAccess,
IN LPVOID ObjectAttributes,
IN HANDLE ProcessHandle,
IN LPTHREAD_START_ROUTINE lpStartAddress,
IN LPVOID lpParameter,
IN BOOL CreateSuspended,
IN ULONG StackZeroBits,
IN ULONG SizeOfStackCommit,
IN ULONG SizeOfStackReserve,
OUT LPVOID lpBytesBuffer
);
引用地址:CreateRemoteThread之梗
typedef NTSTATUS (NTAPI *_ZwCreateThreadEx)(
OUT PHANDLE ThreadHandle,
IN ACCESS_MASK DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN HANDLE ProcessHandle,
IN PTHREAD_START_ROUTINE StartRoutine,
IN PVOID StartContext,
IN ULONG CreateThreadFlags,
IN SIZE_T ZeroBits OPTIONAL,
IN SIZE_T StackSize OPTIONAL,
IN SIZE_T MaximumStackSize OPTIONAL,
IN PPROC_THREAD_ATTRIBUTE_LIST AttributeList
);
引用地址:线程API:ZwCreateThreadEx的一些特殊用法

手动调试使之注入成功的两种方法:
方法一(修改参数CreateSuspended):
1、下断CreateRemoteThread,逐步找到ZwCreateThreadEx再次下断,然后找到它的第七个参数CreateSuspended手动修改0,继续运行会注入成功。
这里写图片描述
方法二(操纵条件分支):
在ZwCreateThreadEx函数调用之后不远处会发现ntdll.CsrclientCallServer函数的调用后面会紧跟一跳转指令JL,它会决定是否调用线程恢复函数zwResumeThread,修改跳转即可使zwResumeThread成功调用,即成功创建远线程。
这里写图片描述
思路将完,为了达到自动化注入的目的还得编程实现,下面贴上源代码:InjectDll

#include "windows.h"
#include "stdio.h"
#include "tchar.h"

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
                          &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,             // lookup privilege on local system
                              lpszPrivilege,    // privilege to lookup 
                              &luid) )          // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

typedef DWORD (WINAPI *PFNTCREATETHREADEX)
( 
    PHANDLE                 ThreadHandle,   
    ACCESS_MASK             DesiredAccess,  
    LPVOID                  ObjectAttributes,   
    HANDLE                  ProcessHandle,  
    LPTHREAD_START_ROUTINE  lpStartAddress, 
    LPVOID                  lpParameter,    
    BOOL                    CreateSuspended,    
    DWORD                   dwStackSize,    
    DWORD                   dw1, 
    DWORD                   dw2, 
    LPVOID                  Unknown 
); 

BOOL IsVistaOrLater()
{
    OSVERSIONINFO osvi;

    ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

    GetVersionEx(&osvi);

    if( osvi.dwMajorVersion >= 6 )
        return TRUE;

    return FALSE;
}

BOOL MyCreateRemoteThread(HANDLE hProcess, LPTHREAD_START_ROUTINE pThreadProc, LPVOID pRemoteBuf)
{
    HANDLE      hThread = NULL;
    FARPROC     pFunc = NULL;

    if( IsVistaOrLater() )    // Vista, 7, Server2008
    {
        pFunc = GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtCreateThreadEx");
        if( pFunc == NULL )
        {
            printf("MyCreateRemoteThread() : GetProcAddress(\"NtCreateThreadEx\") failed!!! [%d]\n",
                   GetLastError());
            return FALSE;
        }

        ((PFNTCREATETHREADEX)pFunc)(&hThread,
                                    0x1FFFFF,
                                    NULL,
                                    hProcess,
                                    pThreadProc,
                                    pRemoteBuf,
                                    FALSE,
                                    NULL,
                                    NULL,
                                    NULL,
                                    NULL);
        if( hThread == NULL )
        {
            printf("MyCreateRemoteThread() : NtCreateThreadEx() failed!!! [%d]\n", GetLastError());
            return FALSE;
        }
    }
    else                    // 2000, XP, Server2003
    {
        hThread = CreateRemoteThread(hProcess, 
                                     NULL, 
                                     0, 
                                     pThreadProc, 
                                     pRemoteBuf, 
                                     0, 
                                     NULL);
        if( hThread == NULL )
        {
            printf("MyCreateRemoteThread() : CreateRemoteThread() failed!!! [%d]\n", GetLastError());
            return FALSE;
        }
    }

    if( WAIT_FAILED == WaitForSingleObject(hThread, INFINITE) )
    {
        printf("MyCreateRemoteThread() : WaitForSingleObject() failed!!! [%d]\n", GetLastError());
        return FALSE;
    }

    return TRUE;
}

BOOL InjectDll(DWORD dwPID, char *szDllName)
{
    HANDLE hProcess = NULL;
    LPVOID pRemoteBuf = NULL;
    FARPROC pThreadProc = NULL;
    DWORD dwBufSize = strlen(szDllName)+1;

    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        printf("[ERROR] OpenProcess(%d) failed!!! [%d]\n", 
        dwPID, GetLastError());
        return FALSE;
    }

    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, 
                                MEM_COMMIT, PAGE_READWRITE);

    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, 
                       dwBufSize, NULL);

    pThreadProc = GetProcAddress(GetModuleHandle(L"kernel32.dll"), 
                                 "LoadLibraryA");

    if( !MyCreateRemoteThread(hProcess, (LPTHREAD_START_ROUTINE)pThreadProc, pRemoteBuf) )
    {
        printf("[ERROR] MyCreateRemoteThread() failed!!!\n");
        return FALSE;
    }

    VirtualFreeEx(hProcess, pRemoteBuf, 0, MEM_RELEASE);

    CloseHandle(hProcess);

    return TRUE;
}

int main(int argc, char *argv[])
{
    // adjust privilege
    SetPrivilege(SE_DEBUG_NAME, TRUE);

    // InjectDll.exe <PID> <dll_path>
    if( argc != 3 )
    {
        printf("usage : %s <PID> <dll_path>\n", argv[0]);
        return 1;
    }

    if( !InjectDll((DWORD)atoi(argv[1]), argv[2]) )
    {
        printf("InjectDll() failed!!!\n");
        return 1;
    }

    printf("InjectDll() succeeded!!!\n");

    return 0;
}

注入主函数与之前一样,就不用再重复,这里DLL注入与之前的区别其实也就是先判断系统版本,如果为windows Vista版本以上就调用NtCreateThreadEx而非CreateRemoteThread,此处应该注意的是NtCreateThreadEx是未公开API可能会不稳定,编程时为了稳定应该尽量避免使用。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值