linux实现TFTP协议

来源:互联网 发布:手机移动网络自动关闭 编辑:程序博客网 时间:2024/04/26 12:13


不论你是热衷于黑客技术的爱好者, 还是从事编写网络应用程序的程序员, 都需要掌握熟练的编程能力, 以及具备扎实的TCP/IP协议的扎实基础。很多初学者都有这样的经历,要么是能编一些复杂算法的程序,却不能结合到实际的应用中, 要么就是三卷W.Richard Stevens的圣经《TCP/IP详解》熟记于胸, 却无法用熟悉的编程语言将其实现。这真是件很令人郁闷的事。出于这样的目的, 本文就结合TCP/IP协议中的TFTP协议来分析,并简单实现了Windows下TFTP的服务器和客户端。
(致编者: 请务必加上以下声明)
注: 本文大部分理论引用于<TCP/IP详解卷一>和中国协议分析网www.cnpaf.net

 

一、协议分析:
TFTP(Trivial File Transfer Protocol)即简单文件传送协议, 是TCP/TP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议, 提供不复杂的, 开销不大的文件传输服务.为了保持简单和短小, TFTP使用UDP协议, 且默认端口号为69,它不提供可靠的数据流传输服务, 也不提供存取授权与认证机制, 使用超时重传方式来保证数据的到达。 与FTP相比, TFTP的大小要小的多, 它主要用于小文件的传输, 只能从文件服务器上获得或写入文件, 不能列出目录。更多的资料可以参考RFC1350。下面给出了5种TFTP报文格式。

我们可以看到,TFTP报文的头两个字节表示操作码。对于读请求(RRQ)和写请求(WRQ),文件名字段说明客户要读或写的位于服务器上的文件。这个文件字段以0字节作为结束。模式字段是一个ASCII码串netascii或octet(大小写可任意组合),同样以0字节结束。netascii表示数据是以成行的ASCII码字符组成,以两个字节—回车字符后跟换行字符(称为CR / LF)作为行结束符。这两个行结束字符在这种格式和本地主机使用的行定界符之间进行转化。octet则将数据看作8 bit一组的字节流而不作任何解释。
每个数据分组包含一个块编号字段,它以后要在确认分组中使用。以读一个文件作为例
子,TFTP客户需要发送一个读请求说明要读的文件名和文件模式( mode )。如果这个文件能被这个客户读取,TFTP服务器就返回一个块编号为1的数据分组。TFTP客户又发送一个块编号为1的ACK。TFTP服务器随后发送块编号为2的数据。TFTP客户发回块编号为2的ACK。重复这个过程直到这个文件传送完。除了最后一个数据分组可含有不足512字节的数据,其他每个数据分组均含有512字节的数据。当TFTP客户收到一个不足512字节的数据分组,就知道它收到最后一个数据分组。
在写请求的情况下,TFTP 客户发送WRQ指明文件名和模式。如果该文件能被该客户写,TFTP 服务器就返回块编号为0的ACK包。该客户就将文件的头512字节以块编号为1发出。服务器则返回块编号为1的ACK。
最后一种TFTP报文类型是差错报文,它的操作码为5。它用于服务器不能处理读请求或写请求的情况。在文件传输过程中的读和写差错也会导致传送这种报文,接着停止传输。差错编号字段给出一个数字的差错码,跟着是一个ASCII表示的差错报文字段,可能包含额外的操作系统说明的信息。

根据图一及上面的分析,我们可以得到以下定义:
#define TFTP_RRQ 01 // 读请求
#define TFTP_WRQ 02 // 写请求
#define TFTP_DATA 03 // 数据包
#define TFTP_ACK 04 // 确认包
#define TFTP_ERROR 05 // 错误代码

头文件结构也能很容易得到:
typedef struct tftphdr {
USHORT tu_opcode; // 操作码
union
{
USHORT tu_block; // 块号
USHORT tu_code; // 错误码
char tu_stuff[1]; // 请求包填充物
}th_u;
char th_data[1]; // 数据或错误字符串
}TFTP_HDR, *PTFTP_HDR;


