首页
社区
课程
招聘
[原创]你想知道的远控编程
发表于: 2022-8-4 17:13 21515

[原创]你想知道的远控编程

2022-8-4 17:13
21515

远控大家都知道最重要的就是框架的编写,框架不好,后面加功能会很累人,很多时候,可能会因为添加一个功能点,就要对框架进行修改,修改后可能所有的功能都要测试,耗时耗力
尤其是对于我这种半路出家的人来说,很折磨人。
因为项目原因,代码不能放出来,我会尽可能的说明问题现象,以及我在处理这些问题时的方法。

基本流程:

建立连接 - 数据加密 - 数据发送 - 数据接收 - 数据解密 -发送到模块 - 不同类型的处理

很多项目可能不止要求一种协议tcp,udp,http,https,icmp 等等,不管是哪种协议,无非就是 建立连接,数据发送,数据接收。我之前写的时候,会存在一个比较长的switch ... case ... 结构,用来对不同的协议进行区分,不同的协议调用不同的函数,大概如下:

这样我每次在添加一种新的协议的时候,都需要对这些switch 进行维护。

这里可以对连接本身进行一种抽象,主框架代码中用类似这样的一种结构来进行编程,代码最开运行的时候,对整个结构体进行初始化就可以了。

粘包和分包问题并不是tcp协议的问题,而是程序员使用tcp协议时,需要主动了解的机制问题。tcp 是流式套接字,数据在发送的时候是以字节流的形式进行发送的,字节流是无界的。举个例子,客户端调用了两次send 函数,每次send 一个字节,控制端 调用recv 进行接收,他可能一次接收就接收到了两个字节,这个就是粘包;客户端一次发送了 100 个字节,控制端接收可能需要两次接收,第一次接收20 个字节,第二次接收到了80 个字节,这个就是分包。究其原因,和tcp本身数据发送的逻辑有关。查看send 和 recv 的api 文档,send 的返回值表示发送成功的数据字节数,其实这里的发送成功并不是真正 的发送成功,他只是表示成功写入到了内核中的发送缓冲区数据的字节数,真正的发送要由内核完成,内核在数据发送的时候,又需要考虑流量控制,拥趸控制等,涉及滑动窗口,mss/mtu,nagle算法等,详细的大家可以看其他文档,他们讲的比我专业的多 。之前在处理粘包分包问题的时候都是简单的使用sleep函数,在项目中加这种sleep 是很恶心的一件事。
常见的解决方案,一般是两个:

一个是添加数据包头,指明数据包的大小;

另一个是添加标志字符串,指明数据的结尾或者开头。

也有说可以通过禁用nagle 算法的,windows下也有相关的socket 选项TCP_NODELAY ,但是从我查阅的文档来看,大部分的windows 操作系统是不支持禁用的。

用户层缓冲区的设计,可以阅读陈硕的 linux多线程服务端编程中关于缓冲区的设计。

这里大概说下缓冲区处理粘包问题的基本思路,接收到数据之后,先将数据放到缓冲区中,再解析数据包头的长度字段,如果整个缓冲区的数据的长度小于这个值,则不进行数据处理;如果大于这个值,则进行循环解析。因为可能出现粘包的情况,缓冲区中可能有多个数据包,所以需要一个循环处理。

不要在多线程中对同一个socket 进行读写操作,即使是加锁。正确的处理思路是维护一个发送队列,使用一个发送线程从发送队列中取数据进行发送。

考虑这么一个场景:

在对socket进行监听的时候,经常会单独起一个线程,然后使用select 函数监听可读,可写。但是如果同时我们的心跳线程检测到了目标端的退出,需要对socket 进行关闭操作,同时希望select 线程进行退出。这个时候如果我们在心跳线程中进行close操作,处于阻塞状态的select 函数是不会做出响应的,select线程将不会退出。
这种情况有两种处理方法:

一种是改变socket 为非阻塞式socket,同时设置一个标志位,然后在心跳线程中设置该标志位为 0 。

另一种方法是使用shutdown函数,关闭socket 的可读可写。

shutdown( SHUT_RDWR);

