/* ************************************
* remote.c
* 创建远程线程、将代码注入到其他进程中执行
**************************************/
/* 头文件 */
#include <windows.h>
#include <Tlhelp32.h>
/*************************************
* BOOL EnablePrivilege (PCSTR name)
* 功能 提升本权限
*防止有些进程我们权限不够,打不开人家
* 参数 PCSTR name 所需的权限
* 返回是否成功
**************************************/
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;
}
/*************************************
* 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")), TEXT("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,"工程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,"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,"s","f",0);
return CallWindowProc(OldWndProc,hWnd,Msg,wParam,lParam);
//return 0;
break;
case WM_LBUTTONDOWN:
//MessageBox(NULL,"b","s",0);
//SetFocus(hWnd);
CreateProcess(NULL,"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";
CHAR lpMessage[MAX_PATH+64];
// 获取PID 和主模块名,将弹出消息框
DWORD dwPID = GetCurrentProcessId();
//GetModuleBaseName(GetCurrentProcess(),NULL,lpMainMoudleName,MAX_PATH);
wsprintf(lpMessage,"Process name: %s, PID: %u ",lpMainMoudleName,dwPID);
MessageBox(NULL,lpMessage,"msg.dll",MB_OK);
hDlg=FindWindow(NULL,"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,所以会被金山等系列提示(大家懂的),仅仅是个试验罢了。完整的工程暂时就不上传了吧,如果有需要的话,再上传或者发邮件也可以,毕竟不是很“光彩”的东西。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)