吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 794|回复: 3
上一主题 下一主题
收起左侧

[系统底层] Windows 网络 API R3 研究

[复制链接]
跳转到指定楼层
楼主
nomomono 发表于 2026-8-14 12:32 回帖奖励
本帖最后由 nomomono 于 2026-8-14 12:41 编辑

前言

在逆向学习的过程中,网络方面也是必不可少的,所以我展开了对 Windows 网络的研究。
既然是 Windows 平台,那么那些软件肯定是需要调用 Windows API 来完成网络请求的,所以我的思路也是很简单:

我想尽可能实现通杀,那么我就在 API 的入口等待你们的请求。

当然,我的这一套方案肯定是可以绕过去的,因为要是想要彻底拦截,还是要在驱动层蹲守。不过那就太费劲了,我们这里只考虑三环的情况,尽可能把它做得更好。
既然已经想好了在 API 等待网络请求,那就开始着手研究一共会经过哪些 DLL。
我找到了三个:    Ws2_32.dll WinHttp.dll    WinINet.dll
俗话说得好:    知己知彼,方能百战百胜。
那就需要先了解一下这三个 DLL 是如何工作的。

DLL 原理解析

观察这三个 DLL 的依赖关系,可以发现它们最终都会依赖到Ws2_32.dll


WinINet.dll
    └── WinHttp.dll
            └── Ws2_32.dll

WinHttp.dll
    └── Ws2_32.dll

Ws2_32.dll

既然最终都会回到 Ws2_32.dll,那么我们的工作量也就减少了很多。

我们再研究一下关键函数究竟是如何运行的,最终定位到 Ws2_32.dll,不就可以完美收场了吗?

一开始我也是这么想的。
WinINet


创建会话句柄
WinINet!InternetOpen
      ↓
打开目标 URL
WinINet!InternetOpenUrl
      ↓
读取服务器返回数据
WinINet!InternetReadFile

WinHTTP


连接服务器
WinHttp!WinHttpOpen
      ↓
创建 HTTP 请求
WinHttp!WinHttpConnect
      ↓
发送请求
WinHttp!WinHttpOpenRequest
      ↓
创建会话句柄
WinHttp!WinHttpSendRequest
      ↓
读取服务器返回数据
WinHttp!WinHttpReadData

Winsock


初始化环境
Ws2_32!WSAStartup
      ↓
查询域名
Ws2_32!getaddrinfo
      ↓
创建 Socket
Ws2_32!socket
      ↓
创建连接
Ws2_32!connect
      ↓
发送请求
Ws2_32!send
      ↓
读取数据
Ws2_32!recv

根据这个简略版的流程,我信心满满地去 Hook Ws2_32.dll里面的这些关键函数。

结果发现……

剩下两个 DLL 并没有按照我想象中的样子断下来。

这就不得不掏出 IDA 进行静态分析了。

经过漫长的分析和查找判断,最终找到了这两个 DLL 所使用的一条关键路径:

Ws2_32!GetAddrInfoExW
        ↓
Ws2_32!WSAIoctl
        ↓
Mswsock!MSAFD_ConnectEx
        ↓
Ws2_32!WSASend
        ↓
Ws2_32!WSARecv

这里你会惊奇地发现,混进来了一个陌生的 DLL:Mswsock.dll
这也是卡住我最久的一个地方。

谁能想到 WSAIoctl这里并不是简单地走 WSAConnect,而是通过 Winsock 扩展函数机制获取到了 ConnectEx 的函数指针,最终进入 Mswsock 中的实现。
估计微软也是为了兼容不同的 Winsock 扩展机制,才搞出了这么一套东西。
对于这种函数指针动态解析的过程,一直都是很有意思的过程,跟猫抓老鼠一样有趣。
下面就跟你们分享一下这里的过程。
首先来看一下 WSAIoctl。
这里使用的是:

SIO_GET_EXTENSION_FUNCTION_POINTER

配合:

WSAID_CONNECTEX