二、TFTP服务器的实现
不知道大家对两年前的冲击波病毒Msblast.exe是否还有印象,该病毒通过RPC远程溢出漏洞溢出目标主机后,会绑定目标主机的4444端口,然后利用Windows系统自带的tftp客户端程序发送下载消息,目标主机通过tftp下载病毒再运行病毒,反复循环导致更多的计算机受到感染。因为Windows系统并没有默认的TFTP服务端程序,所以此时的关键就在于Msblast.exe病毒本身就是个tftp服务器。至于TFTP服务器的实现, 以前ww0830在《黑防》介绍过,为了满足大多数读者的要求,及关系到本文的完整性,我这里也给出具体实现,注意,本TFTP服务端只实现了供客户端程序下载的功能,更多的功能读者可以自己完善。
下面给出TFTP实现核心代码及详细注释
(注: 以下所有代码均在WindowsXP SP1+VC6.0环境下编译通过, 因为主要理解思路, 省略了错误处理等步骤, 完整源代码见附件内容):

初始化Winsock库, 注意在要加上ws2_32.lib库文件: 
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 2), &WSAData);

创建套接字
SOCKET sock = INVALID_SOCKET;
sock = socket(AF_INET, SOCK_DGRAM, 0); // 这里用的是UDP协议,所以协议类型为//SOCK_DGRAM
初始化本地主机地址信息:
struct sockaddr_in ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(MyIp); // MyIp为本主机IP
ServerAddr.sin_port = htons(69); // TFTP服务器默认端口号

绑定套接字
bind(sock,(struct sockaddr *)&ServerAddr, sizeof(struct sockaddr_in) )

初始化远程地址信息
struct sockaddr_in Client;
Client.sin_family = AF_INET;
Client.sin_port = htons(INADDR_ANY);
Client.sin_addr.s_addr = inet_addr(RemoteIp); // 远程IP地址

定义TFTP首部缓冲区
TFTP_HDR RecvBuff; // 接收的数据缓冲区
TFTP_HDR LocalBuff;// 本地数据缓冲区

接收数据,定义数据块大小为512字节:
int alen = sizeof(Client);
recvfrom(sock, (char *)&RecvBuff, 512, 0, (struct sockaddr *)&Client, &alen);

获得客户端的请求操作码
LocalBuff.tu_opcode = ntohs(RecvBuff.tu_opcode);

打开本地文件
fp = fopen("abc.txt", "rb"); // 假设abc.txt 为服务器文件
读本地文件
fread(FileBuff,sizeof(char),512,fp);
填充TFTP首部
SendBuff.tu_opcode = htons(3); // 操作码3 = data
SendBuff. th_u.tu_block = htons(time); // 块号
memcpy( &(SendBuff.th_data), FileBuff, i); // 数据

发送文件给客户机(接收文件或数据类似)
sendto(sock, (char *)&SendBuff, i+4, MSG_DONTROUTE, (struct sockaddr *)&Client, sizeof(Client) );

整个简单的TFTP服务器这样就完成了,是不是很简单呐。
带外说一说,这里如果要模拟冲击波病毒的传播方式,程序最开始有个随机扫描的代码:
WSAStartup(MAKEWORD(2, 2), &WSAData);
SOCKADDR_IN addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // 通过80端口是否开放判断对方主机是否存活
addr.sin_addr.s_addr = inet_addr(RemoteIp); // 这里的RemoteIp应该通过一
//个随机生成网段内IP地址的函数得到
if(connect(sock,(struct sockaddr *) &addr, sizeof(addr))<0)
{
closesocket(sock);
WSACleanup();
return ; //连接失败,停止扫描
}
// 通过gethostsocket()获取连出去的ip
struct sockaddr_in hostname;
int namelen = sizeof(hostname);
char * source;
getsockname(sock, (struct sockaddr *)&hostname, &namelen);
source = inet_ntoa( hostname.sin_addr );

