上一次做了个双进程保护。后来试着做了DLL远程注入。可行性倒是没问题。问题在于,最理想的进程是注入在explorer里面,但是在WIN7 64位的系统注入不了。getlasterror()返回5.拒绝访问。不知道要怎么去解决。
远程注入大致就几个步骤,首先。记得提升权限。
DWORD EnablePrivilege (PCSTR name) { HANDLE hToken; BOOL rv; //设置结构 TOKEN_PRIVILEGES priv = { 1, {0, 0, SE_PRIVILEGE_ENABLED} }; // 查找权限值 LookupPrivilegeValue ( 0, name, &priv.Privileges[0].Luid ); // 打开本进程Token OpenProcessToken( GetCurrentProcess (), TOKEN_ADJUST_PRIVILEGES, &hToken ); // 提权 AdjustTokenPrivileges ( hToken, FALSE, &priv, sizeof priv, 0, 0 ); // 返回值,错误信息,如果操作成功,则应为ERROR_SUCCESS,为O rv = GetLastError(); // 关闭Token CloseHandle (hToken); return rv; }
提升权限之后我们就可以开始注入了,先获取目标进程ID。之后再由ID来获得句柄。
BOOL GetProcessIdByName(LPSTR 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,"explorer.exe")==0) { // 找到了 *lpPID = ps.th32ProcessID; CloseHandle(hSnapshot); return TRUE; } } while(Process32Next(hSnapshot,&ps)); // 没有找到 CloseHandle(hSnapshot); return FALSE; }
上面这个函数就是由进程名而获取句柄咯。
之后就开始正式工作。你要在人家的进程里面运行线程加载DLL模块,总要给人家一个地方去放DLL的路径不是?不然都不知道在哪里,怎么去运行。所以要计算DLL路径名的字节数。
然后在开辟空间。开辟的空间比计算的字节数要大一。还有点要注意,DLL的路径最好是用绝对路径。总之我只用绝对路径才成功。
开辟空间的函数
pszLibFileRemote = (PSTR)VirtualAllocEx(
hProcess, //申请内存所在的进程句柄
NULL, //保留页面的内存地址;一般用NULL自动分配
cch, //欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
MEM_COMMIT,
PAGE_READWRITE
);
hProcess, //申请内存所在的进程句柄
NULL, //保留页面的内存地址;一般用NULL自动分配
cch, //欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍
MEM_COMMIT,
PAGE_READWRITE
);
开辟空间后,就把DLL路径写进去。
WriteProcessMemory(
hProcess,//由OpenProcess返回的进程句柄。如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程
(PVOID)pszLibFileRemote, //要写的内存首地址.再写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。
(PVOID)lpszLibName, //指向要写的DLL的路径的指针。
cch, //要写入的字节数。
NULL)
hProcess,//由OpenProcess返回的进程句柄。如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程
(PVOID)pszLibFileRemote, //要写的内存首地址.再写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。
(PVOID)lpszLibName, //指向要写的DLL的路径的指针。
cch, //要写入的字节数。
NULL)
然后在目标进程中通过函数LoadLibraryA来加载我们的DLL。LoadLibraryA函数在Kerne132.dll里面。通过显示调用,获取真实地址,然后用函数CreateRemoteThread创建远程线程,调用DLL。
CreateRemoteThread(
hProcess, //目标进程句柄.
NULL, //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性.
0,//线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
pfnThreadRtn, //在远程进程的地址空间中,该线程的线程函数的起始地址.
(PVOID)pszLibFileRemote, //传给线程函数的参数.
0, //线程的创建标志.
NULL
)
hProcess, //目标进程句柄.
NULL, //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性.
0,//线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
pfnThreadRtn, //在远程进程的地址空间中,该线程的线程函数的起始地址.
(PVOID)pszLibFileRemote, //传给线程函数的参数.
0, //线程的创建标志.
NULL
)
至于怎么去获得DLL路径。
int num;
string x(szPath);
num = x.length();
x.replace(num - strlen("remote.exe"),num,"远程DLL.dll");
LPTSTR lp=const_cast<char*>(x.c_str());
string x(szPath);
num = x.length();
x.replace(num - strlen("remote.exe"),num,"远程DLL.dll");
LPTSTR lp=const_cast<char*>(x.c_str());
其中remote.exe就是本进程名,远程DLL.dll就是我们的dll名。这样只要DLL和本身进程在一个目录。就能正确读取。
BOOL LoadRometeDll(DWORD dwProcessId, LPTSTR lpszLibName) { BOOL bResult = FALSE; HANDLE hProcess = NULL; HANDLE hThread = NULL; PSTR pszLibFileRemote = NULL; DWORD cch; PTHREAD_START_ROUTINE pfnThreadRtn; __try { // 获得想要注入代码的进程的句柄. hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, dwProcessId ); if (hProcess == NULL) __leave; // 计算DLL路径名需要的字节数. cch = 1 + lstrlen(lpszLibName); // 在远程线程中为路径名分配空间. pszLibFileRemote = (PSTR)VirtualAllocEx( hProcess, //申请内存所在的进程句柄 NULL, //保留页面的内存地址;一般用NULL自动分配 cch, //欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 MEM_COMMIT, PAGE_READWRITE ); if (pszLibFileRemote == NULL) __leave; // 将DLL的路径名复制到远程进程的内存空间. if (!WriteProcessMemory( hProcess,//由OpenProcess返回的进程句柄。如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程 (PVOID)pszLibFileRemote, //要写的内存首地址.再写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据。 (PVOID)lpszLibName, //指向要写的DLL的路径的指针。 cch, //要写入的字节数。 NULL)) __leave; // 获得LoadLibraryA在Kernel32.dll中的真正地址. pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress( GetModuleHandle(TEXT("Kernel32")), TEXT("LoadLibraryA")); if (pfnThreadRtn == NULL) __leave; // 创建远程线程,并通过远程线程调用用户的DLL文件. hThread = CreateRemoteThread( hProcess, //目标进程句柄. NULL, //一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结指定了线程的安全属性. 0,//线程初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小. pfnThreadRtn, //在远程进程的地址空间中,该线程的线程函数的起始地址. (PVOID)pszLibFileRemote, //传给线程函数的参数. 0, //线程的创建标志. NULL ); if (hThread == NULL) { int a = GetLastError(); cout<<"error = "<<a<<endl; __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; }
这是效果图。。可是看出,对话框是由QQ弹出。
DLL代码就不贴了。主要DLL里面要写线程,不要直接写函数。否则注入的进程直接会挂掉。
本文代码参考自《精通Windows.API-函数、接口、编程实例》