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

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

  • VirtualAllocEx()

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

  • WriteProcessMemory()

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

  • CreateRemoteThread()

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

0x01 代码实现

1.提升进程为DEBUG权限

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

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路径名字到目标进程的内存

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函数地址(两程序中地址一样).

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函数地址传进去,留意一下参数

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类型。第五个为写入目标进程参数的地址。代码如下:

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

  • 测试工具为火绒剑

  • Mydll功能就一个弹窗

0x03 完整代码

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

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