这里我们要用到上篇中的那个 msg.dll
下面的代码还是使用的统一的宽字符版本,已经在VS2010下编译通过。
首先上代码,然后慢慢解释:
#include <windows.h>
#include <TlHelp32.h>
BOOL LoadRemoteDll(DWORD dwProcessId, LPTSTR lpszLibName)
{
BOOL bResult = FALSE;
HANDLE hProcess = NULL;
HANDLE hThread = NULL;
PSTR pszLibFileRemote = NULL;
DWORD cch;
PTHREAD_START_ROUTINE pfnThreadRrn;
__try
{
//获得想要注入代码的进程的句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
__leave;
//计算DLL路径名需要的字节数
cch = 2 * (1 + lstrlen(lpszLibName));
//在远程线程中为路径名分配空间
pszLibFileRemote = (PSTR)VirtualAllocEx(hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE);
if (pszLibFileRemote == NULL)
__leave;
//将DLL的路径名复制到远程进程的地址空间
if (!WriteProcessMemory(hProcess, (PVOID)pszLibFileRemote, (PVOID)lpszLibName, cch, NULL))
__leave;
//获得LoadLibraryA在Kernel.dll中得真正地址
pfnThreadRrn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
if (pfnThreadRrn == NULL)
__leave;
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRrn, (PVOID)pszLibFileRemote, 0, NULL);
if (hThread == NULL)
__leave;
//等待远程线程终止
WaitForSingleObject(hThread, INFINITE);
bResult = TRUE;
}
__finally
{
//关闭句柄
if (pszLibFileRemote != NULL)
VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);
if (hThread != NULL)
CloseHandle(hThread);
if (hProcess != NULL)
CloseHandle(hProcess);
}
return bResult;
}
BOOL GetProcessIdByName(LPWSTR szProcessName, LPDWORD lpPID)
{
//变量及其初始化
STARTUPINFO st;
PROCESS_INFORMATION pi;
PROCESSENTRY32 ps;
HANDLE hSnapshot;
ZeroMemory(&st, sizeof(STARTUPINFO));
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
st.cb = sizeof(STARTUPINFO);
ZeroMemory(&ps, sizeof(PROCESSENTRY32));
ps.dwSize = sizeof(PROCESSENTRY32);
//遍历进程
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE)
return FALSE;
if (!Process32First(hSnapshot, &ps))
return FALSE;
do
{
//比较进程名
if (lstrcmpi(ps.szExeFile, TEXT("calc.exe")) == 0)
{
//找到了
*lpPID = ps.th32ProcessID;
CloseHandle(hSnapshot);
return TRUE;
}
}
while (Process32Next(hSnapshot, &ps));
//没有找到
CloseHandle(hSnapshot);
return FALSE;
}
//修改进程权限
BOOL EnablePrivilege(LPWSTR name)
{
HANDLE hToken;
BOOL rv;
TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}};
LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid);
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
AdjustTokenPrivileges(hToken, FALSE, &priv, sizeof priv, 0, 0);
rv = GetLastError() == ERROR_SUCCESS;
CloseHandle(hToken);
return rv;
}
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
DWORD dwPID;
//提权,获取SE_DEBUG_NAME 权限
//可以在其他进程的内存空间中写入,创建线程
if (0 == EnablePrivilege(SE_DEBUG_NAME))
return 0;
if (!GetProcessIdByName(TEXT("calc.exe"), &dwPID))
return 0;
//通过上传远程线程加载dll
//将msg.dll放置在系统目录下
if (!LoadRemoteDll(dwPID, TEXT("msg.dll")))
return 0;
return 1;
}
把上次的dll放到相对目录下面或者是扔到system32下面。
我们首先从main函数开始吧:
主要是由三个部分组成的:
1.提取权限
//提权,获取SE_DEBUG_NAME 权限
//可以在其他进程的内存空间中写入,创建线程
if (0 == EnablePrivilege(SE_DEBUG_NAME))
return 0;
2.遍历搜索进程UID
if (!GetProcessIdByName(TEXT("calc.exe"), &dwPID))
return 0;
3.创建远程线程
//通过上传远程线程加载dll
//将msg.dll放置在系统目录下
if (!LoadRemoteDll(dwPID, TEXT("msg.dll")))
return 0;
前两个步骤我们就不具体讲了,不是我们的重点。关键是其实对于进程令牌Token自己也不是很理解,不好意思给大家讲。。。
关键我们还是来看看第三个步骤好了。
创建远程线程句柄有下面几个步骤:
1.获得想要注入代码的进程的句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
2.计算DLL路径名需要的字节数
cch = 2 * (1 + lstrlen(lpszLibName));
这里自己对宽字符的处理没有做好
可以使用wcslen函数
3.在远程线程中为路径名分配空间
pszLibFileRemote = (PSTR)VirtualAllocEx(hProcess, NULL, cch, MEM_COMMIT, PAGE_READWRITE);
4.将DLL的路径名复制到远程进程的地址空间
WriteProcessMemory(hProcess, (PVOID)pszLibFileRemote, (PVOID)lpszLibName, cch, NULL);
5.获得LoadLibraryA在Kernel.dll中得真正地址
pfnThreadRrn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
6.最后是最最关键的函数
hThread = CreateRemoteThread(hProcess, NULL, 0, pfnThreadRrn, (PVOID)pszLibFileRemote, 0, NULL);
7.当然还有扫尾的工作
//关闭句柄
if (pszLibFileRemote != NULL)
VirtualFreeEx(hProcess, (PVOID)pszLibFileRemote, 0, MEM_RELEASE);
if (hThread != NULL)
CloseHandle(hThread);
if (hProcess != NULL)
CloseHandle(hProcess);
运行一下,发现成功了!~~~
最后栋哥跟我提醒了几点:
1.关于进程令牌的代码,有时候不写也是可以通过的。这里还有待研究一下。
2.被注入的程序有时候要放在相对目录下才会产生效果。关于这点自己还没有实践过。有待验证。
3.像explorer.exe有时候会无法注入。换个简单的calc.exe完全可以。火狐,qq全部都可以。可能是explorer.exe有点特别。
4.第一次远程注入可以成功,第二次就不行了,就不能弹出对话框了,貌似进程打开之后马上就关闭了。个人以为原因可能处在dll的fdwReason。有时间尝试着调试一下。
自己改了一下,应该是这样的。上面的代码里面没有FreeLibrary,那么dll还是在远程地址空间里面。第二次想要注入的时候是不会发送DLL_PROCESS_ATTACH 消息的。
DLL_PROCESS_ATTACH消息的发送是这样的:
线程调用LoadLibrary --> Dll是否已经被映射到了进程的地址空间中 (如果是) --> 递增DLL的使用计数 --> 使用计数器是否等于1 (如果是) --> 调用DLL的DllMain并且传入DLL_PROCESS_ATTACH
所以说,在Dll已经被映射到了进程地址空间之后,只不过DLL的使用计数增加了,如果是2,那么很明显就不会调用DLL的DllMain并且传入DLL_PROCESS_ATTACH
但是如果我把dll改一下,把弹框代码写在DLL_THREAD_ATTACH里面,那么每次创建线程的时候就都会收到DLL_THREAD_ATTACH消息了。
关于远程线程注入就先讲到这里。当然这些都是最简单的最基本的东西。
自己有时间去看看核心编程,顺便进一步了解下操作系统。