进程与端口映射
文章目录:
1. Demo 效果展示:
2. 进程和端口乱扯淡:
3. 查询进程和端口的 API 的介绍:
4. 根据进程 ID 获得该进程所打开的 TCP 和 UDP 端口:
5. 根据端口号来获得打开该端口号的进程:
6. 小结:
1. Demo 效果展示:
下面的这个 Demo 我只是用最简单的方式做了个 CUI 界面来演示一下而已,
支持 Windows XP,Windows Server 2003,Vista,Windows 7 ~
并且使用 C++ 来完成的,所以对于博客园里面这么多的 .Net 开发者来说不太友好,
但是有兴趣的可以将 Demo 改写成 DLL,然后 .Net 程序也就可以进行平台调用了,
里面所有的封装都很工整,所有改写 DLL 以及平台调用的声明也会很简单,有过平台调用经验的都可以改写的。
本文的后面我将会把这个 Demo 提供链接下载;
本篇博文 Demo 效果演示:
(关于端口的查询操作,你可以使用 netstat 命令行进行查询,
也可以使用 TCPView 这个工具来查询,当然还可以使用 360 的流量防火墙里面的网络连接来进行查询)
查询指定进程所打开的所有的 TCP 端口:
查询指定进程所打开的所有的 UDP 端口:
查询指定 TCP 端口所属的进程 ID:
查询指定 UDP 端口所属的进程 ID:
2. 进程和端口乱扯淡:
很久没写博文了,以前跟自己说的,每个月要写那么一两篇还看得下去的博文,
可是去年春节家里出了些事情给耽搁了 1 个多月,而后人又懒了 1 个多月,所以算下来有 2 个多月没有写博文了。
而后今年过来帮一小哥做了个小东西,主要是用来实现伪造端口、伪造远程地址以及进程内存的保护啊之类的动作。
而后呢就是上面提到的这个小东西中中涉及到了端口的知识,
一开始嘛,我也是放狗一搜索,然后发现关于端口这一块的东西,
在 2000、XP、Server 2003 下的资料很多,但惟独到了 Vista 和 7 上面就没什么资料了,
似乎一下子端口就完了似的,就好比端口的隐藏吧,在 XP 上无数端口隐藏工具,
应用层的,内核层的都有,但是在 Vista 和 7 上呢就都死绝了(主要是指内核层),
没办法,只能够自己去研究了,研究的成果呢,主要分为内核层和应用层的成果,
当然咯,今天我的这篇博文并不会去介绍内核里面关于端口的什么东西,
而是介绍如何在应用层里面完成端口和进程之间的映射。
所谓的进程与端口映射在我来理解,
简单点就是假如我知道一个进程,那我就要知道这个进程所打开的所有的端口,
又假如我知道一个端口,那我就应该知道是哪一个进程打开了这个端口。
所以要完成端口和进程的映射其实归根结底是一个根据进程查询端口和根据端口查询进程的问题。
然后我稍微解释一下为何到了 Vista 和 7 以后,狗上面的关于端口的资源就锐减了,
其实原因是这样的,在 Vista 之前,我这里摘抄一点在别人博客上的东西出来:
Windows Vista 的网络模块较从前发生了很大变化,从而导致很多旧的端口隐藏工具无法使用.
事实上在 Vista 下 netstat.exe 调用 Iphlpapi.dll 导出的 InternalGetTcpTable2 函数实现对所有打开端口的列举.
InternalGetTcpTable2 随后调用由 nsi.dll 导出的 NsiAllocateAndGetTable 函数,
nsi.dll 则调用 NsiEnumerateObjectsAllParametersEx 向 nsiproxy.sys 发送 Irp 最终转入内核.
而nsiproxy.sys 又只是对 netio.sy s的简单封装,它最终调用了 netio.sys 导出的内核服务例程.
上面的这些文字多说无益,大体的意思就是从 Vista 开始,Windows 的网络模块发生了大的变化,
不过话说回来,上面的这种变化在应用层体会的不多,不过如果做到内核的话,
你就能够体会到网络层确实变化很大 ~
安全焦点上的一篇关于在 Vista 之前的系统上的进程和端口的很古老的文章
《再谈进程与端口的映射》:http://www.xfocus.net/articles/200202/344.html
3. 查询进程和端口的 API 介绍:
好,上面说的有点离题了,这篇博文的目的在应用层而不在内核层,
所以我们无须去体会 Windows 网络层的这种变化。
一般来说,要完成进程和端口的映射,我们使用的是由 IPHlpapi.dll 中导出的 API 来完成的,
在 Vista 之前的操作系统中,要查询进程和端口的信息,容易很多,
你可以通过调用 IPHlpapi.dll 中导出的下面两个 API 来完成查询 TCP 和 UDP 端口的信息,
下面的两个 API 分别用来查询 TCP 和 UDP 端口信息,
(下面的两个 API 仅适用于 XP 和 Server 2003,在 Vista 以及 7 中,这两个 API 被移除)
1: DWORD WINAPI AllocateAndGetTcpExTableFromStack(
2: __out PVOID* ppTcpTable,
3: __in BOOL bOrder,
4: __in HANDLE hHeap,
5: __in DWORD dwFlags,
6: __in DWORD dwFamily
7: );
8:
9: DWORD WINAPI AllocateAndGetUdpExTableFromStack(
10: __out PVOID* ppUDPTable,
11: __in BOOL bOrder,
12: __in HANDLE hHeap,
13: __in DWORD dwFlags,
14: __in DWORD dwFamily
15: );
16:
参数说明:
第一个参数:很显然,为输出参数,带回我们查询到的 TCP 或者 UDP 信息;
第二个参数:也很显然,判断输出结果是否排序;
第三个参数:指定在哪个 Heap 上分配内存;
第四个参数:指定用来控制 Heap 的标识;
第四个参数:指定 IP_V4 还是 IP_V6,具体看下面的截图:
不过要完成进程和端口的映射在 Vista 和 7 下确实也没那么容易,至于原因呢,
就是 IPHlpapi.dll 中关于查询进程和端口信息的 API 没有公开,即为 UnDocument API,
不过我们还是可以通过 UnDocument API 来完成这个查询的工作,具体涉及到下面的两个 API,
当然,下面的这两个 API 由于是 UnDocumnet API,自然你查什么 MSDN 啊之类都找不到这个 API 的声明的,
下面的两个 API 分别用来查询 TCP 和 UDP 端口信息,
1: //=====================================================================================//
2: //Name: DWORD InternalGetTcpTable2() //
3: // //
4: //Descripion: 该函数在 Windows Vista 以及 Windows 7 下面效 //
5: // //
6: //=====================================================================================//
7: typedef DWORD (WINAPI *PFNInternalGetTcpTable2)(
8: PMIB_TCPEXTABLE_VISTA *pTcpTable_Vista,
9: HANDLE heap,
10: DWORD flags
11: );
12:
13: //=====================================================================================//
14: //Name: DWORD InternalGetUdpTableWithOwnerPid() //
15: // //
16: //Descripion: 该函数在 Windows Vista 以及 Windows 7 下面效 //
17: // //
18: //=====================================================================================//
19: typedef DWORD (WINAPI *PFNInternalGetUdpTableWithOwnerPid)(
20: PMIB_UDPEXTABLE *pUdpTable,
21: HANDLE heap,
22: DWORD flags
23: );
参数说明:
第一个参数:pTcpTable_Vista 或者 pUdpTable 则不需要多说,其为输出参数,带回查询到的值;
第二个参数:heap 也不需要多说,其指示要从哪个 Heap 上面分配内存;
第三个参数:flags 则未知,一般都设置为 1,不过貌似设置为 2 啊之类的好像又多影响不大;
4. 根据进程 ID 获得该进程所打开的所有的 TCP 和 UDP 端口:
废话不多少的,直接上代码来得更加容易,注意下面函数中的 HeapFree 函数的调用,
因为从函数里面分配了内存,并且函数返回指针带出了这片内存,所以必须在外部释放,否则会造成内存泄露。
1: //=======================================================================================//
2: //Name: DWORD GetAllPortByProcessId() //
3: // //
4: //Descripion: 根据进程 ID 来求出该进程所打开的所有的端口号,并且在 dwAllPort 数组中返回所有端口号 //
5: // 其中 dwMaxLen 为数组的长度,函数的返回值为进程所打开的端口的数目 //
6: // (支持 XP,Server 2003,Vista,Win7) //
7: // //
8: //=======================================================================================//
9: DWORD GetAllPortByProcessId(TcpOrUdp type, DWORD dwProcessId, DWORD * dwAllPort, DWORD dwMaxLen)
10: {
11: DWORD dwPortCount = 0;
12: HMODULE hModule = LoadLibraryW(L"iphlpapi.dll");
13: if (hModule == NULL)
14: {
15: return dwPortCount;
16: }
17:
18: if(type == TcpType)
19: {
20: // 表明查询的是 UDP 信息
21: PFNAllocateAndGetTcpExTableFromStack pAllocateAndGetTcpExTableFromStack;
22: pAllocateAndGetTcpExTableFromStack =
23: (PFNAllocateAndGetTcpExTableFromStack)GetProcAddress(hModule, "AllocateAndGetTcpExTableFromStack");
24: if (pAllocateAndGetTcpExTableFromStack != NULL)
25: {
26: // 表明为 XP 或者 Server 2003 操作系统
27: PMIB_TCPEXTABLE pTcpExTable = NULL;
28: if (pAllocateAndGetTcpExTableFromStack(&pTcpExTable, TRUE, GetProcessHeap(), 0, AF_INET) != 0)
29: {
30: if (pTcpExTable)
31: {
32: HeapFree(GetProcessHeap(), 0, pTcpExTable);
33: }
34:
35: FreeLibrary(hModule);
36: hModule = NULL;
37:
38: return dwPortCount;
39: }
40:
41: for (UINT i = 0; i < pTcpExTable->dwNumEntries; i++)
42: {
43: // 过滤掉数据,只获取我们要查询的进程的 Port 信息
44: if(dwProcessId == pTcpExTable->table[i].dwProcessId)
45: {
46: if(dwPortCount < dwMaxLen)
47: {
48: dwAllPort[dwPortCount] = ntohs(0x0000FFFF & pTcpExTable->table[i].dwLocalPort);
49: dwPortCount++;
50: }
51: }
52: }
53:
54: if (pTcpExTable)
55: {
56: HeapFree(GetProcessHeap(), 0, pTcpExTable);
57: }
58:
59: FreeLibrary(hModule);
60: hModule = NULL;
61:
62: return dwPortCount;
63: }
64: else
65: {
66: // 表明为 Vista 或者 7 操作系统
67: PMIB_TCPEXTABLE_VISTA pTcpExTable = NULL;
68: PFNInternalGetTcpTable2 pInternalGetTcpTable2 =
69: (PFNInternalGetTcpTable2)GetProcAddress(hModule, "InternalGetTcpTable2");
70: if (pInternalGetTcpTable2 == NULL)
71: {
72: if (pTcpExTable)
73: {
74: HeapFree(GetProcessHeap(), 0, pTcpExTable);
75: }
76:
77: FreeLibrary(hModule);
78: hModule = NULL;
79:
80: return dwPortCount;
81: }
82:
83: if (pInternalGetTcpTable2(&pTcpExTable, GetProcessHeap(), 1))
84: {
85: if (pTcpExTable)
86: {
87: HeapFree(GetProcessHeap(), 0, pTcpExTable);
88: }
89:
90: FreeLibrary(hModule);
91: hModule = NULL;
92:
93: return dwPortCount;
94: }
95:
96: for (UINT i = 0;i < pTcpExTable->dwNumEntries; i++)
97: {
98: // 过滤掉数据,只获取我们要查询的进程的 TCP Port 信息
99: if(dwProcessId == pTcpExTable->table[i].dwProcessId)
100: {
101: if(dwPortCount < dwMaxLen)
102: {
103: dwAllPort[dwPortCount] = ntohs(0x0000FFFF & pTcpExTable->table[i].dwLocalPort);
104: dwPortCount++;
105: }
106: }
107: }
108:
109: if (pTcpExTable)
110: {
111: HeapFree(GetProcessHeap(), 0, pTcpExTable);
112: }
113:
114: FreeLibrary(hModule);
115: hModule = NULL;
116:
117: return dwPortCount;
118: }
119: }
120: else if(type == UdpType)
121: {
122: // 表明查询的是 UDP 信息
123: PMIB_UDPEXTABLE pUdpExTable = NULL;
124: PFNAllocateAndGetUdpExTableFromStack pAllocateAndGetUdpExTableFromStack;
125: pAllocateAndGetUdpExTableFromStack =
126: (PFNAllocateAndGetUdpExTableFromStack)GetProcAddress(hModule,"AllocateAndGetUdpExTableFromStack");
127: if (pAllocateAndGetUdpExTableFromStack != NULL)
128: {
129: // 表明为 XP 或者 Server 2003 操作系统
130: if (pAllocateAndGetUdpExTableFromStack(&pUdpExTable, TRUE, GetProcessHeap(), 0, AF_INET) != 0)
131: {
132: if (pUdpExTable)
133: {
134: HeapFree(GetProcessHeap(), 0, pUdpExTable);
135: }
136:
137: FreeLibrary(hModule);
138: hModule = NULL;
139:
140: return dwPortCount;
141: }
142:
143: for (UINT i = 0; i < pUdpExTable->dwNumEntries; i++)
144: {
145: // 过滤掉数据,只获取我们要查询的进程的 UDP Port信息
146: if(dwProcessId == pUdpExTable->table[i].dwProcessId)
147: {
148: if(dwPortCount < dwMaxLen)
149: {
150: dwAllPort[dwPortCount] = ntohs(0x0000FFFF & pUdpExTable->table[i].dwLocalPort);
151: dwPortCount++;
152: }
153: }
154: }
155:
156: if (pUdpExTable)
157: {
158: HeapFree(GetProcessHeap(), 0, pUdpExTable);
159: }
160:
161: FreeLibrary(hModule);
162: hModule = NULL;
163:
164: return dwPortCount;
165: }
166: else
167: {
168: // 表明为 Vista 或者 7 操作系统
169: PFNInternalGetUdpTableWithOwnerPid pInternalGetUdpTableWithOwnerPid;
170: pInternalGetUdpTableWithOwnerPid =
171: (PFNInternalGetUdpTableWithOwnerPid)GetProcAddress(hModule, "InternalGetUdpTableWithOwnerPid");
172: if (pInternalGetUdpTableWithOwnerPid != NULL)
173: {
174: if (pInternalGetUdpTableWithOwnerPid(&pUdpExTable, GetProcessHeap(), 1))
175: {
176: if (pUdpExTable)
177: {
178: HeapFree(GetProcessHeap(), 0, pUdpExTable);
179: }
180:
181: FreeLibrary(hModule);
182: hModule = NULL;
183:
184: return dwPortCount;
185: }
186:
187: for (UINT i = 0; i < pUdpExTable->dwNumEntries; i++)
188: {
189: // 过滤掉数据,只获取我们要查询的进程的 UDP Port信息
190: if(dwProcessId == pUdpExTable->table[i].dwProcessId)
191: {
192: if(dwPortCount < dwMaxLen)
193: {
194: dwAllPort[dwPortCount] = ntohs(0x0000FFFF & pUdpExTable->table[i].dwLocalPort);
195: dwPortCount++;
196: }
197: }
198: }
199: }
200:
201: if (pUdpExTable)
202: {
203: HeapFree(GetProcessHeap(), 0, pUdpExTable);
204: }
205:
206: FreeLibrary(hModule);
207: hModule = NULL;
208:
209: return dwPortCount;
210: }
211: }
212: else
213: {
214: FreeLibrary(hModule);
215: hModule = NULL;
216:
217: return dwPortCount;
218: }
219: }
5. 根据端口号来获得打开该端口的进程:
同样是直接上代码,同样需要注意下面函数中的 HeapFree 函数的调用,
因为从函数里面分配了内存,并且函数返回指针带出了这片内存,所以必须在外部释放,否则会造成内存泄露。
1: //=====================================================================================//
2: //Name: DWORD GetProcessIdByPort() //
3: // //
4: //Descripion: 根据端口号求出打开该端口号的进程 ID(支持 XP,Server 2003,Vista,Win7) //
5: // //
6: //=====================================================================================//
7: DWORD GetProcessIdByPort(TcpOrUdp type, DWORD dwPort)
8: {
9: HMODULE hModule = LoadLibraryW(L"iphlpapi.dll");
10: if (hModule == NULL)
11: {
12: return 0;
13: }
14:
15: if(type == TcpType)
16: {
17: // 表明查询的是 TCP 信息
18: PFNAllocateAndGetTcpExTableFromStack pAllocateAndGetTcpExTableFromStack;
19: pAllocateAndGetTcpExTableFromStack =
20: (PFNAllocateAndGetTcpExTableFromStack)GetProcAddress(hModule, "AllocateAndGetTcpExTableFromStack");
21: if (pAllocateAndGetTcpExTableFromStack != NULL)
22: {
23: // 表明为 XP 或者 Server 2003 操作系统
24: PMIB_TCPEXTABLE pTcpExTable = NULL;
25: if (pAllocateAndGetTcpExTableFromStack(&pTcpExTable, TRUE, GetProcessHeap(), 0, AF_INET) != 0)
26: {
27: if (pTcpExTable)
28: {
29: HeapFree(GetProcessHeap(), 0, pTcpExTable);
30: }
31:
32: FreeLibrary(hModule);
33: hModule = NULL;
34:
35: return 0;
36: }
37:
38: for (UINT i = 0; i < pTcpExTable->dwNumEntries; i++)
39: {
40: // 过滤掉数据,只查询我们需要的进程数据
41: if(dwPort == ntohs(0x0000FFFF & pTcpExTable->table[i].dwLocalPort))
42: {
43: DWORD dwProcessId = pTcpExTable->table[i].dwProcessId;
44: if (pTcpExTable)
45: {
46: HeapFree(GetProcessHeap(), 0, pTcpExTable);
47: }
48:
49: FreeLibrary(hModule);
50: hModule = NULL;
51:
52: return dwProcessId;
53: }
54: }
55:
56: if (pTcpExTable)
57: {
58: HeapFree(GetProcessHeap(), 0, pTcpExTable);
59: }
60:
61: FreeLibrary(hModule);
62: hModule = NULL;
63:
64: return 0;
65: }
66: else
67: {
68: // 表明为 Vista 或者 7 操作系统
69: PMIB_TCPEXTABLE_VISTA pTcpExTable = NULL;
70: PFNInternalGetTcpTable2 pInternalGetTcpTable2 =
71: (PFNInternalGetTcpTable2)GetProcAddress(hModule, "InternalGetTcpTable2");
72: if (pInternalGetTcpTable2 == NULL)
73: {
74: if (pTcpExTable)
75: {
76: HeapFree(GetProcessHeap(), 0, pTcpExTable);
77: }
78:
79: FreeLibrary(hModule);
80: hModule = NULL;
81:
82: return 0;
83: }
84:
85: if (pInternalGetTcpTable2(&pTcpExTable, GetProcessHeap(), 1))
86: {
87: if (pTcpExTable)
88: {
89: HeapFree(GetProcessHeap(), 0, pTcpExTable);
90: }
91:
92: FreeLibrary(hModule);
93: hModule = NULL;
94:
95: return 0;
96: }
97:
98: for (UINT i = 0;i < pTcpExTable->dwNumEntries; i++)
99: {
100: // 过滤掉数据,只查询我们需要的进程数据
101: if(dwPort == ntohs(0x0000FFFF & pTcpExTable->table[i].dwLocalPort))
102: {
103: DWORD dwProcessId = pTcpExTable->table[i].dwProcessId;
104: if (pTcpExTable)
105: {
106: HeapFree(GetProcessHeap(), 0, pTcpExTable);
107: }
108:
109: FreeLibrary(hModule);
110: hModule = NULL;
111:
112: return dwProcessId;
113: }
114: }
115:
116: if (pTcpExTable)
117: {
118: HeapFree(GetProcessHeap(), 0, pTcpExTable);
119: }
120:
121: FreeLibrary(hModule);
122: hModule = NULL;
123:
124: return 0;
125: }
126: }
127: else if(type == UdpType)
128: {
129: // 表明查询的是 UDP 信息
130: PMIB_UDPEXTABLE pUdpExTable = NULL;
131: PFNAllocateAndGetUdpExTableFromStack pAllocateAndGetUdpExTableFromStack;
132: pAllocateAndGetUdpExTableFromStack =
133: (PFNAllocateAndGetUdpExTableFromStack)GetProcAddress(hModule,"AllocateAndGetUdpExTableFromStack");
134: if (pAllocateAndGetUdpExTableFromStack != NULL)
135: {
136: // 表明为 XP 或者 Server 2003 操作系统
137: if (pAllocateAndGetUdpExTableFromStack(&pUdpExTable, TRUE, GetProcessHeap(), 0, AF_INET) != 0)
138: {
139: if (pUdpExTable)
140: {
141: HeapFree(GetProcessHeap(), 0, pUdpExTable);
142: }
143:
144: FreeLibrary(hModule);
145: hModule = NULL;
146:
147: return 0;
148: }
149:
150: for (UINT i = 0; i < pUdpExTable->dwNumEntries; i++)
151: {
152: // 过滤掉数据,只查询我们需要的进程数据
153: if (dwPort == ntohs(0x0000FFFF & pUdpExTable->table[i].dwLocalPort))
154: {
155: DWORD dwProcessId = pUdpExTable->table[i].dwProcessId;
156: if (pUdpExTable)
157: {
158: HeapFree(GetProcessHeap(), 0, pUdpExTable);
159: }
160:
161: FreeLibrary(hModule);
162: hModule = NULL;
163:
164: return dwProcessId;
165: }
166: }
167:
168: if (pUdpExTable)
169: {
170: HeapFree(GetProcessHeap(), 0, pUdpExTable);
171: }
172:
173: FreeLibrary(hModule);
174: hModule = NULL;
175:
176: return 0;
177: }
178: else
179: {
180: // 表明为 Vista 或者 7 操作系统
181: PFNInternalGetUdpTableWithOwnerPid pInternalGetUdpTableWithOwnerPid;
182: pInternalGetUdpTableWithOwnerPid =
183: (PFNInternalGetUdpTableWithOwnerPid)GetProcAddress(hModule, "InternalGetUdpTableWithOwnerPid");
184: if (pInternalGetUdpTableWithOwnerPid != NULL)
185: {
186: if (pInternalGetUdpTableWithOwnerPid(&pUdpExTable, GetProcessHeap(), 1))
187: {
188: if (pUdpExTable)
189: {
190: HeapFree(GetProcessHeap(), 0, pUdpExTable);
191: }
192:
193: FreeLibrary(hModule);
194: hModule = NULL;
195:
196: return 0;
197: }
198:
199: for (UINT i = 0; i < pUdpExTable->dwNumEntries; i++)
200: {
201: // 过滤掉数据,只查询我们需要的进程数据
202: if (dwPort == ntohs(0x0000FFFF & pUdpExTable->table[i].dwLocalPort))
203: {
204: DWORD dwProcessId = pUdpExTable->table[i].dwProcessId;
205: if (pUdpExTable)
206: {
207: HeapFree(GetProcessHeap(), 0, pUdpExTable);
208: }
209:
210: FreeLibrary(hModule);
211: hModule = NULL;
212:
213: return dwProcessId;
214: }
215: }
216: }
217:
218: if (pUdpExTable)
219: {
220: HeapFree(GetProcessHeap(), 0, pUdpExTable);
221: }
222:
223: FreeLibrary(hModule);
224: hModule = NULL;
225:
226: return 0;
227: }
228: }
229: else
230: {
231: FreeLibrary(hModule);
232: hModule = NULL;
233:
234: return -1;
235: }
236: }
6. 小结:
好了,文章就写到这里了,对于那些对端口有兴趣的童鞋,我想这篇文章还是很有帮助的,
因为在网络上关于这一块的资料真的不多的,不过到最后了还是要提一下,
就是上面的文章介绍的是在应用层,也就是 Ring3 下的端口的查询以及使用,
我做过在 Ring0 下的端口隐藏,是通过 Hook NtDeviceIoControlFile 来实现的,
在 Vista 之前的操作系统中都容易搞定,但是在 Vista 后的操作系统中,
由于网络模块和之前完全不同了,而且公开的资料又太少,
所以在 Vista 后的操作系统中的端口隐藏以及端口伪造实现效果很不好,
不知有同仁是否研究过这一块的东西,有的话,我们可以讨论讨论的 ~
Demo 下载地址: http://files.cnblogs.com/BoyXiao/TestPort.zip