来获取 ConnectEx 对应的函数地址。
简单来说,就是:

WSAIoctl
   │
   ├── SIO_GET_EXTENSION_FUNCTION_POINTER
   │
   ├── WSAID_CONNECTEX
   │
   └── 输出 ConnectEx 函数地址

微软通过 GUID 查表的方式去找到 WSAID_CONNECTEX 对应的扩展函数地址。
这非常的微软。
我缓缓地扣出一个:? 哈哈哈。
在逆向过程中,我找到了一段比较有意思的代码:


48:895C24 18    mov qword ptr ss:[rsp+18],rbx                 ; <== 暂停条件(RDX == 0xC8000006)
.......................省略了很多行............................
49:8BC2         mov rax,r10
E8 DE520400     call 0x7FFE6B435010                                   ; <== 动态获取 MSAFD_ConnectEx 的地址
44:8BF0         mov r14d,eax                                              ; <== *([rsp+28]) == MSAFD_ConnectEx 的地址

这里需要特别注意:
RDX == 0xC8000006 是我在调试过程中设置的暂停条件,它并不意味着 0xC8000006 本身就是 ConnectEx 的地址。
真正有意思的是后面的函数调用:

call 0x7FFE6B435010

这个过程最终会根据前面传入的扩展函数标识,找到对应的ConnectEx 实现。
也就是:

WSAID_CONNECTEX
       ↓
Winsock 扩展函数查找
       ↓
MSAFD_ConnectEx
       ↓
得到函数地址

开始实战

既然我们已经清楚了如何在 Ws2_32.dll 去蹲守,那就开始根据已有的结构开始布局吧。
这些函数的结构体你都可以在微软文档中获取,只有 MSAFD_ConnectEx 这个结构可能比较难直接找到。
根据逆向得到的调用形式,它的参数可以对应到:

BOOL MSAFD_ConnectEx(
    SOCKET s,
    const sockaddr *name,
    int namelen,
    PVOID lpSendBuffer,
    DWORD dwSendDataLength,
    LPDWORD lpdwBytesSent,
    LPOVERLAPPED lpOverlapped
);

如果我需要获取域名,那就去 Hook:

Ws2_32!GetAddrInfoExW

进入函数后:

GetAddrInfoExW:
40:55       push rbp                        ; <== RCX 里面装着域名
53            push rbx
56            push rsi

在这里可以根据函数参数去获取对应的域名。
需要注意的是,因为域名解析结果可能会被缓存,所以并不是每次网络连接都会重新进行一次 DNS 查询。
所以如果我们需要更加准确地获取目标 IP,还是需要继续往后跟MSAFDConnectEx
这里可以获取连接目标的地址信息。

MSAFD_ConnectEx:
48 8B C4      mov rax, rsp                  ; <== RDX 指向 sockaddr
48 89 58 08   mov [rax+8], rbx          ;     [RDX+2] => Port
48 89 70 10   mov [rax+10h], rsi        ;     [RDX+4] => IPAddress
48 89 78 18   mov [rax+18h], rdi

通过这里就可以进一步获取连接目标的 IP 和端口。
这样,即使前面的域名解析走了缓存,我们依然可以从实际建立连接的地方获取到目标地址。
继续往后跟:

Ws2_32!WSASend

这里就装着发送的数据。

WSASend:
48:895C24 08   mov qword ptr ss:[rsp+8],rbx         ; <== RDX => lpBuffers
48:896C24 10   mov qword ptr ss:[rsp+10],rbp        ;     [RDX]    => WSABUF.len
48:897424 18   mov qword ptr ss:[rsp+18],rsi        ;     [RDX+8]  => WSABUF.buf

WSASend 的关键就在于:

RDX
 ↓
lpBuffers
 ↓
WSABUF
 ├── len
 └── buf
       ↓
    实际发送的数据

所以我们就可以在这里拿到发送出去的数据。
如果是 HTTP 请求,那么通常就可以在这里看到类似:

GET / HTTP/1.1
Host: example.com