这一段代码的作用是循环连接网段内存活主机的80端口,如果发现某台主机存活,则蠕虫自身生成的TFTP服务器则接受被冲击波病毒溢出的客户端下载请求,复制自身到对方主机,如此恶性循环,导致蠕虫成级数性的扩散。

三、TFTP客户端的实现
通常情况下,Windows2K以上操作系统都可以使用系统自带的TFTP客户端程序,但自从Msblast.exe蠕虫爆发后,很多用户都关掉了TFTP的默认使用。对于很多学习入侵和渗透的人来说,这真是个噩耗^_^。所以,通常很多木马或后门的设计者都在他们的作品里加入了FTP,Http下载,Email或TFTP客户端的功能,以加强对肉鸡的控制。下面,我们就来分析一下TFTP的客户端实现。
TFTP客户端的实现主要体现在对TFTP各种数据包首部字段的分析及填充上。下面给出了部分核心功能的伪代码,(具体代码可以参看附件里的源代码:该代码作者为helloworld1,在此对他所提供的源代码表示万分感谢)

填充(读/写)请求字段,根据图一,可以得到下面的读/些 请求数据包格式:

于是,定义一个makereq()函数来填充请求字段:
int makereq(char type,int mode,char *filename,char *buffer,int size)
{
int pos = 0; // 位置
int i = 0; 
char s[32] = "";
if(mode==TFTP_NETASCII) // 传输模式
strcpy(s,"netascii");
else
strcpy(s,"octet"); 
buffer[pos] = 0; 
pos++;
buffer[pos] = type; // Opcode = 01(RRQ) or 02(WRQ)
pos++;
for(i=0;i<strlen(filename);i++) // &buffer[3] = Filename
{
buffer[pos] = filename[i];
pos++;
}
buffer[pos] = 0; // 0
pos++;
for(i=0;i<strlen(s);i++) // Mode
{
buffer[pos] = s[i];
pos++;
}
buffer[pos] = 0; // 0
pos++;
return pos; // 返回请求包头长度
}
下面为ACK包格式及填充地段函数makeack():

图3
int makeack(unsigned short num,char *buffer,int size )
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_ACK; // Opcode = 04
pos++;
buffer[pos] = (char)(num>>8); // 块号2个字节
pos++;
buffer[pos] = (char)num;
pos++;
return pos;
}

同理,DATA包格式及填充函数makedata()如下:
int makedata(int num,char *data,int datasize,char *buffer,int bufsize)
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_DATA; // Opcode = 03
pos++;
buffer[pos] = (char)(num>>8); // Block # 块号
pos++;
buffer[pos] = (char)num;
pos++;
memcpy(&buffer[pos],data,datasize); // Data 数据
pos = pos + datasize;
return pos;
}

实现下载文件,cmd[][256]为自定义的参数数据结构,pcount为参数个数:
(详细代码见光盘)

代码的注释很清楚,其实结合文章开始的理论及图,再实现就很简单了。至于上传(put)文件和下载类似,由于篇幅的原因,大家可以参看源代码。

实现效果:
首先,tftpServer.exe为我们第2部分介绍实现的TFTP服务端程序,在该程序的同级目录下放一个名为“abc.txt”的文件,运行tftpd.exe。然后,运行第3部分介绍的TFTP客户端程序tftpClient.exe,并输入命令:
#get abc.txt

 

总结:
对于学习网络知识也好,学习黑客技术也罢,对于TCP/IP协议的掌握是重中之重, 本文讨论虽然只是简单讨论TFTP协议的相关理论及实现,但稍微复杂一点的FTP协议,POP3协议,SNMP协议也都可以通过类似的分析来实现的。本人水平有限,本文给出都是思路,实现都简单的不能再简单。如有不对之处,请大家给予批评指正。

