回忆:
记得还在学校时,就会用 net send 命令在局域网内发消息(1998年,那时候还没用过QQ,好像还是以前的挪威网),觉
得又神奇又好玩,那时就想自已也写一个类似程序,但当时能力有限,渐渐的也忘了。。。
今天就用它来做试验了。。。
在windows 系统中同样有 net send 命令,只是双方系统都启动了消息服务(现在很多防火墙会关掉它),就可以相互用 net send 命令在局域网内通讯聊天. 现在用关了 GUI 了又重新回到命令行太难受了,那就重写它吧。有几种方法重写。
1。包装 net send 命令 。编写一个窗口程序,接受参数 ,然后构造一条 net send 命令,启动一人进程运行命令。
这是最简单的方法.
2. 找出 net send 的内部方法 (采用 反汇编和跟踪调试找到它使用的API,我用的是IDA反编译,和onlydbg调试跟踪),利用它重写。。。此方法我下次再价绍.
3.用工具截取和分析 net send 发出的信息包(此工具也是自已编写的,截取封包是另外课题,下次再介绍),找出它的格式然后按格式定义包自已发送.此方法是今天要讲的话题,并且在此功能上加入 编写 raw socket 和 伪造 IP包的内容.
费话不多说了,看代码吧,delphi 代码.
大家可以参考一下, tcp/ip 相关的资料,本内容涉及 IP 包结构 ,UDP 包结构,及 socket 编程.
另外我会有完程工程上传,但现在没积分,传不了。
ps: 我是第一次写blog,有不清楚的就大家原谅并指出。
{ *********************************************************************** }
{ }
{ NetSendPacket }
{ }
{ 此模块封装了 windows 操作系统中 net send 消息传送程序的消息封包格式,并且}
{ 采用了直接构造 IP 包和UDP包,采用 RAW socket 方式发送封包,具有完全隐藏IP }
{ 和IP欺骗功能.请大家本着学习的态度对待,用之不当,后果自负 }
{ }
{ *********************************************************************** }
{ Fangkailove@yeah.net }
{ 2004/12/08 }
{ *********************************************************************** }
unit NetSendPacket;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
const
IP_HDRINCL=2;
type
TPacket = array [0..8192] of byte;
var
// net send 消息的网络封包头格式,按此格式填充UDP包并发送到目标机的 135 端口.对方能收到信息.需要启动windows的消息服务.
packet_header:array[0..79] of byte =(
$04,$00,$28,$00
,$10,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
,$00,$00,$00,$00,$f8,$91,$7b,$5a,$00,$ff,$d0,$11,$a9,$b2,$00,$c0
,$4f,$b6,$e6,$fc
,$ff,$ff,$ff,$ff // @40 : unique id over 16 bytes
,$ff,$ff,$ff,$ff
,$ff,$ff,$ff,$ff
,$ff,$ff,$ff,$ff
,$00,$00,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00
,$00,$00,$ff,$ff,$ff,$ff
,$ff,$ff,$ff,$ff // @74 : fields length
,$00,$00);
field_header :array [0..11]of byte =
($ff,$ff,$ff,$ff // @0 : field length
,$00,$00,$00,$00
,$00,$00,$00,$00); // @8 : field length
function sd( FromIp:string; Toip:string;ToPort:integer;FromName:string; ToName:string; msg:string ):bool ;
implementation
uses RawHeaders,WinSock;
//构造IP包时需要自已计算校验码,并填充到包.此函数负责计算校验码
function CheckSum(var Buffer;Size:integer):Word;
type
TwordArray=Array[0..1] of Word;
var
ChkSum:LongWord;
i :Integer;
begin
ChkSum:=0;
i:=0;
While Size>1 do
begin
ChkSum:=ChkSum+TwordArray(Buffer)[i];
inc(i);
Size:=Size-SizeOf(Word);
end;
if Size=1 then ChkSum:=ChkSum+Byte(TwordArray(Buffer)[i]);
ChkSum:=(ChkSum shr 16)+(ChkSum and $FFFF);
ChkSum:=ChkSum+(ChkSum shr 16);
Result:=Word(ChkSum);
end;
//发送封包函数
function sd( FromIp:string; Toip:string; ToPort:integer ;FromName:string; ToName:string; msg:string ):bool ;
var
DwFromIP:LongWord;
DwToIP :longWord;
wsaData:TWSADATA ;
s,ret :integer;
addr:TSockAddr ;
bOpt:integer;
packet : array [0..8192] of byte ;
fields_size,packet_size:integer;
pw:pdword;
len:longint;
IpHdr: TIpHdr;
IpVersion :Word;
IpSize: Word;
udpHdr: TUdpHdr;
udpsize:word;
psendHdr:^TSendHdr ;
udb_checkSum:word;
ip_packet: array [0..4095] of byte ;
ip_packet_size:integer;
pb:pbyte;
begin
WSAStartup(makeword(2,2), wsaData);
DwFromIP:=inet_Addr(pchar(FromIp)) ;
DwToIP:=inet_Addr(pchar(Toip)) ;
fillchar(addr, sizeof(addr),0);
addr.sin_addr.s_addr := DwToIP;
addr.sin_port := htons(ToPort);
addr.sin_family := AF_INET;
fillchar(packet,sizeof(packet),0);
packet_size:=0;
move(packet_header,packet[packet_size],sizeof(packet_header));
inc(packet_size,sizeof(packet_header));
len:=length(FromName)+1;
fillchar(field_header,sizeof(field_header),0);
field_header[0]:=len;
field_header[8]:=len;
move(field_header,packet[packet_size],sizeof(field_header));
inc(packet_size,sizeof(field_header));
StrCopy(@packet[packet_size], PAnsiChar(fromName));
inc(packet_size,(((len - 1) shr 2) + 1) shl 2); // align to 4 bytes
len:=length(ToName)+1;
fillchar(field_header,sizeof(field_header),0);
field_header[0]:=len;
field_header[8]:=len;
move(field_header,packet[packet_size],sizeof(field_header));
inc(packet_size,sizeof(field_header));
StrCopy(@packet[packet_size], PAnsiChar(ToName));
inc(packet_size,(((len - 1) shr 2) + 1) shl 2); // align to 4 bytes
len:=length(msg)+1;
fillchar(field_header,sizeof(field_header),0);
field_header[0]:=len;
field_header[8]:=len;
move(field_header,packet[packet_size],sizeof(field_header));
inc(packet_size,sizeof(field_header));
StrCopy(@packet[packet_size], PChar(msg));
inc(packet_size,len);
fields_size := packet_size - sizeof(packet_header);
pw:=@packet[40];
pw^:=gettickcount();
pw:=@packet[74] ;
pw^:= fields_size;
ip_packet_size:=sizeof(TIpHdr)+sizeof(TUdpHdr)+packet_size;
IpVersion:=4;
IpSize:=sizeof(TIpHdr) div sizeof(LongWord);
IpHdr.ip_verlen:=(IpVersion shl 4) or IpSize;
IpHdr.ip_tos:=0;
IpHdr.ip_len:=htons(ip_packet_size);
IpHdr.ip_id:=0;
IpHdr.ip_off:=0;
IpHdr.ip_ttl:=128;
IpHdr.ip_p:=IPPROTO_UDP;
IpHdr.ip_sum:=0;
IpHdr.ip_src:=dwFromIP;
IpHdr.ip_dst:=DwToIP;
udpsize:=sizeof(TudpHdr)+packet_size;
udpHdr.udp_sport:=htons(135);
udpHdr.udp_dport:=htons(135);
udpHdr.udp_ulen:=htons(udpsize);
udpHdr.udp_sum:=0;
//以下创建套接字时,采用了IPPROTO_UDP,所以不用做checksum
{
fillChar(ip_packet,sizeof(ip_packet),0);
pb:=@ip_packet[0];
psendHdr:= @ip_packet[0];
psendHdr^.send_src:=IpHdr.ip_src;
psendHdr^.send_dst:=IpHdr.ip_dst;
psendHdr^.send_P:=IpHdr.ip_p;
psendHdr^.send_len:= udpHdr.udp_ulen;
inc(pb,sizeof(TSendHdr));
move(udpHdr,pb^,sizeof(TudpHdr));
inc(pb,sizeof(TudpHdr));
Move(packet,pb^,packet_size);
udpHdr.udp_sum:=CheckSum(ip_packet,sizeof(TSendHdr)+sizeof(TudpHdr)+packet_size);
IpHdr.ip_sum:=checksum(ip_packet,ip_packet_size);
}
fillChar(ip_packet,sizeof(ip_packet),0);
pb:=@ip_packet[0];
Move(IpHdr,pb^,sizeof(TIpHdr));
inc(pb,sizeof(TIpHdr));
Move(udpHdr,pb^,sizeof(TudpHdr));
inc(pb,sizeof(TudpHdr));
Move(packet,pb^,packet_size);
//创建RawSocket
s :=socket(AF_INET,SOCK_RAW,IPPROTO_UDP);
if (s=INVALID_SOCKET) then
begin
showmessage('创建Socket错误');
exit;
end;
bOpt:=1;
//设置要以 IP 包传输
ret:=SetSockOpt(s,IPPROTO_IP,IP_HDRINCL,@bOpt,sizeof(bopt));
if ret=SOCKET_ERROR then
begin
showmessage('设置Socket错误');
exit;
end;
ret:=sendto(s, ip_packet, ip_packet_size, 0, TSOCKADDR(addr), sizeof(addr));
if ret=SOCKET_ERROR then showmessage('sendto 错误');
CloseHandle(s);
WSACleanup;
end;
end.