netStat逆向分析
netStat和Fport的功能差不多,据说xp上比fport少某些功能,但是我的xp可能比较特殊,fport的功能netStat上的都有,一头雾水地抓起WIn7的netStat就开始分析。
目标是五个功能
- 本地地址 [ip + 端口]
- 外部地址 [ip + 端口]
- 状态
- PID
- 所有权信息
还是先通过IDA和OD,找到程序关键的函数 DoConnection():
主要的逻辑就在这儿,先通过InternalGetTcpTableWithOwnerModule()获取Tcp表,这点和Fport的AllocateAndGetTcpExTableFromStack分厂相似,这两个函数都属于 iphlpapi.dll,只不过后者最高支持xp系统,而前者在xp后才支持。
通过OD调试查看输出信息可以确定所有有用的信息应该都在这个表里面,在MSDN上没有查到(能力问题)合适的数据结构,还可以发现输出是在DisplayTcpConnEntry里完成的,每一个while循环输出一个表项,所以确定了while循环的退出条件也就是返回的表的第一个四字节显示了表中表项的个数。最开始以为DisplayTcpConnEntry的参数就是具体的ip、port之类的,后来发现其实是传入了表项的首地址,通过IDA反的C代码可以确定第一个表项是从返回的表偏移两个四字节处开始,并且表项长度为0XA0个字节,由此可以确定数据结构的结构。进入displayTcpConnEntry查看。
通过与输出信息的比对可以发现v6为本地地址port,v7为本地地址ip,所以表项偏移一个四字节为本地ip,偏移两个四字节为port的大端存储【第一项√】(事后发现高兴的太早了)。
然后是一长串的switch,通过比对函数的返回值发现执行了这个函数之后出现了状态字串(如LISTENLING),然后发现给FormatMessage()传入适当参数可以返回这个状态字串(这儿偷了个懒没有调用这个接口,通过更改即时的数据获得了TCP表项中的数字与状态的关系)【第三项√】,之后的函数调用发现他喵的有点眼熟啊
这他喵的就是传本地ip和port的那个函数啊,大胆猜测是外部地址好吧【第二项√】(还是太天真)。
之后调用了一个output的函数输出了上述的三种信息,继续跟踪发现在DisplayOwnerInfo里面输出了pid和所有权信息,进入
在里面看到一个老朋友,dwProcessId就是pid,而这个参数是函数的形参,再回去看函数调用
哦你他吗就是偏移五个四字节嘛【第四项√】,回到displayOwnerInfo()
K32GetModuleBaseNameA()就返回了所有者的那个*.exe的信息【第五项√】,这儿有个判断,如果是svchost.exe或者rundll32.exe就会输出相应的模块信息,用后面的那啥不说的打开一个advapi32.dll然后使用I_QueryTagInformation,这儿偷懒了没做这个功能
至此Tcp表所有信息解析完毕,与此类似的还有Tcp6表、Udp表、Udp6表。感觉马上就可以写程序好吧,但是
傻逼了吧,内网地址外网地址输出千变万化,特别是TCP6、UDP6的输出信息,咋这么奇怪呢。
回到刚才传入ip和port的那个函数,刚才我们只是表面推断了ip和port的存入地址,看来并非所有情况都是如此,OD动态跟进定位字符串获取的API
获取IP字串的逻辑就是这一坨,靠getnameinfo()
这个API可以返回host name和service name,返回的字串就存在host里面,通过动态跟踪观察发现netstat在-abno的参数下主要信息就只是通过第一个getnameinfo返回,懒了说实话突然没那个耐心去分析什么情况下会调用其他的getNameInfo,大胆猜测在这儿其他的没用,接下来是确定参数,第一个参数是一个结构体指针比较重要,里面就存放了输入信息,第二个指针是sizeof(struct sockaddr),第三个参数outputBuffer,第四个参数立即数buffer长度,由于不需要Servicename,五六参数均置为0,flag如果是tcp、udp就是那个什么不说0xB,如果是tcp6、udp6就是0x1b。
Tcp和Udp初始化结构体的函数,可以看出结构体长度16字节,第一个字节的第一个二字节存放0x2,第二个二字节存放端口的大端表示,之后跟的是先前确定存放ip信息的四字节,之后是两个int 0。
TCP6和UDP6的结构体把表项首地址也传进来了,第一个short为23,第二个也是short为端口的网络字节序,之后是表项的头16字节,之后是ip信息的四个字节,一共28个字节。
Ps:在用getNameInfo的时候需要先调用WSAstartup(),不然不能正确返回。
至此分析结束,得差不多就这样
垃圾代码附上,注释区是天真的区域
#include<WS2tcpip.h> #include<iostream> #include<Windows.h> #include<WinBase.h> #include<IPHlpApi.h> #include<Psapi.h> #include<cstdio> using namespace std; typedef int _stdcall A(int**, HANDLE, int); typedef int _stdcall B(int**, HANDLE, int); typedef int _stdcall C(int**, HANDLE, int); typedef int _stdcall D(int**, HANDLE, int); typedef struct sockaddrM { unsigned short v1, v2; unsigned int v3, v4, v5; }SOCKADD; // // //void ShowIpAndPort(unsigned int ip, unsigned int port, unsigned int tuFlag, unsigned int p6Flag) { // char myIp[0x104]; // SOCKADD* soc; // soc->v1 = soc->v2 = soc->v3 = soc->v4 = soc->v5 = 0; // if (p6Flag) { // soc->v1 = 0x17; // } // else { // soc->v1 = 0x2; // } // soc->v2 = htons(port); // soc->v3 = ip; // getnameinfo((const SOCKADDR *)soc, 0x10, myIp, 0x104, 0, 0, tuFlag); // cout << myIp << ":" << port << " "; //} // //void ShowInformation(int ipN, int portN, int ipW, int portW, int stat, int pid, int type) { // switch (type) // { // case 1: // ShowIpAndPort(ipN, portN, 0xB, 0); // break; // case 2: // ShowIpAndPort(portN, 0xB, 1); // break; // case 3: // ShowIpAndPort(portN, 0x1B, 0); // break; // case 4: // ShowIpAndPort(portN, 0x1B, 1); // break; // default: // break; // } // //byte* ipn = (byte*)&ipN; // //cout << (u_short)*ipn << "." << (u_short)*(ipn + 1) << "." << (u_short)*(ipn + 2) << "." << (u_short)*(ipn + 3) << ":"; // //byte* ipw = (byte*)&ipW; // //cout << (u_short)*ipw << "." << (u_short)*(ipw + 1) << "." << (u_short)*(ipw + 2) << "." << (u_short)*(ipw + 3) << ":"; // if (ipW) { // if (ipW == 1) { // ShowIpAndPort(portW, 0xB, 0); // } // else { // ShowIpAndPort(portW, 0xB, 1); // } // } // ShowStat(stat); // cout << pid << endl; // ShowName(pid); //} typedef struct _tcpSock { u_short v1, v2; u_int v3, v4, v5; }TcpSock; typedef struct _tcp6Sock { u_short v1, v2; u_int v3, v4, v5, v6, v7, v8; }Tcp6Sock; void ShowStat(int stat) { switch (stat) { case 1: cout << "CLOSED "; break; case 2: cout << "LISTENING "; break; case 3: cout << "SYN_SENT "; break; case 4: cout << "SYN_RECEIVED "; break; case 5: cout << "ESTABLISHED "; break; case 6: cout << "FIN_WAIT_1 "; break; case 7: cout << "FIN_WAIT_2 "; break; case 8: cout << "CLOSE_WAIT "; break; case 9: cout << "CLOSING "; break; case 10: cout << "LAST_ACK "; break; case 11: cout << "TIME_WAIT "; break; } } void ShowName(int pid) { char Name[40] = { '0' }; HANDLE pH = OpenProcess(0x410u, 0, pid); if (pH == NULL) { cout << "无法获取!" << endl; return; } GetModuleBaseNameA(pH, 0, (LPSTR)Name, 256); cout << Name << endl; } void ShowTcpIpv4AndPort(u_int pid, u_short port) { char oIp[0x104]; TcpSock* so = new TcpSock; memset(so, 0, 0x10); so->v1 = 0x2; so->v2 = port; so->v3 = pid; getnameinfo((const SOCKADDR*)so, 0x10, oIp, 0x104, 0, 0, 0xb); u_short oPort = ntohs(port); cout << oIp << ":" << oPort << " "; } void ShowTcpIpv6AndPort(int *T, u_int pid, u_short port) { char oIp[0x104]; Tcp6Sock* so = new Tcp6Sock; memset(so, 0, 0x1c); so->v1 = 0x17; so->v2 = port; so->v4 = T[0]; so->v5 = T[1]; so->v6 = T[2]; so->v7 = T[3]; so->v8 = pid; getnameinfo((const SOCKADDR*)so, 0x1c, oIp, 0x104, 0, 0, 0xb); u_short oPort = ntohs(port); cout << oIp << ":" << oPort << " "; } void ShowUdpIpv4AndPort(u_int pid, u_short port) { char oIp[0x104]; TcpSock* so = new TcpSock; memset(so, 0, 0x10); so->v1 = 0x2; so->v2 = port; so->v3 = pid; getnameinfo((const SOCKADDR*)so, 0x10, oIp, 0x104, 0, 0, 0x1b); u_short oPort = ntohs(port); cout << oIp << ":" << oPort << " "; } void ShowUdpIpv6AndPort(int *T, u_int pid, u_short port) { char oIp[0x104]; Tcp6Sock* so = new Tcp6Sock; memset(so, 0, 0x1c); so->v1 = 0x17; so->v2 = port; so->v4 = T[0]; so->v5 = T[1]; so->v6 = T[2]; so->v7 = T[3]; so->v8 = pid; getnameinfo((const SOCKADDR*)so, 0x1c, oIp, 0x104, 0, 0, 0x1b); u_short oPort = ntohs(port); cout << oIp << ":" << oPort << " "; } void ShowTcpInformation(int *Table) { ShowTcpIpv4AndPort(Table[1], Table[2]); ShowTcpIpv4AndPort(Table[3], Table[4]); ShowStat(Table[0]); cout << Table[5] <<endl;//pid ShowName(Table[5]); } void ShowTcp6Information(int *Table) { ShowTcpIpv6AndPort(Table, Table[6], Table[5]); ShowTcpIpv6AndPort(Table, Table[10], Table[11]); ShowStat(Table[12]); cout << Table[13] << endl;//pid ShowName(Table[13]); } void ShowUdpInformation(int *Table) { ShowUdpIpv4AndPort(Table[0], Table[1]); cout << "*:*" << " "; cout << Table[2] << endl;//pid ShowName(Table[2]); } void ShowUdp6Information(int *Table) { ShowUdpIpv6AndPort(Table, Table[4], Table[5]); cout << "*:*" << " "; cout << Table[6] << endl;//pid ShowName(Table[6]); } int main() { cout << "本地地址 外部地址 状态 PID" << endl; WSADATA wsaData = { 0 }; int isResult = WSAStartup(MAKEWORD(2, 2), &wsaData); if (isResult != 0) { printf("WSASTRATUP FAILED!"); return 1; } int *lp = NULL; LPCWSTR libraryName = L"IPHLPAPI.DLL"; HMODULE libraryM = LoadLibraryW(libraryName); A* GetTcpTable = (A*)GetProcAddress(libraryM, "InternalGetTcpTableWithOwnerModule"); B* GetTcp6Table = (B*)GetProcAddress(libraryM, "InternalGetTcp6TableWithOwnerModule"); C* GetUdpTable = (C*)GetProcAddress(libraryM, "InternalGetUdpTableWithOwnerModule"); D* GetUdp6Table = (D*)GetProcAddress(libraryM, "InternalGetUdp6TableWithOwnerModule"); HANDLE hHeap = GetProcessHeap(); int index = 0; GetTcpTable(&lp, hHeap, 0); int TcpCnt = *lp; int *Tcp; cout << "[Tcp information.]" << endl; while(TcpCnt--) { Tcp = lp + 2 + index; ShowTcpInformation(Tcp); index += 40; } index = 0; GetTcp6Table(&lp, hHeap, 0); int Tcp6Cnt = *lp; int *Tcp6; cout << "[Tcp6 information.]" << endl; while (Tcp6Cnt--) { Tcp6 = lp + 2 + index; ShowTcp6Information(Tcp6); index += 48; } index = 0; GetUdpTable(&lp, hHeap, 0); int UdpCnt = *lp; int *Udp; cout << "[Udp information.]" << endl; while (UdpCnt--) { Udp = lp + 2 + index; ShowUdpInformation(Udp); index += 40; } index = 0; GetUdp6Table(&lp, hHeap, 0); int Udp6Cnt = *lp; int *Udp6; cout << "[Udp6 information.]" << endl; while (Udp6Cnt--) { Udp6 = index + 2 + lp; ShowUdp6Information(Udp6); index += 44; } return 0; }