记Android 发送PDU相关问题
2012年12月18日 13:15
Happy coding
首先要了解下ICMP,先前用IcmpSendEcho实现了Ping功能,见 http://ckfan.is-programmer.com/posts/36168.html ICMP:是Internet 控制信息协议(ICMP)是 IP 组的一个整合部分,通过 IP 包传送的 ICMP 信息主要用于涉及网络 操作或错误操作的不可达信息。 ICMP 的报文类型:
这里再说说下,我原先一些误区: 1.ICMP没有端口,它并不像TCP/UDP那样有发到特定的接收端口,它是控制协议,服务于IP层。当某个网关发现传输错误时,立即向信源主机发 送 ICMP报文,报告出错信息,让信源主机采取相应处理措施,它是一种差错和控制报文协议,不仅用于传输差错报文,还传输控制报文。 2.ICMP 包发送是不可靠的。
code: .h #pragma once #include <winsock2.h> #include <ws2tcpip.h> #pragma comment(lib, "ws2_32.lib") #pragma pack(1) // ICMP 头部 typedef struct _ihdr { BYTE i_type; // 8位类型 BYTE i_code; // 8位代码 USHORT i_cksum; // 16位校验和 USHORT i_id; // 识别号 USHORT i_seq; // 报文序列号 ULONG timestamp; // 时间戳 }ICMP_HEADER; typedef struct _iphdr{ unsigned char h_lenver; // 4 位IP版本号+4位首部长度 unsigned char tos; // 8位服务类型TOS unsigned short total_len; // 16位IP包总长度(字节) unsigned short ident; // 16位标识, 用于辅助IP包的拆装,本实验不用,置零 unsigned short frag_and_flags; // 3位标志位+13位偏移位, 也是用于IP包的拆装,本实验不用,置零 unsigned char ttl; // 8位IP包生存时间 TTL unsigned char proto; // 8位协议 (TCP, UDP 或其他), 本实验置ICMP,置为1 unsigned short checksum; // 16位IP首部校验和,最初置零,等 // 所有包头都填写正确后,计算并替换. unsigned int sourceIP; // 32位源IP地址 unsigned int destIP; // 32位目的IP地址 }IP_HEADER; #pragma pack() class CIPing { public: CIPing(); ~CIPing(); // PING X.X.X.X BOOL Ping(LPCTSTR ip, int timeout); private: SOCKET s; }; .cpp #include "stdafx.h" #include "IPing.h" // 校验和 unsigned short CheckSum(unsigned short *addr, int count) { /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; while( count > 1 ) { /* This is the inner loop */ sum += *addr++; count -= sizeof(USHORT); } /* Add left-over byte, if any */ if( count > 0 ) sum += * (unsigned char *) addr; /* Fold 32-bit sum to 16 bits */ while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); return (USHORT)~sum; } CIPing::CIPing() { } CIPing::~CIPing() { } // PING X.X.X.X BOOL CIPing::Ping(LPCTSTR ip, int timeout) { struct sockaddr_in addrSrc; struct sockaddr_in addrDest; ICMP_HEADER head; UCHAR ucSendBuf[1024]; UCHAR ucRecBuf[1024]; int recFromLen; IP_HEADER *pIpHdr = NULL; unsigned short len; // create if((s = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET) { return FALSE; } // 发送时限 setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, ( char * )&timeout, sizeof( int ) ); // 接收时限 setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, ( char * )&timeout, sizeof( int ) ); memset(&addrSrc, 0, sizeof(sockaddr_in)); // addr addrSrc.sin_family = AF_INET; addrSrc.sin_addr.s_addr = inet_addr(ip); // 填充ICMP首部 memset(&head, 0, sizeof(ICMP_HEADER)); // 回显应答 head.i_type = 0; head.i_code = 0; // 校验和置0 head.i_cksum = 0; head.i_id = 2; // 时间戳 head.timestamp = GetTickCount(); head.i_seq = 999; memset(ucSendBuf, 0, 1024); memcpy(ucSendBuf, &head, sizeof(ICMP_HEADER)); memset(ucSendBuf + sizeof(ICMP_HEADER), '1', 32); head.i_cksum = CheckSum((USHORT*)ucSendBuf, sizeof(ICMP_HEADER) + 32); memcpy(ucSendBuf, &head, sizeof(ICMP_HEADER)); // 发送 if(sendto(s, (char*)ucSendBuf, sizeof(ICMP_HEADER) + 32, 0, (struct sockaddr*)&addrSrc,sizeof(addrSrc)) == SOCKET_ERROR) { closesocket(s); s = NULL; return FALSE; } memset(ucRecBuf, 0, 1024); memset(&addrDest, 0, sizeof(struct sockaddr)); recFromLen = sizeof(sockaddr); addrDest.sin_family = AF_INET; if (recvfrom(s, (char*)ucRecBuf, 1023, 0, (struct sockaddr*)&addrDest, &recFromLen) == SOCKET_ERROR) { if(WSAGetLastError() == WSAETIMEDOUT) { OutputDebugString("Time out...\n"); } closesocket(s); s = NULL; return FALSE; } closesocket(s); s = NULL; // 判断包的正确性 pIpHdr = (IP_HEADER *)ucRecBuf; // Number of 32-bit words * 4 = bytes // 计算ip包头长度 len = sizeof(unsigned long) * (pIpHdr->h_lenver & 0xf); memcpy(&head,& ucRecBuf[len], sizeof(ICMP_HEADER)); // 标识 if (head.i_id != 2) { return FALSE; } return TRUE; }
在用recvfrom收包的时候,发现都是失败(SOCK_ERROR),后面才发现参数传错了。见MSDN:
int recvfrom(
_In_ SOCKET s,
_Out_ char *buf,
_In_ int len,
_In_ int flags,
_Out_ struct sockaddr *from,
_Inout_opt_ int *fromlen
);
Parameters
s [in]
A descriptor identifying a bound socket.
buf [out]
A buffer for the incoming data.
len [in]
The length, in bytes, of the buffer pointed to by the buf parameter.
flags [in]
A set of options that modify the behavior of the function call beyond the options specified for the associated socket. See the Remarks below for more details.
from [out]
An optional pointer to a buffer in a sockaddr structure that will hold the source address upon return.
fromlen [in, out, optional]
An optional pointer to the size, in bytes, of the buffer pointed to by the from parameter.
今天在进行对一张单色bmp读取数据时,碰到了挺多的问题。 这里总结下。 1.对BMP的头部信息了解有误。BMP包含图像文件头,图像由于BMP中信息头,调色板数据,图像数据。 这里调色板数据的大小为N*4byte.这里需要注意的是调试板并不是所有的图片都有调试板的,需要取 决于biCompression,当biCompression成员的值是BI_RGB时,它没有调色板。而且biBitCount大 等于24的时候,没有调试版,biBitCount为32的时候,biCompression为BI_RGB时,没有调试板, 为BI_BITFIELDS原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、 绿、蓝分量在32位中所占的位置。具体的BMP的调试板相关问题就到这里,详细信息MSDN(BITMAPINFOHEADER), 如果想中文版的,可以百科http://baike.baidu.com/view/189487.htm 2.字节对齐问题 // 位图文件头 typedef struct tagBITMAPFILEHEADER { int16u bfType; // 位图文件的类型,必须为BM int32u bfSize; // 位图文件的大小,以字节为单位 int16u bfReserved1; // 位图文件保留字,必须为0 int16u bfReserved2; // 位图文件保留字,必须为0 int32u bfOffBits; // 位图数据的起始位置,以相对于位图 // 文件头的偏移量表示,以字节为单位 }BITMAPFILEHEADER; /*test.bmp 头部HEX 00000000h: 42 4D FE 3F 00 00 00 00 00 00 3E 00 00 00 28 00 ; BM?......>...(. 00000010h: 00 00 80 01 00 00 54 01 00 00 01 00 01 00 00 00 ; ..€...T......... 00000020h: 00 00 C0 3F 00 00 00 00 00 00 00 00 00 00 00 00 ; ..?............ 00000030h: 00 00 00 00 00 00 FF 00 00 00 00 00 FF 00 ; ............ */ code: fs.open("./test.bmp", ios::in); if(fs.is_open() == false) { printf("\r\n TestMid: open print_header.tmp failed... \r\n"); continue; } // 读取位图文件头 memset(&file_header, 0, sizeof(BITMAPFILEHEADER)); fs.read((char*)&file_header, 14); fs.seekg(file_header.bfOffBits); fs.read(...); .... 可是每次数据读出来什么都没有,加了各种调试信息。后面发现file_header.bfOffBits 为0, 这时候我就奇怪了......后面才想起字节对齐问题,平时也了解了字节对齐相关问题, 但是在实际编程中还是忽略了。 解决上面的问题: 方法一: fs.read((char*)&file_header.bfType, 2); fs.read((char*)&file_header.bfSize, 4); fs.read((char*)&file_header.bfReserved1, 2); fs.read((char*)&file_header.bfReserved2, 2); fs.read((char*)&file_header.bfOffBits, 4); 方法二: #pragma pack(1) /*指定按1字节对齐*/ // 位图文件头 typedef struct tagBITMAPFILEHEADER { int16u bfType; // 位图文件的类型,必须为BM int32u bfSize; // 位图文件的大小,以字节为单位 int16u bfReserved1; // 位图文件保留字,必须为0 int16u bfReserved2; // 位图文件保留字,必须为0 int32u bfOffBits; // 位图数据的起始位置,以相对于位图 // 文件头的偏移量表示,以字节为单位 }BITMAPFILEHEADER; #pragma pack()// 恢复 这里就不再重复字节问题了,具体可以见这个BLOG:http://www.cnblogs.com/logogcn/archive/2010/11/30/1891699.html,写的很好。
最近在学习IP数据包时,学习下了数据包校验和的计算方法
下面是RFC1071 source
unsigned short csum(unsigned char *addr, int count) { /* Compute Internet Checksum for "count" bytes * beginning at location "addr". */ register long sum = 0; while( count > 1 ) { /* This is the inner loop */ sum += * (unsigned short) addr++; count -= 2; } /* Add left-over byte, if any */ if( count > 0 ) sum += * (unsigned char *) addr; /* Fold 32-bit sum to 16 bits */ while (sum>>16) sum = (sum & 0xffff) + (sum >> 16); return ~sum; }
代码: .h #pragma once #include <WinSock.h> // ping 操作接口 typedef struct ip_option_information { UCHAR Ttl; UCHAR Tos; UCHAR Flags; UCHAR OptionsSize; PUCHAR OptionsData; } IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION; typedef struct icmp_echo_reply { ULONG Address; ULONG Status; ULONG RoundTripTime; // 往返时间 USHORT DataSize; USHORT Reserved; PVOID Data; IP_OPTION_INFORMATION Options; } ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; typedef HANDLE (WINAPI IcmpCreateFile)(VOID); typedef BOOL (WINAPI IcmpCloseHandle)(HANDLE IcmpHandle); typedef DWORD (WINAPI IcmpSendEcho)( // 传入由IcmpCreateFile创建的一个测试句柄 HANDLE IcmpHandle, // 要测试的IP地址,(需要用到inet_addr() 函数来转换) ULONG DestinationAddress, // 需要发送到IP地址的数据,传入指针 LPVOID RequestData, // 发送的数据大小 WORD RequestSize, // IP头选项,传入指针 PIP_OPTION_INFORMATION RequestOptions, // 测试后返回的数据,用icmp_echo_reply来接收 LPVOID ReplyBuffer, // ReplyBuffer的大小,一般是 sizeof(icmp_echo_reply) DWORD ReplySize, // 一个以MS为单位的值,代表着一个超时值 DWORD Timeout); class CPingI { public: CPingI(); ~CPingI(); BOOL Ping(int timeout, LPCTSTR pHost); private: HINSTANCE hIcmpDLL; IcmpCreateFile* lpIcmpCreateFile; IcmpCloseHandle* lpIcmpCloseHandle; IcmpSendEcho* lpIcmpSendEcho; }; .cpp #include "stdafx.h" #include "PingI.h" CPingI::CPingI() { // load hIcmpDLL = LoadLibrary("ICMP.DLL"); if (hIcmpDLL == NULL) { return; } lpIcmpCreateFile = (IcmpCreateFile*)GetProcAddress(hIcmpDLL,"IcmpCreateFile"); lpIcmpCloseHandle = (IcmpCloseHandle*)GetProcAddress(hIcmpDLL,"IcmpCloseHandle"); lpIcmpSendEcho = (IcmpSendEcho*)GetProcAddress(hIcmpDLL,"IcmpSendEcho"); if (lpIcmpSendEcho == NULL || lpIcmpCloseHandle == NULL || lpIcmpCreateFile == NULL) { return; } } CPingI::~CPingI() { if (hIcmpDLL != NULL) { FreeLibrary(hIcmpDLL); hIcmpDLL = NULL; } } // ping x.x.x.x BOOL CPingI::Ping(int timeout, LPCTSTR pHost) { IP_OPTION_INFORMATION ip_option; UCHAR sendData[128]; UCHAR recData[128]; HANDLE handle; PICMP_ECHO_REPLY pEchoReply; // 应答个数 int nReply; // error if (lpIcmpSendEcho == NULL || lpIcmpCloseHandle == NULL || lpIcmpCreateFile == NULL) { return FALSE; } memset(&ip_option, 0, sizeof(IP_OPTION_INFORMATION)); // TTL ip_option.Ttl = 128; // send Data memset(sendData, '1', 128); // rec Data memset(recData, 0, 128); pEchoReply = (PICMP_ECHO_REPLY)recData; // create handle handle = lpIcmpCreateFile(); if (handle == INVALID_HANDLE_VALUE) { return FALSE; } // send nReply = lpIcmpSendEcho(handle, inet_addr(pHost), sendData, 64, &ip_option, pEchoReply, sizeof(ICMP_ECHO_REPLY) + 64, timeout); // close lpIcmpCloseHandle(handle); if (nReply == 0) { // GetLastErr SetLastError(pEchoReply->Status); return FALSE; } return TRUE; }
使用TCP/IP协议栈指纹进行远程操作系统辨识
Fyodor (www.insecure.org)
1998-10-18
最后修改:1999-4-10
译者neko
2000-6-30
本文可以自由分发
概述
本文讨论了如何查询一台主机的TCP/IP协议栈来收集宝贵的信息。首先,我列举了栈指纹之外的几种“经典的”操作系统辨识方法。然后我描述了栈指纹工具的“工艺现状”。接下来说明让远程主机泄漏其信息的一些技术。最后详述了我的实现(nmap),和用它获得的一些流行网站的操作系统信息。
理由
我认为辨识一个系统所运行OS的用处是相当显而易见的,所以这一节会很短。最有力的例子之一是许多安全漏洞是OS相关的。试想你正在作突破试验并发现53端口是打开的。如果那是易遭攻击的bind版本, 则你只有一次机会利用它因为失败的尝试会杀死守护程序。有了正确的TCP/IP指纹,你将很快发现它运行的是’Solaris 2.51’或者’Linux 2.0.35而因此调整你的外壳代码。
一个比较糟的例子是某人扫描500,000台主机以找出它们运行什么OS
和哪些端口是打开的。然后等谁贴(说)有一个root漏洞在Sun的comsat
守护程序里,我们的小朋友能从人家的列表中找出 ’UDP/512’和’Solaris 2.6’
这两个词,并立即得到了整页整页的可得到root特权的盒子。必须认
识到那是脚本小子(SCRIPT KIDDIE)的行为。你证明了你的无能而
且没有人,甚至对方也,对你能找到没有及时修补漏洞而易受攻击的
.edu之事留有印象。人们也_较少_留有印象如果你用你新找到的通路
去破坏政府的web 站,换以一个自大的你如何强大而管理员们如何愚
蠢的话的话。。
另一个用法是社会工学。假如你扫描目标公司而namp报告一个’Datavoice
TxPOR TPRISM 3000 T1 CSU/DSU 6.22/2.06’。则黑客就以’Datavoice
support’为名打电话并讨论他们PRISM 3000的一些问题。“我们正要
公布一个安全漏洞,但希望我们现在的客户先安装补丁--我刚刚寄给
你...”一些天真的管理员会假定只有Datavoice指定的工程师才会对
他们的CSU/DSU知道的如此之多。
这个能力另一个潜在的用途是评价你要交易的公司。在选择一个新ISP
前,扫描它们看用的是什么设备。那些“ 99美元/年”的买卖不向听
起来那么好当你发现它们用廉价的路由器并用一堆运行Windows 的机
器提供PPP 服务的时候。
经典技术
栈指纹以独特的方式解决OS辨识的问题。我想这个技术最有把握,但
现在有许多其他解决方案。遗憾的是,这仍是其中最有效的:
playground~> telnet hpux.u-aizu.ac.jp
Trying 163.143.103.12...
Connected to hpux.u-aizu.ac.jp.
Escape character is ’^]’.
HP-UX hpux B.10.01 A 9000/715 (ttyp2)
login:
没有必要在指纹上费这么大力气,如果机器能大声对世界说明它们运
行的是什么!遗憾的是,许多制造商交付带有这类标志的_现有的_系
统而且许多管理员没有关上它。[译者:原文如此]只是因为还有其他
方法找出运行的OS(例如指纹),但不是说我们应该通知每个尝试连
接的笨蛋我们的OS和体系结构。
依靠这个技术的问题是越来越多的人把标志关闭,许多系统不给出更
多信息,还有少数在标志中“说谎”。不过,你得到全部就是读标志
方式的OS和OS版本检查,如果你把上千美元花在商业的ISS 扫描器上
的话。下载nmap或queso代替它们可以替你省钱:)。
即使你关闭了标志,当被询问时许多应用程序仍然很高兴给出这类信
息。例如这个FTP服务器:
payfonez> telnet ftp.netscape.com 21
Trying 207.200.74.26...
Connected to ftp.netscape.com.
Escape character is ’^]’.
220 ftp29 FTP server (UNIX(r) System V Release 4.0) ready.
SYST
215 UNIX Type: L8 Version: SUNOS
首先,它给出了它默认的系统细节标志。然后如果我们给出’SYST’命
令它愉快地送回更多信息。
如果FTP的anon被支持,我们经常可以下载/bin/ls或其他的二进制文
件而测定它所建造的体系结构。
许多其他应用程序对信息太随便了。比如web服务器:
playground> echo ’GET / HTTP/1.0\n’ | nc hotbot.com 80 | egrep ’^Server:’
Server: Microsoft-IIS/4.0
playground>
Hmmm ... 我对这些家伙运行的感到惊讶。
其他经典技术包括DNS 主机信息记录(不太有效)和社会工学。如果
它在听161/udp (snmp),用CMU SNMU工具包里的’snmpwalk’和’public’
通信名你肯定能获得一大堆信息。
当前指纹问题
Nmap不是第一个用TCP/IP指纹辨识OS的程序。Johan的通用的IRC欺骗
程序sirc包括了非常基本的指纹技术从版本3 (或更早)开始。它尝
试把主机分为 "Linux","4.4BSD", "Win95", 或 "Unknown"几类通过
几个简单TCP标志测试。
另一个这样的程序是checkos,作者Shok对版本7终于有了信心在今年
一月公开发行。指纹技术和SIRC的完全一样,甚至_代码_都有许多同
样之处。Checkos 在公开发行前私下流传了很久,所以不知谁偷谁的。
但看起来谁都不信任对方。checkos增加的是telnet 标志检查,有用
但有前面说的问题。[更新:Shok写信来说checkos无意公开发行而这
就是为什么他没有找SIRC要那些代码的许可。]
Su1d也写了一个OS检查程序。他称它叫SS其版本3.11可以辨识12个不
同的OS类型。我有些偏爱它因为他许可我的nmap程序一些网络代码:)。
然后是queso 。这是最新的而且对其他程序是一个巨大的飞跃。不仅
是因为它们推出了一些新测试,而且它们是首先(就我所见)把OS指
纹_移出_代码的。其他扫描的代码象:
/* from ss */
if ((flagsfour & TH_RST) && (flagsfour & TH_ACK) && (winfour == 0) &&
(flagsthree & TH_ACK))
reportos(argv[2],argv[3],"Livingston Portmaster ComOS");
相反,queso 把这些代码移到一个配置文件显然使更易扩展而且使增
加一个OS成为在指纹文件中增加几行的简单工作。
Queso由Savage,Apostols.org的高手之一,所写。
以上所述所有问题中的一个问题是只有非常有限数量的指纹测试从而
限制了回答的详细程度。我想知道不仅是’这台机器是OpenBSD, FreeBSD,
或者NetBSD’,我想确切知道它到底是那一个还有版本号。同样,我希
望看到’Solaris 2.6’ 而不仅仅是’Solaris’。为此,我对一系列指纹
技术进行了研究,它们将在下一节说明。
指纹方法学
有许多许多技术可以用来定义网络栈指纹。基本上,你只要找出操作
系统间的不同并写探测器查明它们。如果你合并足够这些,你可以非
常细致的区分它们。例如nmap可以可靠分辨出Solaris 2.4 和Solaris 2.5-2.51
以及Solaris 2.6。他能分辨Linux内核2.0.30到2.0.31-34或or 2.0.35。
这有一些技术:
FIN 探测器 -- 这里我们送一个FIN包(或任何其他包不带ACK 或SYN
标记)给一个打开的端口并等待回应。正确的RFC793行为是不
响应,但许多有问题的实现例如 MS Windows, BSDI, CISCO,
HP/UX,MVS,和IRIX 发回一个RESET。许多现有工具利用这个技
术。
BOGUS 标记探测器 -- Queso 是我见过的第一个用这个聪明技术扫描
器。这个主意是设置一个未定义的TCP "标记"(64或128)在SYN
包的TCP头里。Linux机器到2.0.35之前在回应中保持这个标记。
我没有发现其他OS有这个错误。然而,一些操作系统象是复位
连接当它们得到一个SYN+ BOGUS包的时候。这一行为对辨识它
们有用。
TCP ISN 取样 -- 这个主意是找出当响应一个连接请求时由TCP 实现
所选择的初始化序列数式样。这可分为许多组例如传统的64K
(许多老UNIX机器),随机增量(新版本的Solaris, IRIX, FreeBSD,
Digital UNIX, Cray, 和许多其他的),真“随机”(Linux 2.0.*,
OpenVMS,新的AIX,等)。Windows 机器(和一些其他的)用一
个“时间相关”模型,每过一段时间ISN 就被加上一个小的固
定数。不用说,这几乎和老的64K 行为一样容易攻破。当然我
喜欢的技术是"常数"。机器总是使用确切同样的ISN :)。我已
经在3Com的集线器(用0x803)和Apple LaserWriter打印机(
用0xC7001 )上看到了。
你也可以通过例如计算其随机数的变化量,最大公约数,以及
序列数的其他函数和数之间的差异再进一步分组。
要知道ISN 的生成和安全息息相关。想了解更多情况,联络在
SDSC的“安全专家”Tsutome Shimmy Shimomura,问他所知道
的。Nmap是我所见到的第一个用它来辨识OS的程序。
不分段位 -- 许多操作系统开始在送出的一些包中设置IP的"Don’t Fragment"
位。这带来多种性能上的好处(尽管它也可能是讨厌的 -- 这
就是nmap的分段扫描对Solaris机器无效的原因)。无论如何,
不是所有的OS都这样做而且另一些做的场合不同,所以通过注
意这个位我们甚至能收集目标OS的更多信息。在那两个程序中
没见过这个。
TCP 初始化窗口 -- 这只包括了检查返回包的窗口大小。较老的扫描
器简单地用一个非零窗口在RST包中来表示“BSD 4.4 族”。新
一些的如queso 和nmap则保持对窗口的精确跟踪因为它对于特定
OS基本是常数。这个测试事实上给出许多信息,因为有些可以被
唯一确定(例如,AIX 是所知唯一用0x3F25的)。在它们“完全
重写”的NT5 TCP 栈中,Microsoft 用的是0x402E。有趣的是,
这和OpenBSD 与FreeBSD 中所用的数字完全一样。
ACK 值 -- 尽管你会认为这个会完全标准,不同实现中一些情况下ACK
域的值是不同的。例如,如果你送了一个FIN|PSH|URG 到一个
关闭的TCP 端口。大多数实现会设置ACK 为你的初始序列数,
而Windows 和一些傻打印机会送给你序列数加1 。若你送一个
SYN|FIN|URG|PSH 到一个打开的端口,Windows 会非常古怪。
一些时候它送回序列号,但也有可能送回序列号加1, 甚至还
可能送回一个随机数。我们觉得奇怪,不知微软写的是些什么
代码。
ICMP 错误信息终结 -- 一些(聪明的)操作系统跟从RFC 1812的建
议限制各种错误信息的发送率。例如,Linux 内核(在net/ipv4/icmp.h)
限制目的不可达消息的生成每4 秒钟80个,违反导致一个1/4
秒的处罚。测试的一种办法是发一串包到一些随机的高UDP 端
口并计数收到的不可达消息。没见过用它的,而且实际上我也
没有把它加进nmap(除了作为UDP 端口扫描用)。这个测试会
让OS辨识多花一些时间因为需要送一批包再等它们回来。而且
对付网络丢包会很痛苦。
ICMP 消息引用 -- RFC 规定ICMP错误消息可以引用一部分引起错误
的源消息。对一个端口不可达消息,几乎所有实现只送回IP请
求头外加8 字节。然而,Solaris 送回的稍多,而Linux 更多。
这使得nmap甚至在没有对方没有监听端口的情况下认出Linux
和Solaris 主机。
ICMP 错误消息回应完整性 -- 我这个想法来自Theo De Raadt (OpenBSD
开发负责人)贴在comp.security.unix的文章。刚刚提到,机
器会把原始消息的一部分和端口不可达错误一起送回。然而一
些机器倾向于在初始化处理时用你的消息头作为“草稿纸”所
以再得到时会有些许的改动。例如,AIX 和BSDI送回一个IP“
全长”域在20字节处。一些 BSDI,FreeBSD,OpenBSD,ULTRIX,
和VAXen 改变了你送的IP ID 。因为TTL 改变而改变了检查和,
有些机器(AIX, FreeBSD, 等)送回错误的或0 检查和。总之,
nmap作9 种测试在ICMP错误上以分辨出这类细微差别。
服务类型 -- 对于ICMP端口不可达消息我察看送回包的服务类型(TOS)
值。几乎所有实现在这个ICMP错误里用0 除了Linux 用0xc0。
这不是标准的TOS 值,而是一个未使用优先域(AFAIK) 的一部
分。我不知道为什么如此,但如果他们改成0 我们还能够分辨
旧系统_而且_还能分辨出旧系统和新系统。
分段控制 -- 这是安全网络公司(现在由一帮在NAI 的Windows 用户
所拥有)的Thomas H. Ptacek喜爱的技术。它获益于事实即不
同实现经常以不同方式控制覆盖IP段。一些会用新的覆盖旧的
部分,另一些情况中旧的优先。有很多不同可能你可以用来决
定如何重组数据包。我没有加入这一特性因为没有简便的方式
发送IP分段(特别是,在Solaris 上是不允许的)。关于覆盖
段的更多信息,可以看IDS 的论文(www.secnet.com)
TCP 选项 -- 这简直是泄漏信息的金矿。它的好处在于:
1) 这通常是可选的(哈!):) 所以并非所有实现都支持。
2) 若一个实现发出设置了选项的请求,目标通过设置它在回
应中表示支持。
3) 可以在一个数据包中设置而一次测试所有选项。
Nmap发送这些选项的几乎所有可能的包:
Window Scale=10; NOP; Max Segment Size = 265; Timestamp; End of Ops;
当你得到回应,看看那个选项被送回也就是被支持。一些操作
系统如最近的FreeBSD 机器支持上面所有的,而其他,如Linux 2.0.X
支持的则很少。最近的Linux 2.1.x 内核支持上面所有的。另
一方面,它们又有更易受攻击的TCP 序列生成方式。去看看。
即使几个操作系统支持同样的选项集,有时仍可以通过选项的
_值_分辨出它们。例如,如果送一个小的MSS值给Linux机器,
它会用那个MSS 生成一个回答给你。其他主机会给你不同的值。
甚至即使你得到同样的支持选项集和同样得值,你仍可以通过
选项提供的_顺序_和填充字进行辨识,例如Solaris返回’NNTNWME’
表示:
而Linux 2.2.122返回MENNTNW。同样的选项,同样的值,但不
同顺序!
没见过其他OS检测工具利用TCP 选项,但它非常有用。
因同样原因有其他几个有用的选项我会探测,象那些支持T/TCP
和选择性确认。
开发年代 -- 甚至使用上面所有测试,nmap仍不能从TCP 栈区分Win95,
WinNT,或Win98。这很令人惊讶,尤其是Win98 比Win95 晚出
现4 年。你可能想它们不得不从某些方面进行改善(象支持更
多的TCP 选项)这样我们可以检测到改变而分辨出它们。不幸
的是,不是这样的。NT的栈显然就是放在95里的蹩脚的东西。
而且到98也没加改动。
但别放弃希望,还有个办法。你可以简单的进行早期的Windows
DOS 攻击(Ping of Death, Winnuke, 等)而比当时的如Teardrop
和Land多做一些。就是在每个攻击之后,ping它们看是否垮
掉了。等到你最后crash 掉它们,你就能缩小的某一服务包
或补丁。
这个没加进nmap,尽管我承认它非常诱人:)。
SYN洪水限度 -- 一些操作系统会停止新的连接尝试如果你送太多的
伪造SYN 给它(伪造包避免你的内核复位连接)。许多操作系
统只能处理8 个包。最近的Linux 内核(包括其他操作系统)
允许不同的方式如SYN cookie来防止这成为严重问题。所以你
可以试着从伪造地址发8 个包到目标打开的端口再尝试你还能
否建立连接以发现一些信息。这没在nmap中实现因为有人不喜
欢你用SYN 洪水,甚至你解释这只是想知道它运行的操作系统
也不能使他们平静。
NMAP实现和结果
我已经作了一个上面说的OS探测技术的参考实现(除了我说不包括的)。
我把它们加到了我的Nmap扫描器这样在分析指纹时它已经知道了什么
端口是打开还是关闭的而不用你再告诉。它也能在Linux,*BSD, 和
Solaris 2.51和2.6, 以及其他一些操作系统间移植。
新版的nmap读一个指纹模板文件。下面是语法的例子:
FingerPrint IRIX 6.2 - 6.4 # Thanks to Lamont Granquist
TSeq(Class=i800)
T1(DF=N%W=C000|EF2A%ACK=S++%Flags=AS%Ops=MNWNNT)
T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=)
T3(Resp=Y%DF=N%W=C000|EF2A%ACK=O%Flags=A%Ops=NNT)
T4(DF=N%W=0%ACK=O%Flags=R%Ops=)
T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=)
T6(DF=N%W=0%ACK=O%Flags=R%Ops=)
T7(DF=N%W=0%ACK=S%Flags=AR%Ops=)
PU(DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E)
看第一行(我加了’>’标记):
> FingerPrint IRIX 6.2 - 6.3 # Thanks to Lamont Granquist
这简单表明这个指纹覆盖IRIX版本6.2到6.3而注释表明Lamont Granquist
友好地送给我测试用IRIX机器的IP地址或指纹。
> TSeq(Class=i800)
这表明ISN 取样放在"i800组"。这意味着每一个新序列号比前一个大
800的整数倍。
> T1(DF=N%W=C000|EF2A%ACK=S++%Flags=AS%Ops=MNWNNT)
这个测试叫T1(比test1 聪明吧?)。这个测试我们送一个SYN 包带
一组TCP 选项到一个打开的端口。DF=N意为回答的"Don’t fragment"
位必须没有设置。W=C000|EF2A意为收到的窗口特征必须是0xC000 或
EF2A。ACK=S++是说响应必须是我们送的序列号加1 。Flags=AS 意为
ACK 和SYN 标记在回答中。Ops=MNWNNT意为回答的选项必须如此顺序:
> T2(Resp=Y%DF=N%W=0%ACK=S%Flags=AR%Ops=)
测试2 包括一个NULL及同样选项到一个打开的端口。Resp=Y表示我们
必须得到一个回答。Ops=表示必须没有任何选项包括在回答中。若整
个用’%Ops=’则任何选项都匹配。
> T3(Resp=Y%DF=N%W=400%ACK=S++%Flags=AS%Ops=M)
测试3 是一个SYN|FIN|URG|PSH w/options 到一个打开端口。
> T4(DF=N%W=0%ACK=O%Flags=R%Ops=)
这是一个到打开端口的ACK。注意这儿没有Resp=。这意味着缺少回答
(比如包在网络上掉了或被防火墙拦住了)不会妨碍其他测试的匹配。
我们如此是因为实际上所有的OS都会回答,所以缺少回答总是网络原
因而不是OS本身造成。测试2和3里有Resp标记因为有OS_确实_丢弃它
们而不回答。
> T5(DF=N%W=0%ACK=S++%Flags=AR%Ops=)
> T6(DF=N%W=0%ACK=O%Flags=R%Ops=)
> T7(DF=N%W=0%ACK=S%Flags=AR%Ops=)
这些测试是SYN,ACK,和FIN|PSH|URG, 分别地,到一个关闭端口。
总是设置同样的选项。当然这显然给了名字’T5’, ’T6’, 和 ’T7’ :)。
> PU(DF=N%TOS=0%IPLEN=38%RIPTL=148%RID=E%RIPCK=E%UCK=E%ULEN=134%DAT=E)
这大家伙是端口不可达消息测试。现在你应该认识DF=N了。TOS=0 意
为IP服务域类型是0 。下两个域给出(16进制)返回的消息头IP全长
域和IP头中给的全长。RID=E 是说在返回的部分原始包中的RID 值应
和原来的一样(就如我们送出的)。RIPCK=E 表示没有修改检查和(
改了的话用RIPCK=F)。UCK=E表示UDP 检查和也正确。下面的是UDP
长度0x134 和DAT=E 表示它们正确返回我们的UDP 数据。因为大多数
实现(包括这个)不送回任何我们的UDP 数据包,它们默认DAT=E。
带这个功能的这个版本的nmap正在私下进行第六次beta测试循环。你
读到的时候,也许已经完成,也许没有。访问http://www.insecure.org/nmap/
以得到最新版本。
流行网站快照
下面是我们努力的成果。我们现在可以随机挑选Internet网站判断它
使用的OS。它们许多修改了telnet标志,等。以使这些信息保密。但
这对我们的新指纹没用!这也是好办法揭露<填上你喜欢的傻OS>的用
户是多么的愚蠢。:)
用在这些例子中的命令是:nmap -sS -p 80 -O -v <主机>
也要注意大多数扫描是在98-10-18进行的。那以后一些家伙会升级/
改变了它们的服务器。
注意我并不喜欢这的每个网站。
# "黑客" 网站或(两方都是)自认为是的
www.l0pht.com => OpenBSD 2.2 - 2.4
www.insecure.org => Linux 2.0.31-34
www.rhino9.ml.org => Windows 95/NT # 没的说 :)
www.technotronic.com => Linux 2.0.31-34
www.nmrc.org => FreeBSD 2.2.6 - 3.0
www.cultdeadcow.com => OpenBSD 2.2 - 2.4
www.kevinmitnick.com => Linux 2.0.31-34 # Free Kevin!
www.2600.com => FreeBSD 2.2.6 - 3.0 Beta
www.antionline.com => FreeBSD 2.2.6 - 3.0 Beta
www.rootshell.com => Linux 2.0.35 # 改成了 OpenBSD 在他们得到以后
# 安全提供商,顾问,等
www.repsec.com => Linux 2.0.35
www.iss.net => Linux 2.0.31-34
www.checkpoint.com => Solaris 2.5 - 2.51
www.infowar.com => Win95/NT
# OS制造商
www.li.org => Linux 2.0.35 # Linux 国际版
www.redhat.com => Linux 2.0.31-34 # 我奇怪它们发行什么 :)
www.debian.org => Linux 2.0.35
www.linux.org => Linux 2.1.122 - 2.1.126
www.sgi.com => IRIX 6.2 - 6.4
www.netbsd.org => NetBSD 1.3X
www.openbsd.org => Solaris 2.6 # 啊嗨 :)
www.freebsd.org => FreeBSD 2.2.6-3.0 Beta
# 学联
www.harvard.edu => Solaris 2.6
www.yale.edu => Solaris 2.5 - 2.51
www.caltech.edu => SunOS 4.1.2-4.1.4 # Hello! 这是90年代的 :)
www.stanford.edu => Solaris 2.6
www.mit.edu => Solaris 2.5 - 2.51 # 这么多好学校喜欢SUN?
# 大概是给.edu打40%的折扣 :)
www.berkeley.edu => UNIX OSF1 V 4.0,4.0B,4.0D
www.oxford.edu => Linux 2.0.33-34 # 好家伙!
# 残疾网站
www.aol.com => IRIX 6.2 - 6.4 # 不奇怪它们那么不安全 :)
www.happyhacker.org => OpenBSD 2.2-2.4 # 病态的, Carolyn?
# 甚至最安全的OS在不合格的管理员手里也没用
# 其他
www.lwn.net => Linux 2.0.31-34 # 这是Linux新闻网站!
www.slashdot.org => Linux 2.1.122 - 2.1.126
www.whitehouse.gov => IRIX 5.3
sunsite.unc.edu => Solaris 2.6
注意:在它们的安全白皮书中,Microsoft 说到它们松懈的安全:“
这种假设已经改变在这些年中Windows NT获得普及很大程度上是由于
其安全特性”。喔,从我这里看Windows 在安全团体中不是很普遍:)。
从中我只看到2 台Windows 机器,而且对nmap来说Windows 是_ 容易_
分辨的它太破了(一流的聪明)
当然,有更多需要检查的。这是ultra-secret Transmeta公司的网站。
有趣的是该公司主要由微软的Paul Allen建立,而非其雇员Linus Torvalds。
那么它们是和Paul用NT或者投身到Linux革命中去呢?我们看看:
我们用命令:
nmap -sS -F -o transmeta.log -v -O www.transmeta.com/24
这个说SYN 扫描一致端口(从/etc/services),记录结果到’transmeta.log’,
详细地,进行OS扫描,并扫描www.transmeta.com所在的’C’地址。这
是结果摘要:
neon-best.transmeta.com (206.184.214.10) => Linux 2.0.33-34
www.transmeta.com (206.184.214.11) => Linux 2.0.30
neosilicon.transmeta.com (206.184.214.14) => Linux 2.0.33-34
ssl.transmeta.com (206.184.214.15) => Linux unknown version
linux.kernel.org (206.184.214.34) => Linux 2.0.35
www.linuxbase.org (206.184.214.35) => Linux 2.0.35 可能和上面的是同一台机器
好,我想结果很清楚了:)。
感谢
现在Nmap能够检测如此多不同的操作系统的唯一原因是那么多的人在
个人beta测试中努力找到新的激动人心的机器来获取指纹!特别是,
Jan Koum, van Hauser, Dmess0r, David O’Brien, James W. Abendschan,
Solar Designer, Chris Wilson, Stuart Stock, Mea Culpa,Lamont Granquist,
Dr. Who, Jordan Ritter, Brett Eldridge, 和Pluvius 送来成吨的
古怪机器的IP地址和/或不能通过Internet访问的机器的指纹。
感谢Richard Stallman些的GNU Emacs。 本文不能顺利自动换行如果
我用vi或cat 和^D的话。
问题和意见可以送到fyodor@insecure.org(如果它不工作了,用fyodor@insecure.org)。
Nmap可以从获得http://www.insecure.org/nmap。
------
译者注:
1>原文在:http://www.insecure.org/nmap/nmap-fingerprinting-article.txt
2>原文还有法语、葡萄牙语、意大利语、俄语、西班牙语、德语译本
3>对译文的任何意见请来信neko@126.com
英文版本地址:Remote OS detection via TCP/IP Stack FingerPrinting
http://www.eviloctal.com/forum/htm_data/84/0412/5208.html
http://www.eviloctal.com/forum/htm_data/48/0601/17981.html
http://www.insecure.org/nmap/nmap-fingerprinting-article.txt
从未接触过仿函数,这个也是这两天才接触的。它主要是一个class重载了“()”来达到目的。直接见代码吧
// 仿函数 struct max_class { public: int operator()(int x, int y) { return x > y ? x : y; } }; template <class T> int get_max(int x, int y, T t) { return t(x,y); } int main() { cout << get_max(11,2,max_class()) << endl; }
本文转【http://blog.csdn.net/byxdaz/article/details/4577113】说的很在理
知识上的投资总能得到最好的回报。
——本杰明·富兰克林
管理知识资产与管理金融资产非常相似:
严肃的投资着定期投资——作为习惯
多远化是长期成功的关键。
聪明的投资着在保守的投资和高风险、高回报的投资之间平衡他们的资产。
投资者设法低买高卖,以获取最大回报。
应周期性地重新评估和平衡资产。
如何经营你的知识资产:
定期投资。就像金融投资一样,你必须定期为你的知识资产投资,即使投资量很小,习惯自身也和总量一样重要。
多元化。你知道的不同的事情越多,你就越有价值。作为底线,你需要知道你目前所用的特定技术的各种特性。但不要就此止步。计算机技术的面貌变化很快——今天的热门技术明天就可能变得近乎无用(至少是不再抢手),你掌握的技术越多,你就越能更好地进行调整,赶上变化。
管理风险。从高风险、可能有高回报,到低风险、低回报,技术存在于这样一条谱带上,把你所有的金钱都投入可能突然崩盘的高风险股票并不是一个好主意;你也不应太保守,错过可能的机会。不要把你所有的技术鸡蛋放在一个篮子里。
低买高卖。在新兴的技术流行之前学习它可能就和找到被低估的股票一样困难,但是得到的就和那样的股票带来的收益一样。在Java刚出现时学习它可能有风险,但对于现在已步入该领域的顶尖行列的早期采用者,这样做到了非常大的回报。
重新评估和平衡。这是一个非常动荡的行业,你上个月开始研究热门技术现在也许已像石头一样冰冷。也许你需要重温你有一阵子没有使用的数据库技术,又或许,如果你之前试用过另一种语言,你就会更有可能获取那个新职位……
提出下列目标,可能更有实践意义:
●每年学习一种新语言。
不同语言能够以不同方式解决同样的问题。而学习各种不同的方法,能够扩宽思路,打破思维局限。学习不同的语言将改变和丰富你思考问题的方式。而且,现在学习新语言比过去要简单得多了。编译器、开发环境、文档都可以自由从网上找到。在一种语言上编程,但别为其束缚了思想。“代码大全”中说:“深入一门语言编程,不要浮于表面”。深入一门语言开发还远远不足,任何编程语言的存在都有其自身的理由,所以也没有哪门语言是“包治百病”的“灵丹妙药”。编程语言对开发人员解决具体问题的思路和方式的影响与束缚的例子俯拾皆是。我的经验是:用面对对象工具开发某些关键模块时,为什么不可以借鉴C、C51、汇编的模块化封装方式?用传统的桌面开发工具(目前主要有VC++、Delphi)进行系统体统结构设计时,为什么不可以参考来自Java社区的IoC、AOP设计思想,甚至借鉴像Spring、Hibernate、JBoss等等优秀的开源框架?在进行类似于实时通信、数据采集等功能的设计、实现时,为什么不可以引用来自实时系统、嵌入式系统的优秀的体系框架与模式?为什么一切都必须以个人、团队在当然开发语言上的传统或者经验来解决问题???“他山之石、可以攻玉”。
●每个季度读一本技术图书。
养成习惯以后,每个月读一本。同样,阅读兴趣也应该广一些,多元化。掌握一门陌生的学问远远没有想想的那么高难、深奥。多方吸取、广泛涉猎。极力夯实自己的影响圈、尽量扩大自己的关注圈。财务、经济、税务、管理等等知识,有空花时间看看,韬光养晦、未雨绸缪。
●还要阅读非技术图书。
不要忘了软件是人在使用,多多了解人这方面的事情。
●参加技术课程。
可以是学校的,也可以是培训班或者技术会议上的。
●加入本地的程序员组织。
不仅是带着耳朵去听,而是要积极参与。“与世隔绝对职业生涯是致命的。” 去发现公司之外的朋友。
●实验不同的环境。
如果你工作中只使用Windows,那么在家里用用Unix/Linux。如果你只用makefile和编辑器,尝试一下IDE,或者反之。
●与时俱进,订阅行业杂志。
●上网,阅读有价值的文章、网站……
最后一条现在已经越来越重要,很大程度已经取代了上一条。现在国外主流的技术杂志,不少已经完全开放在网上,包括书中推荐的Dr. Dobb's Journal。
●多做笔记,总结,采用Wiki或者blog的形式。有意识地提炼日常工作成果,形成自己的个人源码库、解决某类问题的通用系统体系结构、甚至进化为框架。
●心态有多开放,视野就有多开阔。不要抱着自己的技术和成果,等到它们都已经过时变成垃圾了,才拿出来丢人现眼。请及时发布自己的研究成果:开发的产品、有创意的设计或代码,公布出来让大家交流或者使用,你的成果才有进化和升华的机会。
●尽量参加开源项目的开发、或者与朋友共同研制一些自己的产品,千万不要因为没有钱赚而不做。网络早已不再只是“虚拟世界”,网上有很多的开源项目、合作开发项目、外包项目,这都是涉猎工作以外的知识的绝好机会,并且能够结识更广的人缘。不要因为工作是做ERP,就不去学习和了解嵌入式、实时、通信、网络等方面的技术,反过来也是一样。如果当他别人拿着合同找你合作,你却这也不会,那也不熟时,你将后悔莫及。
●提高自己的专业知识,但能胜任工作。提高软件知识和技术只是问题的表面,本质是要提高自己认识问题、分析问题、解决问题的思想高度。软件专业知识的很多方法和原理,可以很容易地延伸、应用到生活的其它方面。在能胜任工作的基础上,立即去涉猎其它领域的专业知识,丰富自己的知识体系、提高自己的综合素质,尤其是那些目标不在技术方面的朋友。
这是在is-programmer的第一篇blog,拉宾米勒算法由一同学提供,谢谢他!
#define type int type get_rand() { type a; a = rand(); a* = rand(); return abs(a); } /******a^b mod c *******/ type PowerMod(type a, type b, type c) { type r =1 ; while(b) { if( b & 1 ) { r = r * a % c; } b >>= 1; a = a * a % c; } return r; } /********* 素数随机判定算法 *******/ type mod_mul (type x , type y , type n) { type T =(type)( sqrt((double)n) + 0.5); type t = T * T - n; type a = x / T; type b = x %T; type c = y / T; type d = y % T; type e = a * c / T; type f = a * c % T; type v = (( a * d + b * c) % n + e * t ) % n; type g = v / T; type h = v % T; type ans = ((( f + g) * t % n + b * d) % n + h * T) % n; while ( ans < 0) ans += n; return ans ; } /* 米勒-拉宾法算法 */ type Rabin_Miller(type n) { type k = 0, i, j, m, a; if( n < 2) return 0; if(n == 2) return 1; if( !(n & 1) ) return 0; m = n - 1; while( !( m & 1 ) ) m >>= 1, k++; for(i = 0; i < 10; i++) { a = get_rand() % (n - 2) + 2; a = PowerMod(a, m, n); if(a == 1) continue; for(j = 0; j < k; j++) { if(a == n-1) break; a = mod_mul(a, a, n); } if(j < k) continue; return 0; } return 1; } /* 获取特定长度的随机素数 */ type Get_Rand_Prime(type bytes) { type i; type prime; bytes = 0; prime = 0; i = 0; while (1) { prime = 0; i = bytes; while(i--) { prime = prime * 10 + rand() % 10; if (i ==( bytes - 1) && prime == 0) { prime = 1; } } prime = prime | 0x01; if (Rabin_Miller(prime)) { break; } } return prime; }