上一次做了个双进程保护。后来试着做了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-函数、接口、编程实例》