如果想要适配的机器更广,都需要对大小端进行处理。大小端处理的方法一般就两种思路。

一个是使用 ntohs / htons。将所有的网络传输中的数字转换为网络序,也就是大端序,数据到达机器之后,然后根据机器不同,考虑是否需要转换成小端序;

另一种方法就是使用atoi。在数据发送的时候,数据统一转换成字符串,到达机器之后,再是使用atoi 函数,转换成数字。

正向的s5 代理好理解,就是在被控端开启一个s5 的代理,直接连接被控端的s5 代理;s5反向代理,其实就是在控制端开一个端口转发的程序,数据传输复用远控框架的通讯通道,到达被控端之后,再连接本地的s5 代理程序。消息类型大概就是这么几种类型,建立连接,数据传输,关闭连接。这里其实最重要的就是连接的管理问题,或者说是连接的对应关系问题。这里我是在被控端和控制端同时维护了一个map,map的key是唯一的,值就是对应的socket 句柄。当控制端接收到一个新的socket之后,就会生成这样唯一key,将key 和 fd 放入到map 中,同时产生一个建立连接的消息,这个消息将携带刚才生成的key值,发送给被控端,被控端接收到消息之后,将会与本地的s5建立连接,连接建立之后将得到的fd 和 key 放入被控端的map中。通过key 这个中间变量,我们就有了控制端fd 和 被控端fd 的一一对应关系了。这种通过关系表处理关联关系的方式,在操作系统中非常常见。

这里还要说下调试的问题,这种s5 反向的调试真的是非常恶心,多线程,缺乏很好的定位手段,尤其是程序不崩溃,部分结果异常。
这里说下我在调试时 的排查点:

1、两边的连接数量是不是一致。

2、两边的发送数据流量 和 接收数据流量是不是一致,单个线程的去统计。

icmp本身携带的数据量比较小,遇到类似 netstat -ano 这种返回结果的时候,单次肯定不能完全返回,这里就涉及到一个拆包,多次返回的问题。
其实下面这种方法对于短链接的模型,应该都是可以用的。

模型大概就是这样的:

控制端发送命令执行请求,目标端获得结果之后,不立即返回,将数据拆包,放在待返回队列中;

控制端发送数据获取请求,目标端从队列中获取数据,发送给控制端。

涉及一个数据返回的结构体

根据请求包的编号,可以按发送顺序获得返回结果,根据分包总个数和当前数据包位于分包的第几个 可以获得完整的返回结果。

并不是所有的功能模块都适合做成插件,像s5,端口转发这种的做成插件就比较鸡肋,因为他们本身对同步性的要求就比较高,这种的做成静态的功能就挺好的。

像远程shell,文件管理,进程管理这种的在做插件化设计时要考虑的点,说到底,其实就是数据如何与主模块进行传输。

我知道的两种吧:

一种是通过虚函数,主模块留出一些和插件之间进行数据交互的接口,然后插件模块对虚函数进行实现,mysend 和 myrecv之类的;

第二种就是导出函数,主模块寻找模块插件中的特定函数,通过调用这些特定函数,实现与主模块的交互。

进程,线程间数据同步的方法很多,个人觉得在项目里面不需要使用太多的方法,一种有效的手段就足够了

共享内存,文件,socket,管道,事件对象,互斥体

数据处理逻辑和页面展示逻辑分开处理,通过消息进行同步。

linux 下进行端口复用的方法比较多,这里说一个我在使用rawsocket 进行端口复用时遇到的问题。

rawsoket 进行端口复用时,如果复用的端口是一个短链接的或者说存在协议校验的端口,如 22 端口,那么使用rawsocket 进行复用的时候,是会出问题的,具体表现就是发送的流量和接收的流量不一致。