不论你是热衷于黑客技术的爱好者, 还是从事编写网络应用程序的程序员, 都需要掌握熟练的编程能力, 以及具备扎实的TCP/IP协议的扎实基础。很多初学者都有这样的经历,要么是能编一些复杂算法的程序,却不能结合到实际的应用中, 要么就是三卷W.Richard Stevens的圣经《TCP/IP详解》熟记于胸, 却无法用熟悉的编程语言将其实现。这真是件很令人郁闷的事。出于这样的目的, 本文就结合TCP/IP协议中的TFTP协议来分析,并简单实现了Windows下TFTP的服务器和客户端。
(致编者: 请务必加上以下声明)
注: 本文大部分理论引用于<TCP/IP详解卷一>和中国协议分析网www.cnpaf.net

 

一、协议分析:
TFTP(Trivial File Transfer Protocol)即简单文件传送协议, 是TCP/TP协议族中的一个用来在客户机与服务器之间进行简单文件传输的协议, 提供不复杂的, 开销不大的文件传输服务.为了保持简单和短小, TFTP使用UDP协议, 且默认端口号为69,它不提供可靠的数据流传输服务, 也不提供存取授权与认证机制, 使用超时重传方式来保证数据的到达。 与FTP相比, TFTP的大小要小的多, 它主要用于小文件的传输, 只能从文件服务器上获得或写入文件, 不能列出目录。更多的资料可以参考RFC1350。下面给出了5种TFTP报文格式。

我们可以看到,TFTP报文的头两个字节表示操作码。对于读请求(RRQ)和写请求(WRQ),文件名字段说明客户要读或写的位于服务器上的文件。这个文件字段以0字节作为结束。模式字段是一个ASCII码串netascii或octet(大小写可任意组合),同样以0字节结束。netascii表示数据是以成行的ASCII码字符组成,以两个字节—回车字符后跟换行字符(称为CR / LF)作为行结束符。这两个行结束字符在这种格式和本地主机使用的行定界符之间进行转化。octet则将数据看作8 bit一组的字节流而不作任何解释。
每个数据分组包含一个块编号字段,它以后要在确认分组中使用。以读一个文件作为例
子,TFTP客户需要发送一个读请求说明要读的文件名和文件模式( mode )。如果这个文件能被这个客户读取,TFTP服务器就返回一个块编号为1的数据分组。TFTP客户又发送一个块编号为1的ACK。TFTP服务器随后发送块编号为2的数据。TFTP客户发回块编号为2的ACK。重复这个过程直到这个文件传送完。除了最后一个数据分组可含有不足512字节的数据,其他每个数据分组均含有512字节的数据。当TFTP客户收到一个不足512字节的数据分组,就知道它收到最后一个数据分组。
在写请求的情况下,TFTP 客户发送WRQ指明文件名和模式。如果该文件能被该客户写,TFTP 服务器就返回块编号为0的ACK包。该客户就将文件的头512字节以块编号为1发出。服务器则返回块编号为1的ACK。
最后一种TFTP报文类型是差错报文,它的操作码为5。它用于服务器不能处理读请求或写请求的情况。在文件传输过程中的读和写差错也会导致传送这种报文,接着停止传输。差错编号字段给出一个数字的差错码,跟着是一个ASCII表示的差错报文字段,可能包含额外的操作系统说明的信息。

根据图一及上面的分析,我们可以得到以下定义:
#define TFTP_RRQ 01 // 读请求
#define TFTP_WRQ 02 // 写请求
#define TFTP_DATA 03 // 数据包
#define TFTP_ACK 04 // 确认包
#define TFTP_ERROR 05 // 错误代码

头文件结构也能很容易得到:
typedef struct tftphdr {
USHORT tu_opcode; // 操作码
union
{
USHORT tu_block; // 块号
USHORT tu_code; // 错误码
char tu_stuff[1]; // 请求包填充物
}th_u;
char th_data[1]; // 数据或错误字符串
}TFTP_HDR, *PTFTP_HDR;


