使用API Monitor工具巧妙探测C++程序中监听某端口的模块
目录
1、问题说明
2、API Monitor工具介绍
2.1、API Monitor主要用途
2.2、如何使用API Monitor工具
3、使用API Monitor监测程序对bind函数的调用,定位启用2620端口的模块
3.1、为啥要监控socket API函数bind
3.2、编写演示代码进行说明
3.3、使用API Monitor进行监测
4、使用API Monitor工具的注意事项
4.1、如何在Call Stack窗口中查看到更多的函数调用?
4.2、如果Start Monitoring失败怎么办?
5、最后
VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)
https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/article/details/131405795C/C++基础与进阶(专栏文章,持续更新中...)
https://blog.csdn.net/chenlycly/category_11931267.html 当我们没法确定软件中的某个端口是哪个模块启用时,可以使用API Monitor工具去监测软件中对相关socket函数的调用去搞清楚。本文将详细讲述如何使用API Monitor工具去监测对端口的启用情况。
1、问题说明
最近在对我们的C++软件做安全测试,使用专用工具对软件使用的端口号进行扫描,发现有模块启用了2620端口,安全测试组的同事需要明确搞清楚这个端口是用来做什么的,但询问了多个开发组,都没人认领这个端口。
后来想到可以尝试用API Monitor工具监测一下软件对socket API函数bind的调用(因为要开启监听端口,一般先要调用bind函数去绑定端口),如果能监测到记录,查看对应的函数调用堆栈就能确定是哪个模块启用了这个端口。
2、API Monitor工具介绍
2.1、API Monitor主要用途
API Monitor可以用来监测程序对Windows系统API函数的调用,也可以监测对第三方dll库接口的调用。监测到函数调用时,可以查看到给函数传递的参数值,还可以查看当前函数调用所在线程的函数调用堆栈。
使用该工具去窥探一些主流软件对系统API函数的调用情况,可以看到调用函数时传入的参数值,这样就能模仿一些主流软件去实现一些类似的功能。我们在项目中多次使用过这个方法,比如我们在处理安装包中创建桌面快捷方式时遇到的若干问题时就用过。
2.2、如何使用API Monitor工具
API Monitor工具的主界面如下所示:

一般我们在使用该工具监控程序对API函数的调用时,首先在左边的函数列表中搜索到要监控的系统API函数,然后勾选上该函数。
接着,在左下角的进程列表中找到要监测的目标进程,右键点击,在弹出的右键菜单中点击“Start Monitoring”开始监控该进程。如果目标程序在运行的过程中调用了被监控的函数,右上方的列表中就会显示对应的记录。选中某条记录时,可以查看调用目标函数时传入的参数值,还可以看到此时所在线程的函数调用堆栈。
和Windbg类似的,开启对目标程序的监测有两种方式:
1)可以在左下方的进程列表中找到已经启动的目标进程,然后右键点击,在弹出的右键菜单中开启监控。
2)可以通过API Monitor去启动目标进程,点击主界面中的“Monitor New Process”,找到目标程序的路径,启动即可。这种方式适用于监测程序启动过程中的函数调用活动。
3、使用API Monitor监测程序对bind函数的调用,定位启用2620端口的模块
3.1、为啥要监控socket API函数bind
一般负责网络通信的模块,无论是TCP还是UDP,底层最终调用的都是socket API函数。我们要监测哪个模块启用2620端口,一般监听端口时需要先调用bind函数绑定2620端口,所以我们监测对bind函数的调用即可。
我们可以在MSDN上对bind函数说明页面中看到相关示例代码:
#ifndef UNICODE
#define UNICODE
#endif#define WIN32_LEAN_AND_MEAN#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdio.h>// Link with ws2_32.lib
#pragma comment(lib, "Ws2_32.lib")int main()
{// Declare some variablesWSADATA wsaData;int iResult = 0; // used to return function results// the listening socket to be createdSOCKET ListenSocket = INVALID_SOCKET;// The socket address to be passed to bindsockaddr_in service;//----------------------// Initialize WinsockiResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (iResult != NO_ERROR) {printf("Error at WSAStartup()\n");return 1;}//----------------------// Create a SOCKET for listening for // incoming connection requestsListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ListenSocket == INVALID_SOCKET) {printf("socket function failed with error: %u\n", WSAGetLastError());WSACleanup();return 1;}//----------------------// The sockaddr_in structure specifies the address family,// IP address, and port for the socket that is being bound.service.sin_family = AF_INET;service.sin_addr.s_addr = inet_addr("127.0.0.1");service.sin_port = htons(2620);//----------------------// Bind the socket.iResult = bind(ListenSocket, (SOCKADDR *) &service, sizeof (service));if (iResult == SOCKET_ERROR) {printf("bind failed with error %u\n", WSAGetLastError());closesocket(ListenSocket);WSACleanup();return 1;}elsewprintf("bind returned success\n");WSACleanup();return 0;
}
从上面代码可以看出,在调用bind函数时,传入的是sockaddr_in结构体对象地址,然后强转成SOCKADDR*。
对于Windows C++程序员,在使用系统API函数遇到问题时,要会到MSDN上查看函数声明、参数含义以及函数的remarks说明,这是基本的要求!
3.2、编写演示代码进行说明
此处不便展示项目中的相关模块和接口,也为了方便日后的视频课程的讲解,我特意写了一些测试代码,来讲述整个问题的监测过程。
我在测试程序启动时调用bind函数绑定2620端口号,相关代码如下所示:
// Declare some variablesWSADATA wsaData;int iResult = 0; // used to return function results// the listening socket to be createdSOCKET ListenSocket = INVALID_SOCKET;// The socket address to be passed to bindsockaddr_in service;//----------------------// Initialize WinsockiResult = WSAStartup(MAKEWORD(2, 2), &wsaData);if (iResult != NO_ERROR) {printf("Error at WSAStartup()\n");return 1;}//----------------------// Create a SOCKET for listening for // incoming connection requestsListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (ListenSocket == INVALID_SOCKET) {printf("socket function failed with error: %u\n", WSAGetLastError());WSACleanup();return 1;}//----------------------// The sockaddr_in structure specifies the address family,// IP address, and port for the socket that is being bound.service.sin_family = AF_INET;service.sin_addr.s_addr = inet_addr("127.0.0.1");service.sin_port = htons(2620);//----------------------// Bind the socket.iResult = bind(ListenSocket, (SOCKADDR *) &service, sizeof (service));
3.3、使用API Monitor进行监测
因为是在程序启动过程中绑定端口的,所以需要使用API Monitor工具启动目标程序,要从程序启动时就开始监控。