猜想如下,如果发送端存在了分包发送逻辑,内核在第一个分包发送完,第二个包还没发送的时候,接收端的22协议此时进行完了协议校验,进行了close 操作,那么第二个分包将无法发送,且数据包将被发送端丢弃,而send 函数只是将数据写入了内核的缓冲区,返回的是成功,无法得知是哪一段数据被丢弃了。
这里提供一种解决方法,通过 lkm 编程,hook close 函数。close 函数的参数是fd,而我们能够进行判断的只有 client 的端口信息,这里想到了3环下 get_peername 函数。可以在__sys_getpeername 内核源码逻辑的基础上,通过转换 fd,struct socket,struct sock之间的关系,得到fd 对应的端口信息。拦截close 的同时,需要在内核中同时开启数据接收线程,将数据从内核缓冲区中取出来,不然会出现 windows zero size 的报错信息。

 
 
void myrecv()
{
switch (协议类型)
{
    case tcp:
.......
    case udp:
..
}
}
 
void mysend()
{
switch()
{
case tcp:
...
case udp:
..
}
}
void myrecv()
{
switch (协议类型)
{
    case tcp:
.......
    case udp:
..
}
}
 
void mysend()
{
switch()
{
case tcp:
...
case udp:
..
}
}
 
myconnect
{
    socket sockfd;
    int protype;
    void (*pconnect)(ip,port);
    void (*pmysend)(void* data,int len);
    void (*pmyrecv)(void* data,int len);
}

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-8-4 21:46 被Wow~~编辑 ,原因:
收藏
免费 3
支持
分享
最新回复 (14)
雪    币: 2402
活跃值: (6808)
能力值: ( LV7,RANK:102 )
在线值:
发帖
回帖
粉丝
2
羡慕远控大佬
2022-8-4 17:19
0
雪    币: 9197
活跃值: (6415)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
你学习的这些知识都是2005年左右的东西哦。
2022-8-5 01:30
2
雪    币: 2278
活跃值: (12799)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
4
asio hpsocket Muduo Libevent..... 总有一款适合你
2022-8-5 08:22
0
雪    币: 1484
活跃值: (14662)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
5
Babuk勒索不是泄露了几百个源码吗,里面的RAT源码全过一遍就行了
2022-8-5 09:13
0
雪    币: 1260
活跃值: (963)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
一半人生 asio hpsocket Muduo Libevent..... 总有一款适合你
现成的框架确实好用
2022-8-5 09:35
0
雪    币: 1260
活跃值: (963)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
7
mudebug 你学习的这些知识都是2005年左右的东西哦。
主要是处理思路
2022-8-5 09:36
0
雪    币: 0
活跃值: (999)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
SSH山水画 Babuk勒索不是泄露了几百个源码吗,里面的RAT源码全过一遍就行了
请问下能说点具体的吗?  github有项目吗
2022-8-12 12:50
0
雪    币: 1484
活跃值: (14662)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
9
spring丶必应 请问下能说点具体的吗? github有项目吗
帖子被删了,连接找不到了,GIT上应该是没有的,你可以搜搜
2022-8-12 15:11
0
雪    币: 0
活跃值: (999)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
SSH山水画 帖子被删了,连接找不到了,GIT上应该是没有的,你可以搜搜
好的,谢谢
2022-8-13 12:22
0
雪    币: 219
活跃值: (190)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
写得不错,赞
2022-8-13 13:10
0
雪    币: 6124
活跃值: (4721)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
12
不想
2022-8-13 13:37
0
雪    币: 2
活跃值: (52)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
发现你不是在做远控,你是在做文件传输程序。你这种相当于临时演员,应该首先考虑一下自己如何活过第一集——如何稳定的安装到操作系统中不被杀软发现,这个是关键,其它的都稳定性功能强大性日后再说吧。
2022-9-2 23:26
0
雪    币: 1555
活跃值: (3118)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
14
sforever 发现你不是在做远控,你是在做文件传输程序。你这种相当于临时演员,应该首先考虑一下自己如何活过第一集——如何稳定的安装到操作系统中不被杀软发现,这个是关键,其它的都稳定性功能强大性日后再说吧。
你说的是木马,人家说的是远控rat,是正规的东西,植入过程都是有UI,以及设置过程。
2022-9-24 22:24
0
雪    币: 1869
活跃值: (4151)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
mark
2022-9-24 23:05
0
游客
登录 | 注册 方可回帖
返回
//