二、TFTP服务器的实现
不知道大家对两年前的冲击波病毒Msblast.exe是否还有印象,该病毒通过RPC远程溢出漏洞溢出目标主机后,会绑定目标主机的4444端口,然后利用Windows系统自带的tftp客户端程序发送下载消息,目标主机通过tftp下载病毒再运行病毒,反复循环导致更多的计算机受到感染。因为Windows系统并没有默认的TFTP服务端程序,所以此时的关键就在于Msblast.exe病毒本身就是个tftp服务器。至于TFTP服务器的实现, 以前ww0830在《黑防》介绍过,为了满足大多数读者的要求,及关系到本文的完整性,我这里也给出具体实现,注意,本TFTP服务端只实现了供客户端程序下载的功能,更多的功能读者可以自己完善。
下面给出TFTP实现核心代码及详细注释
(注: 以下所有代码均在WindowsXP SP1+VC6.0环境下编译通过, 因为主要理解思路, 省略了错误处理等步骤, 完整源代码见附件内容):

初始化Winsock库, 注意在要加上ws2_32.lib库文件: 
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 2), &WSAData);

创建套接字
SOCKET sock = INVALID_SOCKET;
sock = socket(AF_INET, SOCK_DGRAM, 0); // 这里用的是UDP协议,所以协议类型为//SOCK_DGRAM
初始化本地主机地址信息:
struct sockaddr_in ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(MyIp); // MyIp为本主机IP
ServerAddr.sin_port = htons(69); // TFTP服务器默认端口号

绑定套接字
bind(sock,(struct sockaddr *)&ServerAddr, sizeof(struct sockaddr_in) )

初始化远程地址信息
struct sockaddr_in Client;
Client.sin_family = AF_INET;
Client.sin_port = htons(INADDR_ANY);
Client.sin_addr.s_addr = inet_addr(RemoteIp); // 远程IP地址

定义TFTP首部缓冲区
TFTP_HDR RecvBuff; // 接收的数据缓冲区
TFTP_HDR LocalBuff;// 本地数据缓冲区

接收数据,定义数据块大小为512字节:
int alen = sizeof(Client);
recvfrom(sock, (char *)&RecvBuff, 512, 0, (struct sockaddr *)&Client, &alen);

获得客户端的请求操作码
LocalBuff.tu_opcode = ntohs(RecvBuff.tu_opcode);

打开本地文件
fp = fopen("abc.txt", "rb"); // 假设abc.txt 为服务器文件
读本地文件
fread(FileBuff,sizeof(char),512,fp);
填充TFTP首部
SendBuff.tu_opcode = htons(3); // 操作码3 = data
SendBuff. th_u.tu_block = htons(time); // 块号
memcpy( &(SendBuff.th_data), FileBuff, i); // 数据

发送文件给客户机(接收文件或数据类似)
sendto(sock, (char *)&SendBuff, i+4, MSG_DONTROUTE, (struct sockaddr *)&Client, sizeof(Client) );

整个简单的TFTP服务器这样就完成了,是不是很简单呐。
带外说一说,这里如果要模拟冲击波病毒的传播方式,程序最开始有个随机扫描的代码:
WSAStartup(MAKEWORD(2, 2), &WSAData);
SOCKADDR_IN addr;
sock = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(80); // 通过80端口是否开放判断对方主机是否存活
addr.sin_addr.s_addr = inet_addr(RemoteIp); // 这里的RemoteIp应该通过一
//个随机生成网段内IP地址的函数得到
if(connect(sock,(struct sockaddr *) &addr, sizeof(addr))<0)
{
closesocket(sock);
WSACleanup();
return ; //连接失败,停止扫描
}
// 通过gethostsocket()获取连出去的ip
struct sockaddr_in hostname;
int namelen = sizeof(hostname);
char * source;
getsockname(sock, (struct sockaddr *)&hostname, &namelen);
source = inet_ntoa( hostname.sin_addr );