首先,在左边的函数列表中搜索到bind函数,因为可能很多函数名称中都包含bind子串,所以搜索时为了快速搜到目标函数,可以勾上“全字匹配”和“大小写匹配”。
然后,点击“Monitor New Process”,找到目标程序的路径,启动程序并开始监测。程序在运行过程中调用了bind函数,就会显示出对应的记录(如果有多次调用,会有多个记录与之对应),如下所示:

在我们的项目软件中,实际上监测到了多条记录,我们如何确定哪条记录操作的端口号是2620呢?
这时我们就需要查看调用bind函数时传入的参数了!选中某条记录时,Parameters窗口中就会显示调用该函数时传入的参数值,并且Call Stack窗口中会显示所在线程的函数调用堆栈。
这个地方注意一下,Parameters窗口中显示的是SOCKADDR*指针,但我们在调用bind函数时传入的是sockaddr_in结构体对象数据,其中端口是设置到该结构体的sin_port字段中的,那么sin_port结构体和SOCKADDR结构体有什么的对应关系呢?我们可以在Visual Studio中go到这两个结构体的定义处,如下所示:
//
// Structure used to store most addresses.
//
typedef struct sockaddr {#if (_WIN32_WINNT < 0x0600)u_short sa_family;
#else ADDRESS_FAMILY sa_family; // Address family.
#endif //(_WIN32_WINNT < 0x0600)CHAR sa_data[14]; // Up to 14 bytes of direct address.
} SOCKADDR, *PSOCKADDR, FAR *LPSOCKADDR;
//
// IPv4 Socket address, Internet style
//typedef struct sockaddr_in {#if(_WIN32_WINNT < 0x0600)short sin_family;
#else //(_WIN32_WINNT < 0x0600)ADDRESS_FAMILY sin_family;
#endif //(_WIN32_WINNT < 0x0600)USHORT sin_port;IN_ADDR sin_addr;CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;
这两个结构体的第一个字段都是sin_family,sockaddr_in结构体的第二个字段就是存放端口号的sin_port字段,所以在调用bind函数将sockaddr_in结构体对象地址强转成SOCKADDR对象地址,那么端口号实际上是对应到SOCKADDR结构体中的sa_data数组内存,端口号占两个字节,所以端口号值是存放在sa_data[0]和sa_data[1]内存中,所以我们在Parameters窗口中查看sa_data[0]和sa_data[1]内存中的值,看能否和2620端口号对应起来。
我们可以使用Windows系统自带的计算器工具将十进制的2620转成16进制数据:

所以两个字节存放的数据为0x0A和0x3C,转换成10进制就是10和60,这样拿这两个值到Parameters窗口中比对,就能确定对应的条目了。
确定对应的条目后,到Call Stack窗口中查看函数调用堆栈,就可以确定是哪个模块启动了2620端口了。
4、使用API Monitor工具的注意事项
4.1、如何在Call Stack窗口中查看到更多的函数调用?
注意,API Monitor的函数调用堆栈窗口中默认只显示5行最近的函数调用,如果要显示更多的函数调用,需要到设置中去设置一下。具体的入口是,点击菜单栏中的Tools -> Options,在打开的设置窗口中,点击Monitoring按钮,显示对应的子页面,在页面中可以修改函数调用堆栈中显示的条目数目:

此处注意一下,API Monitor工具不支持加载pdb文件,不像Process Explorer和Process Monitor那样支持加载pdb文件!
4.2、如果Start Monitoring失败怎么办?
如果在左下角的进程列表中右键点击目标进程,在弹出的右键菜单中点击“Start Monitoring”开始监控,结果报错,提示开启监控失败。可能是目标进程是以管理员权限运行的,而当前的API Monitor没有管理员权限运行,而是以标准用户权限运行的。因为目标进程的运行权限(管理员权限)与API Monitor(标准用户权限)不对等,所以在低权限的API Monitor中不能操作高权限的目标进程。
解决办法是,关闭当前的API Monitor程序,然后右键以管理员权限重新启动API Monitor,即可开启对目标进程的监控了。
5、最后
本例通过API Monitor去监控程序对bind函数的调用,去巧妙定位启用2620端口的模块。文中详细讲解了API Monitor定位问题的完整过程,希望能给大家提供一个借鉴或参考。
相关文章:
使用API Monitor工具巧妙探测C++程序中监听某端口的模块
目录 1、问题说明 2、API Monitor工具介绍 2.1、API Monitor主要用途 2.2、如何使用API Monitor工具 3、使用API Monitor监测程序对bind函数的调用,定位启用2620端口的模块 3.1、为啥要监控socket API函数bind 3.2、编写演示代码进行说明 3.3、使用API Moni…...
vue-grid-layout移动卡片到页面底部时页面滚动条跟随滚动
问题描述:默认情况下 vue-grid-layout 移动卡片到页面底部时页面滚动条并不会跟随卡片滚动。 问题解决: 在 grid-item中的move事件中,获取到当前移动的元素,并使用scrollIntoView方法来实现滚动条跟随。 代码如下: c…...
git查看自己所在的分支
很多时候可能大家不太想切换其他工具,又不知道自己是否在自己需要操作的分支 可以直接终端执行 git branch此时 他就会在终端将所有的本地分支输出出来 并特殊标注自己所在的分支 这样我们就可以进一步去做自己想要做的操作了 当然 随着各种编辑器的发展 这个命令…...
竞赛 基于视觉的身份证识别系统
0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于机器视觉的身份证识别系统 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🧿 更多资料, 项目分享: https://gitee.com/dancheng-sen…...
Redis的softMinEvictableIdleTimeMillis和minEvictableIdleTimeMillis参数
背景: Redis的softMinEvictableIdleTimeMillis,minEvictableIdleTimeMillis是一个令人疑惑两个参数,特别是当它和minIdle组合起来时就更难理解了,本文就来梳理下他们的之间的关系 softMinEvictableIdleTimeMillis,mi…...
向量数据库库Milvus Cloud2.3 的QA问题
1. Milvus 从 2.2.x 升级至 2.3.x 的最大变化是什么? 如果用一句话来总结,那就是使用的场景更加丰富了。具体可以从两个方面来体现,即部署环境和用户的使用感。 例如,从部署环境来看,Milvus 原来只支持 X86 架构的 CPU,版本升级后,不仅可以支持 GPU,还能够支持 ARM 架构…...
嵌入式 - 经典的有刷电机和先进的无刷电机
自从无刷直流电机诞生,“古老的”有刷电机就开始没落,但它依然是低成本应用的可靠选择,并且实现起来简单。 在有刷电机中,磁极方向的跳转是通过移动固定位置的接触点来完成的,该接触点在电机转子上与电触点相对连接。这…...
【力扣2154】将找到的值乘以 2
👑专栏内容:力扣刷题⛪个人主页:子夜的星的主页💕座右铭:前路未远,步履不停 目录 一、题目描述二、题目分析 一、题目描述 题目链接:将找到的值乘以 2 给你一个整数数组 nums ,另给…...
C++ —— 单机软件加入Licence许可权限流程(附详细流程图、详细代码已持续更新..)
单机版许可证简介 笼统的说:实现一个生成授权Lic文件应用程序(我们使用),生成的Lic文件给应用程序(客户使用)启动时读取一下对比加密后的字符串或自定义格式的密钥判断是否正确。 单机版许可证执行流程 第一级比对:发布的加密许可证文件,该加密许可证文件仅可用使用的软…...
Windows 下 MySQL 8.1.0 安装及配置图文指南,快速搭建实验学习环境
目录 下载 MySQL安装 MySQL配置 MySQL修改密码配置环境变量 卸载 MySQL开源项目微服务商城项目前后端分离项目 下载 MySQL 访问 MySQL 下载地址:https://dev.mysql.com/downloads/mysql/ 下载 MySQL 时,你可以选择 ZIP 包或 MSI 安装: ZIP包…...
Linux内核顶层Makefile的make过程总结
一. Linux内核源码的make编译 本文对 Linux内核源码的 make时, 顶层Makefile所做的事进行总结。即总结一下 Linux内核源码的 make 过程。 本文续上一篇文章,地址如下: Linux内核顶层Makefile的make过程说明二_凌肖战的博客-CSDN博客 二.…...
C语言每日一题(9):跳水比赛猜名次
文章主题:跳水比赛猜名次🔥所属专栏:C语言每日一题📗作者简介:每天不定时更新C语言的小白一枚,记录分享自己每天的所思所想😄🎶个人主页:[₽]的个人主页🏄&am…...
L10 数据库
1, 数据库的安装 sudo dpkg -i *.deb 2, 数据库命令: 1)系统命令 , 都以.开头 .exit .quit .table 查看表 .schema 查看表的结构 2)sql语句&…...
前端面试:01.图中输入什么?
~~~~~~~~~~~~~ 先自行想一想,答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想,答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想,答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~ 先自行想一想,答案在~~~~~~~~~~~~~~~~~ ~~~~~~~~…...
Oracle拉链表
目录 -- 准备一个拉链表 -- 2.将所有的数据 同步到拉链表中 TEST_TARGET中 --3. 源表的数据发生了变化 --4. 将新增和修改的数据同步到拉链表 -- 开链的过程 -- 判断源表和目标表的数据,不同数据插入 --5. 修改拉链表中失效的时间和状态(将原本的开链时间,改为当前时间)-- …...
Git 代理(Proxy) 配置
某些情况下,我们需要通过代理才能访问特定网络环境下的git资源,git支持代理配置, 支持 http(s), SOCKS4/SOCKS5. HTTP(S) HTTP 代理配置格式如下: git config --global http.proxy http://[proxy]:[port]实际环境下, 其实我们大多数情况下,并不需要全部git资源都需要通过代理…...
C++,对象赋值与对象拷贝的区别、深浅拷贝
在C中,对象赋值和对象拷贝是两个不同的操作,它们有明显的区别: 1. 对象赋值(Object Assignment): - 对象赋值是指将一个已经存在的对象的值复制给另一个已经存在的对象。这通常通过赋值操作符(…...
MATLAB实现相关性分析
目录 一.基本理论 二.两类相关系数的对比 三.相关系数的假设检验 四.MATLAB的相关操作 五.其他有关的一些列技巧 六.案例展示 七.实战操作 一.基本理论 所谓相关系数,本质上是来衡量两组数据的关系大小——对应呈现函数关心的两种变量,那么我们可以…...
MySQL索引看这篇就够了
能简单说一下索引的分类吗? 例如从基本使用使用的角度来讲: 主键索引: InnoDB 主键是默认的索引,数据列不允许重复,不允许为 NULL,一个表只能有一个主键。唯一索引: 数据列不允许重复,允许为 NULL 值&…...
无法从 /var/lib/rpm 打开软件包数据库
使用yum命令安装软件包时,报错“无法从 /var/lib/rpm 打开软件包数据库” 小白教程,一看就会,一做就成。 1.原因 是误操作导致 rpm 数据库损坏。(/var/lib/rpm 目录下的文件被损坏) 2.解决 当RPM 数据库发生损坏&a…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
装饰模式(Decorator Pattern)重构java邮件发奖系统实战
前言 现在我们有个如下的需求,设计一个邮件发奖的小系统, 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
