Fport是一个可以在winXP上实现查询端口信息功能的软件。
首先将文件拖入IDA分析
找到main函数,通过与OD动态分析结合,发现输出信息都在一个keyFunction里完成。
通过OD发现程序的主体逻辑大概是:Tcp和Udp操作类似,首先GetProcessHeap()获取当前堆,然后调用AllocateAndGetTcpExTableFromStack(),再将其返回信息送入workFunc()加工,最后由msgShow()输出到屏幕上。通过MSDN上查询可以知道:
GetProcessHeap 返回当前进程堆句柄。
【垃圾博客园写了一大堆没有保存按到鼠标全部没有了!!!!心态爆炸】
API的功能是获取Tcp信息,第一个参数是一个指针,存放指向返回的tcpTable的指针,第三个参数是getProcessHeap返回的handle,其他都是立即数。经过调试发现,输出的pid和port信息全在tcpTable里面,通过输出和对workFunc的传参发现该表中结构体占4X6个字节,最后4个字节存放pid,9、10字节存放大端方式的port值。所以大胆推测workFunc只是对信息作一定的处理,最后方便msgShow统一地进行输出。进入workFunc验证
看到根据pid获取程序名称,地址(zaigetFileAddress中)的功能也在此实现,stringFunc1应该就是对输出信息的处理。至此想要写出同样功能的程序我们只需要调用一次AllocateAndGetTcpExTableFromStack()即可,但是老师的意思似乎是要让我们分析这个API,于是要进到函数中去。(AllocateAndGetTcpExTableFromStack()是iphlpdll.dll里的)
进入AllocateAndGetTcpExTableFromStack()
ps:最开始的时候不知道IDA静态分析dll,用OD动态分析吃了不少苦头不过也增强了看汇编代码的能力。附上几张草稿纸
由于最终目的是要写出同样功能的程序,所以只需要获得所有API的调用情况以及具体参数就可以了。
函数的关系情况,initUnicodeString为createFile提供文件名,createEvent()为zwDeviceIoControlFile()提供handle,第一次调用的返回值为allocateHeap提供大小,第二次调用zwDeviceIoControlFile()返回了tcpTable于是zwDeviceIoControlFile()就是关键API,搞定了它的参数就可以获得pid和port。
看上去这是一个和硬件打交道的API,从第二个参数开始讲起,Event就是CreateEvent返回的handle,第三四个是立即数0,第五个是指向PIO_STATUS_BLOCK的指针,这儿没有用,第六个看名字就知道比较关键在此是0x120003,后两个也比较关键Inputbuffer,告诉Api你想要做什么,都是立即数轻易获取,每次调用都不一样,长度是固定的值0x24,outputBuffer也很关键,返回的东西就存在里面,第一次调用长度固定0x3c,allocate长度位于返回buffer最后一个字节,由一系列奇怪的计算获得。第二次传入的参数也就是allocateHeap的长度。
第一个参数Filehandle,由CreateFile返回,看到函数调用关系,每次调用DeviceIoControlFile之前都有CreateFile操作,理所应当的觉得这个参数由之前的createFile决定,但是动态分析的时候发现并不是这样,CreateFIle并没有返回句柄,而且zwDeviceIoControlFile用的是一个全局变量0x3c,这就很奇怪了,那要CreateFIle函数有什么用,全程都没用到。尝试nop掉其中所有对CreateFile的调用,程序居然能够正常出结果!猜测:第一个参数是一个固定的数值0x3c。
于是准备开始写自己的第一版程序,首先是工具,直接用WIN10上面VS2015写出来的程序被报错:不是有效的win32应用程序,于是在设置IDE的时候遇到了一连串的问题,不详细讲了只大概列出来:
- API函数未定义
- API头文件找不到
- API符号解析出错,有头文件没有实现,也就是说缺失lib
- 缺失xp工具集
- 找不到dll
- 缺失lib【最后去偷了一个lib居然成了】
- ...
第一版程序大概就实现了调用zwDeviceIoControlFile,但是!重点!但是!我没有实现createFile,因为我觉得它没用啊,没用我为什么要实现,但发现调用了之后outputBuffer啥都没有你说气人不气人,这个程序不能在win10上跑,所以只能用OD调试,最开始以为是PIO_STATUS_BLOCK的错误,因为我偷懒传递了个null,但后来发现并不是这样,我坚决不加createFile上去,因为在loadLibrabry、getProcAddress之后就直接到了allocateAndGetTcpExTableFromStack(),期间也没有机会调用这个函数,我把他nop之后能够执行说明他并没有什么亂用。
后来经过一系列的机缘巧合,我不小心在createFile里面打了一个断点,然后惊人的发现在loadLibrary的时候居然执行了这个函数,而且全局变量0x3c就是在这个时候赋值的(以前看到loadLibrary之后该变量地址就是0x3c坚定的认为他是一个死值),然后手写了一个createFile返回handle供ntDeviceIoControlFile调用就成功返回了。
返回的Tcp表是乱序的,api里用一个qsort进行排序,觉得没必要,想着能得到pid和port就行了还要啥自行车啊,没想到埋下了祸根。
事故发生的时候,我刚把Udp的部分写完,原理和Tcp类似,获取pid的算法不一样。
输出Tcp时候pid和port都是去TcpTable里面找,但是输出Udp的时候pid是在Tcp里找的意思是和Tcp输出时的pid一样,port是在UdpTable里找的。这个时候因为他的是排过序的,我的没有排过序,所以我的程序Udp输出的pid和port对应与他的不一样。
又是机缘巧合之下发现IDE可以用来分析dll,于是赶紧拖进去看看cmp函数怎么写的
int __cdecl CompareTcp6Row(int a1, int a2) { int result; // eax int v3; // eax int v4; // ecx int v5; // esi int v6; // esi int v7; // edi result = memcmp((const char *)a1, (const char *)a2, 16); if ( !result ) { v3 = *(_DWORD *)(a1 + 16); v4 = *(_DWORD *)(a2 + 16); if ( v3 != v4 ) return v3 - v4; v5 = ntohs(*(_WORD *)(a1 + 20)); v6 = v5 - ntohs(*(_WORD *)(a2 + 20)); if ( v6 ) return v6; result = memcmp((const char *)(a1 + 24), (const char *)(a2 + 24), 16); if ( !result ) { v3 = *(_DWORD *)(a1 + 40); v4 = *(_DWORD *)(a2 + 40); if ( v3 != v4 ) return v3 - v4; v7 = ntohs(*(_WORD *)(a1 + 44)); result = v7 - ntohs(*(_WORD *)(a2 + 44)); } } return result; }
int __cdecl CompareUdp6Row(int a1, int a2) { int result; // eax int v3; // eax int v4; // ecx int v5; // edi result = memcmp((const char *)a1, (const char *)a2, 16); if ( !result ) { v3 = *(_DWORD *)(a1 + 16); v4 = *(_DWORD *)(a2 + 16); if ( v3 == v4 ) { v5 = ntohs(*(_WORD *)(a1 + 20)); result = v5 - ntohs(*(_WORD *)(a2 + 20)); } else { result = v3 - v4; } } return result; }
其实这个时候我并没有什么闲心看他,因为我发现这个秘密的时候我也大胆推测得差不多了,知道现在我也没有闲心看他。
大胆推测:tcp表按照port升序,udp表首先前四字节升序,再按port升序,这样有一个疑问
当Udp表规模比Tcp表大的时候,取到的pid值其实已经超出了tcpTable的有效区域,事实也就是这样
那些6553646和0就是垃圾值。一度怀疑fport本身算法有问题。
附一张自己的程序运行结果:
以及源代码:
1 #include<iostream> 2 #include<Windows.h> 3 #include<WinBase.h> 4 #include<ntstatus.h> 5 #include<winternl.h> 6 #include<psapi.h> 7 8 using namespace std; 9 HANDLE pH; 10 int TcpCnt; 11 int UdpCnt; 12 13 typedef struct _TcpTable { 14 int v1, v2, v3, v4, v5, v6; 15 }TcpTable; 16 17 typedef struct _UdpTable { 18 int v1, v2, v3; 19 }UdpTable; 20 21 int DtoX(int value) { 22 return (((value & 0x000000FF) << 24) | ((value & 0x0000FF00) << 8) | 23 ((value & 0x00FF0000) >> 8) | 24 ((value & 0xFF000000) >> 24)) >> 16; 25 } 26 27 int cmpTcp(const void* v1, const void* v2) { 28 int a1 = DtoX((*(TcpTable*)v1).v3); 29 int a2 = DtoX((*(TcpTable*)v2).v3); 30 if (a1 < a2) 31 return -1; 32 else 33 return 1; 34 } 35 36 int cmpUdp(const void* v1, const void* v2) { 37 if ((const int)((*(UdpTable*)v1).v1) < (const int)((*(UdpTable *)v2).v1)) { 38 return 1; 39 } 40 if ((const int)((*(UdpTable*)v1).v1) > (const int)((*(UdpTable *)v2).v1)) { 41 return -1; 42 } 43 int a1 = DtoX((*(UdpTable*)v1).v2); 44 int a2 = DtoX((*(UdpTable*)v2).v2); 45 if (a1 < a2) { 46 return -1; 47 } 48 else { 49 return 1; 50 } 51 } 52 53 void GetTableFromStack(int * inputBuffer, int * outputBuffer, int outputBufferLength) { 54 HANDLE eventhandle = CreateEvent(0, 1, 0, 0); 55 if (eventhandle == NULL) { 56 printf("Create event error"); 57 return; 58 } 59 IO_STATUS_BLOCK iostatus_block; 60 NtDeviceIoControlFile(pH, eventhandle, 0, 0, &iostatus_block, 0x120003, inputBuffer, 0x24, outputBuffer, outputBufferLength); 61 CloseHandle(eventhandle); 62 } 63 64 int * AllocateAndGetUdpTable() { 65 int input1[9] = { 0x401, 0, 0x200, 0x100, 1, 0, 0, 0, 0 }; 66 int input2[9] = { 0x401, 0, 0x200, 0x100, 0x102, 0, 0, 0, 0 }; 67 int output1[5] = { 0 }; 68 GetTableFromStack(input1, output1, 0x14); 69 UdpCnt = output1[4]; 70 int TableSize = ((UdpCnt+ 0xA) * 3 + 3) * 4 - 4; 71 int * ptrToTable = (int *)malloc(TableSize); 72 GetTableFromStack(input2, ptrToTable, TableSize); 73 //qsort(); 74 return ptrToTable; 75 } 76 77 int * AllocateAndGetTcpTable() { 78 int input1[9] = { 0x400, 0, 0x200, 0x100, 1, 0, 0, 0, 0 }; 79 int input2[9] = { 0x400, 0, 0x200, 0x100, 0x102, 0, 0, 0, 0 }; 80 int output1[15] = { 0 }; 81 GetTableFromStack(input1, output1, 0x3c); 82 TcpCnt = output1[14]; 83 int TableSize = (TcpCnt + 0xA) * 24 + 0xc - 4; 84 int * ptrToTable = (int *)malloc(TableSize); 85 GetTableFromStack(input2, ptrToTable, TableSize); 86 //qsort(); 87 return ptrToTable; 88 } 89 90 void OpenTcpDriver() { 91 IO_STATUS_BLOCK ioStB; 92 PCWSTR sourceString = L"\Device\Tcp"; 93 UNICODE_STRING DestinationString; 94 RtlInitUnicodeString(&DestinationString, sourceString); 95 OBJECT_ATTRIBUTES objAtt; 96 objAtt.ObjectName = &DestinationString; 97 objAtt.Length = 24; 98 objAtt.RootDirectory = 0; 99 objAtt.Attributes = 64; 100 objAtt.SecurityDescriptor = 0; 101 objAtt.SecurityQualityOfService = 0; 102 NtCreateFile(&pH, 0x20000000u, &objAtt, &ioStB, 0, 0x80u, 3u, 3u, 0, 0, 0); 103 return; 104 } 105 106 void showInformation(int pid, int port) { 107 char ProcessName[28] = { '0' }; 108 char Path[409] = { '0' }; 109 char PathA[409] = { '0' }; 110 HMODULE hModule = 0; 111 HMODULE hModule2 = 0; 112 LPDWORD cbNeeded = 0; 113 LPDWORD cbNeeded2 = 0; 114 HANDLE processH = OpenProcess(0x410u, 0, pid); 115 if (processH) { 116 if (EnumProcessModules(processH, &hModule, 0x28u, (LPDWORD)&cbNeeded)) { 117 if (GetModuleBaseNameA(processH, hModule, (LPSTR)ProcessName, 0x78u)) { 118 HANDLE processH2 = OpenProcess(0x410u, 0, pid); 119 if (processH2) { 120 if (EnumProcessModules(processH2, &hModule, 4096, (LPDWORD)&cbNeeded2)) { 121 GetModuleFileNameEx(processH2, hModule2, (LPTSTR)Path, 260); 122 } 123 CloseHandle(processH2); 124 } 125 } 126 } 127 else { 128 CloseHandle(processH); 129 } 130 } 131 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, (LPCWSTR)Path, -1, (LPSTR)PathA, 409, NULL, NULL); 132 cout << " pid: " << pid << " port: " << port << " ProcessName: " << ProcessName << " Path: " << PathA << endl; 133 } 134 135 int main() { 136 OpenTcpDriver(); 137 int * pToTcpTable = AllocateAndGetTcpTable(); 138 qsort(pToTcpTable, TcpCnt, 24, cmpTcp); 139 int index = 0; 140 cout << "TCP information:" << endl; 141 while (TcpCnt--) { 142 int pid = pToTcpTable[index + 5]; 143 int port = DtoX(pToTcpTable[index + 2]); 144 showInformation(pid, port); 145 index += 6; 146 } 147 int * pToUdpTable = AllocateAndGetUdpTable(); 148 qsort(pToUdpTable, UdpCnt, 12, cmpUdp); 149 index = 0; 150 int index2 = 0; 151 cout << "Udp information:" << endl; 152 while (UdpCnt--) { 153 int pid = pToTcpTable[index + 5]; 154 int port = DtoX(pToUdpTable[index2 + 1]); 155 showInformation(pid, port); 156 index2 += 3; 157 index += 6; 158 } 159 return 0; 160 }