这一段代码的作用是循环连接网段内存活主机的80端口,如果发现某台主机存活,则蠕虫自身生成的TFTP服务器则接受被冲击波病毒溢出的客户端下载请求,复制自身到对方主机,如此恶性循环,导致蠕虫成级数性的扩散。

三、TFTP客户端的实现
通常情况下,Windows2K以上操作系统都可以使用系统自带的TFTP客户端程序,但自从Msblast.exe蠕虫爆发后,很多用户都关掉了TFTP的默认使用。对于很多学习入侵和渗透的人来说,这真是个噩耗^_^。所以,通常很多木马或后门的设计者都在他们的作品里加入了FTP,Http下载,Email或TFTP客户端的功能,以加强对肉鸡的控制。下面,我们就来分析一下TFTP的客户端实现。
TFTP客户端的实现主要体现在对TFTP各种数据包首部字段的分析及填充上。下面给出了部分核心功能的伪代码,(具体代码可以参看附件里的源代码:该代码作者为helloworld1,在此对他所提供的源代码表示万分感谢)

填充(读/写)请求字段,根据图一,可以得到下面的读/些 请求数据包格式:

于是,定义一个makereq()函数来填充请求字段:
int makereq(char type,int mode,char *filename,char *buffer,int size)
{
int pos = 0; // 位置
int i = 0; 
char s[32] = "";
if(mode==TFTP_NETASCII) // 传输模式
strcpy(s,"netascii");
else
strcpy(s,"octet"); 
buffer[pos] = 0; 
pos++;
buffer[pos] = type; // Opcode = 01(RRQ) or 02(WRQ)
pos++;
for(i=0;i<strlen(filename);i++) // &buffer[3] = Filename
{
buffer[pos] = filename[i];
pos++;
}
buffer[pos] = 0; // 0
pos++;
for(i=0;i<strlen(s);i++) // Mode
{
buffer[pos] = s[i];
pos++;
}
buffer[pos] = 0; // 0
pos++;
return pos; // 返回请求包头长度
}
下面为ACK包格式及填充地段函数makeack():

图3
int makeack(unsigned short num,char *buffer,int size )
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_ACK; // Opcode = 04
pos++;
buffer[pos] = (char)(num>>8); // 块号2个字节
pos++;
buffer[pos] = (char)num;
pos++;
return pos;
}

同理,DATA包格式及填充函数makedata()如下:
int makedata(int num,char *data,int datasize,char *buffer,int bufsize)
{
int pos = 0;
buffer[pos] = 0;
pos++;
buffer[pos] = TFTP_DATA; // Opcode = 03
pos++;
buffer[pos] = (char)(num>>8); // Block # 块号
pos++;
buffer[pos] = (char)num;
pos++;
memcpy(&buffer[pos],data,datasize); // Data 数据
pos = pos + datasize;
return pos;
}

实现下载文件,cmd[][256]为自定义的参数数据结构,pcount为参数个数:
(详细代码见光盘)

代码的注释很清楚,其实结合文章开始的理论及图,再实现就很简单了。至于上传(put)文件和下载类似,由于篇幅的原因,大家可以参看源代码。

实现效果:
首先,tftpServer.exe为我们第2部分介绍实现的TFTP服务端程序,在该程序的同级目录下放一个名为“abc.txt”的文件,运行tftpd.exe。然后,运行第3部分介绍的TFTP客户端程序tftpClient.exe,并输入命令:
#get abc.txt

 

总结:
对于学习网络知识也好,学习黑客技术也罢,对于TCP/IP协议的掌握是重中之重, 本文讨论虽然只是简单讨论TFTP协议的相关理论及实现,但稍微复杂一点的FTP协议,POP3协议,SNMP协议也都可以通过类似的分析来实现的。本人水平有限,本文给出都是思路,实现都简单的不能再简单。如有不对之处,请大家给予批评指正。

0 0
原创粉丝点击