当然,具体数据还要根据程序使用的协议以及是否进行了加密来判断。
发送完请求之后,自然就是接收服务器返回的数据。
所以继续蹲:

Ws2_32!WSARecv

这里通常会有一个或者多个 WSABUF

WSARecv:
48:895C24 08   mov qword ptr ss:[rsp+8],rbx        ; <== RDX => lpBuffers
48:896C24 10   mov qword ptr ss:[rsp+10],rbp        ;     [RDX]    => WSABUF.len
48:897424 18   mov qword ptr ss:[rsp+18],rsi        ;     [RDX+8]  => WSABUF.buf

.......................省略了很多行............................

41:5F          pop r15
41:5E          pop r14
5F             pop rdi                ; <== 这里可以读取 [RDX+8] 指向的缓冲区
C3             ret

这里:

RDX
 ↓
lpBuffers
 ↓
WSABUF
 ├── len
 └── buf
       ↓
   服务器返回的数据

所以在合适的位置,我们就可以读取 [RDX+8] 指向的数据。
这里千万不要小瞧这个 buf。
如果数据本身是明文协议,例如 HTTP/1.1,那么你甚至可以在数据返回到上层之前观察甚至修改其中的内容。
例如:

HTTP/1.1 301 Moved Permanently

这类内容在合适的 Hook 点上都可以被观察到。
最后这里可以给你们提供一个简单的 Hook 模板。

func_address:
mov rax, hook_address
jmp rax                                             

hook_address:                                           
**:****    ; 首先还原你覆盖掉的那些字节  
**:****                                                      
pushad                                                        
**:****    ; Hook 汇编内容                        
**:****                                                    
popad                                                     
mov rax, func_address+0x12
jmp rax

当然,这只是一个非常简化的模板。
实际使用的时候,还需要根据目标函数的前几条指令、指令长度、寄存器状态以及栈布局进行调整。
对模板进行微调之后,你就可以根据自己的需求获取对应的数据。

成果展示

根据上述对Windows 网络 DLL的了解后我们就可以针对性去编写hook函数,从而获取我们想要的数据包了

这里我没有放出成品软件,还是因为我是针对性调整并没有适配全部Windows系统

注入窗口


注入成功后

成功监听网络数据

总结

对于 Windows 网络来说,肯定还有其他的 API 是我没有覆盖全面的。
不过通过这次的学习,我也算是寻找到了一条比较有意思的网络 API 调用路径。
通过了解这些 API 函数,感觉比生硬地去看网络知识会更加有意思。
特别是当你真正拿到数据之后,再去做解析的过程

你拿到的就是一个指针。

然后你需要对这个指针进行解引用,才能真正看到文档里面描述的结构体以及其中的数据。
还有很多句柄,其实在一些关键的地方还是很有用的,也能帮助我们更好地去理解 Windows 中的句柄操作。
在编写这些函数的时候,你可能会遇到很多宏定义。
当时可能完全不理解,甚至会觉得很疑惑。
但是等到后面在逆向过程中真正找到它、跟进去、理解它之后,才能真正体会到这些宏定义背后的奥妙。
逆向其实就是这样。
很多东西你单独去看,可能觉得很枯燥。
但是当你真正把它串起来,从一个 API 一路跟到另一个 DLL,再跟到真正执行网络操作的位置时,整个过程就会变得非常有意思。

看到这里,相信你也对这三个 DLL 以及 Windows 网络 API 的调用过程有了一个基本的理解。


免责声明:本文仅供技术研究和学习交流,请勿用于任何非法用途。读者因使用本文代码而产生的任何后果由本人自行承担

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
xiaolia 发表于 2026-8-14 20:14
感谢老哥
3#
hyzmllcn 发表于 2026-8-15 15:31
4#
xiao14116 发表于 2026-8-15 16:56
这其实是一个很古老的东西,你研究一下什么叫SPI和早就有的开源SPI框架就知道是怎么回事了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-8-17 08:05

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表