乐于分享
好东西不私藏

跨平台系统信息获取库libsigar源码分析系列(九)

跨平台系统信息获取库libsigar源码分析系列(九)

官网:http://securitytech.cc

源码分析mettle后门工具学习 所使用的依赖库

libsigar Windows 分支源码深度解析

目录

  1. 概述
  2. 核心架构
  3. 模块组织
  4. 关键技术实现
  5. API调用分析
  6. 核心功能模块
  7. 性能优化机制
  8. 特殊技术

概述

libsigar (System Information Gatherer And Reporter) 的 Windows 分支实现了一套完整的跨平台系统信息收集接口。Windows 实现主要通过 Windows API、性能计数器 (Performance Counters)、WMI (Windows Management Instrumentation) 以及各种系统 DLL 动态加载来获取系统信息。

关键文件结构

  1. src/os/win32/
  2. ├── win32_sigar.c       # 主要实现文件 (113KB, 4300+行)
  3. ├── sigar_os.h          # Windows特定头文件定义
  4. ├── sigar_pdh.h         # 性能数据助手宏定义
  5. ├── peb.c               # 进程环境块(PEB)相关操作
  6. ├── wmi.cpp             # WMI接口封装 (C++)
  7. └── sigar.rc.in# 资源文件模板

核心架构

1. sigar_t 结构体 (Windows 扩展)

  1. structsigar_t{
  2.     SIGAR_T_BASE;// 基础字段
  3. char*machine;// 目标机器名称
  4. int using_wide;// 是否使用宽字符
  5. long pagesize;// 页面大小
  6. // 性能计数器相关
  7.     HKEY handle;// 注册表句柄 (HKEY_PERFORMANCE_DATA)
  8. char*perfbuf;// 性能计数器缓冲区
  9.     DWORD perfbuf_size;// 缓冲区大小
  10. // DLL 模块句柄集合
  11. sigar_wmi_handle_t*wmi_handle;// WMI 句柄
  12. sigar_wtsapi_t wtsapi;// 终端服务API
  13. sigar_iphlpapi_t iphlpapi;// IP助手API
  14. sigar_advapi_t advapi;// 高级API
  15. sigar_ntdll_t ntdll;// NT DLL
  16. sigar_psapi_t psapi;// 进程状态API
  17. sigar_winsta_t winsta;// 工作站API
  18. sigar_kernel_t kernel;// 内核API
  19. sigar_mpr_t mpr;// 多提供商路由器
  20. // 缓存和状态
  21. sigar_win32_pinfo_t pinfo;// 进程信息缓存
  22. sigar_cache_t*netif_adapters;// 网卡适配器缓存
  23. sigar_cache_t*netif_mib_rows;// MIB行缓存
  24. sigar_cache_t*netif_addr_rows;// 地址行缓存
  25. sigar_cache_t*netif_names;// 网卡名称缓存
  26. int netif_name_short;// 网卡名称简化模式
  27.     WORD ws_version;// Winsock版本
  28. int ws_error;// Winsock错误
  29. int ht_enabled;// 超线程启用
  30. int lcpu;// 逻辑CPU数量
  31. int winnt;// 是否为Windows NT
  32. };

2. DLL 动态加载机制

libsigar 使用动态加载来确保向后兼容性,避免旧版本 Windows API 调用失败:

  1. typedefstruct{
  2. constchar*name;// DLL名称
  3.     HINSTANCE handle;// 加载的句柄
  4. }sigar_dll_handle_t;
  5. typedefstruct{
  6. constchar*name;// 函数名
  7.     FARPROC func;// 函数指针
  8. }sigar_dll_func_t;
  9. typedefstruct{
  10. sigar_dll_handle_t handle;
  11. sigar_dll_func_t funcs[12];// 函数数组
  12. }sigar_dll_module_t;

加载的 DLL 列表:

DLL
主要用途
关键函数
wtsapi32.dll
终端服务会话管理
WTSEnumerateSessions, WTSQuerySessionInformation
iphlpapi.dll
IP助手功能
GetAdaptersInfo, GetTcpTable, GetIpAddrTable
advapi32.dll
高级安全和服务管理
ConvertStringSidToSid, QueryServiceStatusEx
ntdll.dll
NT底层系统信息
NtQuerySystemInformation, NtQueryInformationProcess
psapi.dll
进程状态API
EnumProcesses, EnumProcessModules
winsta.dll
工作站信息
WinStationQueryInformationW
kernel32.dll
内核功能
GlobalMemoryStatusEx
mpr.dll
多提供商路由器
WNetGetConnectionA

模块组织

1. 初始化和清理

sigarosopen() – 初始化函数

  1. int sigar_os_open(sigar_t**sigar_ptr){
  2. sigar_t*sigar = calloc(1,sizeof(*sigar));
  3. // 连接到性能数据注册表
  4. RegConnectRegistryA(sigar->machine, HKEY_PERFORMANCE_DATA,&sigar->handle);
  5. // 获取系统信息
  6. GetSystemInfo(&sysinfo);
  7.     sigar->ncpu = sysinfo.dwNumberOfProcessors;
  8.     sigar->pagesize = sysinfo.dwPageSize;
  9. // 初始化所有DLL模块
  10.     DLLMOD_COPY(wtsapi);
  11.     DLLMOD_COPY(iphlpapi);
  12. // ... 其他DLL
  13. // 启用调试权限以增加进程可见性
  14.     sigar_enable_privilege(SE_DEBUG_NAME);
  15. // 初始化WMI连接
  16.     sigar->wmi_handle =(sigar_wmi_handle_t*)wmi_handle_open(&wmi_status);
  17. // 初始化网络接口缓存
  18.     sigar->netif_mib_rows = NULL;
  19.     sigar->netif_addr_rows = NULL;
  20.     sigar->netif_adapters = NULL;
  21.     sigar->netif_names = NULL;
  22. }

sigarosclose() – 清理函数

  1. int sigar_os_close(sigar_t*sigar){
  2. // 释放所有DLL
  3.     DLLMOD_FREE(wtsapi);
  4.     DLLMOD_FREE(iphlpapi);
  5. // ... 其他DLL
  6. // 释放缓存
  7. if(sigar->netif_mib_rows){
  8.         sigar_cache_destroy(sigar->netif_mib_rows);
  9. }
  10. // 关闭注册表句柄
  11. RegCloseKey(sigar->handle);
  12. // 清理Winsock
  13. if(sigar->ws_version !=0){
  14. WSACleanup();
  15. }
  16.     free(sigar);
  17. }

关键技术实现

1. 性能计数器 (Performance Counters)

libsigar 通过访问 HKEY_PERFORMANCE_DATA 注册表键来获取 Windows 性能计数器数据。

性能计数器缓冲区管理

  1. #define PERFBUF_SIZE 8192
  2. static DWORD perfbuf_init(sigar_t*sigar){
  3. if(!sigar->perfbuf){
  4.         sigar->perfbuf = calloc(1, PERFBUF_SIZE);
  5.         sigar->perfbuf_size = PERFBUF_SIZE;
  6. }
  7. return sigar->perfbuf_size;
  8. }
  9. static DWORD perfbuf_grow(sigar_t*sigar){
  10.     sigar->perfbuf_size += PERFBUF_SIZE;
  11.     sigar->perfbuf = realloc(sigar->perfbuf, sigar->perfbuf_size);
  12. return sigar->perfbuf_size;
  13. }

性能计数器访问宏

  1. #defineMyRegQueryValue() \
  2. (USING_WIDE()? \
  3. RegQueryValueExW(sigar->handle, \
  4.                          wcounter_key, NULL,&type, \
  5. (LPBYTE)sigar->perfbuf, \
  6. &bytes): \
  7. RegQueryValueExA(sigar->handle, \
  8.                          counter_key, NULL,&type, \
  9. (LPVOID)sigar->perfbuf, \
  10. &bytes))

主要性能计数器键值

计数器键
标题索引
用途
“2”
PERFTITLESYS_KEY
系统信息
“4”
PERFTITLEMEM_KEY
内存信息
“230”
PERFTITLEPROC_KEY
进程信息
“238”
PERFTITLECPU_KEY
CPU信息
“236”
PERFTITLEDISK_KEY
磁盘信息

性能计数器偏移量定义

  1. // CPU 计数器
  2. #define PERF_TITLE_CPU_USER    142
  3. #define PERF_TITLE_CPU_IDLE    1746
  4. #define PERF_TITLE_CPU_SYS     144
  5. #define PERF_TITLE_CPU_IRQ     698
  6. // 进程计数器
  7. #define PERF_TITLE_CPUTIME    6
  8. #define PERF_TITLE_PAGE_FAULTS 28
  9. #define PERF_TITLE_MEM_VSIZE  174
  10. #define PERF_TITLE_MEM_SIZE   180
  11. #define PERF_TITLE_THREAD_CNT 680
  12. #define PERF_TITLE_HANDLE_CNT 952
  13. #define PERF_TITLE_PID        784
  14. #define PERF_TITLE_PPID       1410
  15. #define PERF_TITLE_PRIORITY   682
  16. #define PERF_TITLE_START_TIME 684
  17. #define PERF_TITLE_IO_READ_BYTES_SEC 1420
  18. #define PERF_TITLE_IO_WRITE_BYTES_SEC 1422
  19. // 磁盘计数器
  20. #define PERF_TITLE_DISK_TIME 200
  21. #define PERF_TITLE_DISK_READ_TIME 202
  22. #define PERF_TITLE_DISK_WRITE_TIME 204
  23. #define PERF_TITLE_DISK_READ  214
  24. #define PERF_TITLE_DISK_WRITE 216
  25. #define PERF_TITLE_DISK_READ_BYTES  220
  26. #define PERF_TITLE_DISK_WRITE_BYTES 222
  27. #define PERF_TITLE_DISK_QUEUE 198

2. NT系统调用 (NtQuerySystemInformation)

对于某些 Windows NT/2000 系统或需要更底层的系统信息,libsigar 使用未文档化的 NT API:

  1. #define SPPI_MAX 128
  2. typedefstruct{
  3.     LARGE_INTEGER IdleTime;
  4.     LARGE_INTEGER KernelTime;
  5.     LARGE_INTEGER UserTime;
  6.     LARGE_INTEGER DpcTime;
  7.     LARGE_INTEGER InterruptTime;
  8.     ULONG InterruptCount;
  9. } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
  10. #defineSystemProcessorPerformanceInformation8
  11. staticint sigar_cpu_ntsys_get(sigar_t*sigar,sigar_cpu_t*cpu){
  12.     SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX];
  13.     sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  14. &info,sizeof(info),&retval);
  15. if(!retval){
  16. returnGetLastError();
  17. }
  18.     num = retval/sizeof(info[0]);
  19.     SIGAR_ZERO(cpu);
  20. for(i=0; i<num; i++){
  21.         cpu->idle += NS100_2MSEC(info[i].IdleTime.QuadPart);
  22.         cpu->user += NS100_2MSEC(info[i].UserTime.QuadPart);
  23.         cpu->sys  += NS100_2MSEC(info[i].KernelTime.QuadPart-
  24.                                  info[i].IdleTime.QuadPart);
  25.         cpu->irq  += NS100_2MSEC(info[i].InterruptTime.QuadPart);
  26. }
  27.     cpu->total = cpu->idle + cpu->user + cpu->sys;
  28. return SIGAR_OK;
  29. }

3. 进程环境块 (PEB) 操作

PEB (Process Environment Block) 包含进程的运行时信息,包括命令行参数、工作目录等。

peb.c 实现

  1. typedefstruct _PEB {
  2.     BYTE Reserved1[2];
  3.     BYTE BeingDebugged;
  4.     BYTE Reserved2[1];
  5.     PVOID Reserved3[2];
  6.     PPEB_LDR_DATA Ldr;
  7.     PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
  8.     BYTE Reserved4[104];
  9.     PVOID Reserved5[52];
  10.     BYTE Reserved6[128];
  11.     PVOID Reserved7[1];
  12.     ULONG SessionId;
  13. } PEB,*PPEB;
  14. typedefstruct _PROCESS_BASIC_INFORMATION {
  15.     PVOID Reserved1;
  16.     PPEB PebBaseAddress;
  17.     PVOID Reserved2[2];
  18.     UINT_PTR UniqueProcessId;
  19.     PVOID Reserved3;
  20. } PROCESS_BASIC_INFORMATION;
  21. staticint sigar_pbi_get(sigar_t*sigar, HANDLE proc, PEB *peb){
  22.     PROCESS_BASIC_INFORMATION pbi;
  23.     DWORD size =sizeof(pbi);
  24.     sigar_NtQueryInformationProcess(proc,
  25. ProcessBasicInformation,
  26. &pbi, size, NULL);
  27. if(!pbi.PebBaseAddress){
  28. return ERROR_DATATYPE_MISMATCH;// 可能是32位/64位不匹配
  29. }
  30. if(ReadProcessMemory(proc, pbi.PebBaseAddress, peb, size, NULL)){
  31. return SIGAR_OK;
  32. }
  33. else{
  34. returnGetLastError();
  35. }
  36. }

4. WMI 接口 (wmi.cpp)

WMI 提供了更高级的接口来获取系统信息,特别是在某些性能计数器不可用时。

WMI 初始化

  1. sigar_wmi_handle_t* wmi_handle_open(int*error){
  2. sigar_wmi_handle_t*handle =(sigar_wmi_handle_t*)calloc(1,sizeof(*handle));
  3. // 初始化COM
  4.     HRESULT hres =CoInitializeEx(NULL, COINIT_MULTITHREADED);
  5. if(FAILED(hres))goto err;
  6. // 设置COM安全级别
  7.     hres =CoInitializeSecurity(NULL,-1, NULL, NULL,
  8.                                 RPC_C_AUTHN_LEVEL_CONNECT,
  9.                                 RPC_C_IMP_LEVEL_IMPERSONATE,
  10.                                 NULL, EOAC_NONE,0);
  11. if(FAILED(hres)&& hres != RPC_E_TOO_LATE)goto err;
  12. // 创建WMI定位器
  13.     hres =CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_ALL,
  14.                            IID_PPV_ARGS(&handle->locator));
  15. if(FAILED(hres))goto err;
  16. // 连接到WMI服务
  17. wchar_t root[]= L"root\\CIMV2";
  18.     hres = handle->locator->ConnectServer(root, NULL, NULL, NULL,
  19.                                           WBEM_FLAG_CONNECT_USE_MAX_WAIT,
  20.                                           NULL, NULL,&handle->services);
  21. if(FAILED(hres))goto err;
  22. return handle;
  23. }

WMI 查询

  1. HRESULT wmi_get_proc_string_property(sigar_t*sigar, DWORD pid,
  2.                                      TCHAR * name, TCHAR * value, DWORD len){
  3. IWbemClassObject*obj;
  4.     VARIANT var;
  5. wchar_t query[56];
  6.     wsprintf(query, L"Win32_Process.Handle=%d", pid);
  7.     result = sigar->wmi_handle->services->GetObject(query,0,0,&obj,0);
  8. if(FAILED(result))return result;
  9.     result = obj->Get(name,0,&var,0,0);
  10. if(SUCCEEDED(result)){
  11. if(var.vt == VT_NULL){
  12.             result = E_INVALIDARG;
  13. }else{
  14.             lstrcpyn(value, var.bstrVal, len);
  15. }
  16. VariantClear(&var);
  17. }
  18.     obj->Release();
  19. return result;
  20. }

API调用分析

1. 内存信息获取

  1. SIGAR_DECLARE(int) sigar_mem_get(sigar_t*sigar,sigar_mem_t*mem){
  2.     DLLMOD_INIT(kernel, TRUE);
  3. if(sigar_GlobalMemoryStatusEx){
  4.         MEMORYSTATUSEX memstat;
  5.         memstat.dwLength =sizeof(memstat);
  6. if(!sigar_GlobalMemoryStatusEx(&memstat)){
  7. returnGetLastError();
  8. }
  9.         mem->total = memstat.ullTotalPhys;
  10.         mem->free  = memstat.ullAvailPhys;
  11. }
  12. else{
  13.         MEMORYSTATUS memstat;
  14. GlobalMemoryStatus(&memstat);
  15.         mem->total = memstat.dwTotalPhys;
  16.         mem->free  = memstat.dwAvailPhys;
  17. }
  18.     mem->used = mem->total - mem->free;
  19. // 从性能计数器获取实际可用内存(考虑文件缓存)
  20.     get_mem_counters(sigar, NULL, mem);
  21.     sigar_mem_calc_ram(sigar, mem);
  22. return SIGAR_OK;
  23. }

2. CPU 信息获取

方式一:性能计数器

  1. staticint sigar_cpu_perflib_get(sigar_t*sigar,sigar_cpu_t*cpu){
  2.     PERF_INSTANCE_DEFINITION *inst;
  3.     PERF_COUNTER_BLOCK *counter_block;
  4.     DWORD perf_offsets[PERF_IX_CPU_MAX];
  5.     SIGAR_ZERO(cpu);
  6.     memset(&perf_offsets,0,sizeof(perf_offsets));
  7.     inst = get_cpu_instance(sigar,(DWORD*)&perf_offsets,0,&err);
  8. if(!inst)return err;
  9. // 第一个实例是总CPU时间
  10.     counter_block =PdhGetCounterBlock(inst);
  11.     cpu->sys  = PERF_VAL_CPU(PERF_IX_CPU_SYS);
  12.     cpu->user = PERF_VAL_CPU(PERF_IX_CPU_USER);
  13.     status = get_idle_cpu(sigar, cpu,-1, counter_block, perf_offsets);
  14.     cpu->irq = PERF_VAL_CPU(PERF_IX_CPU_IRQ);
  15.     cpu->nice =0;// Windows没有nice值
  16.     cpu->wait =0;// Windows没有wait值
  17.     cpu->total = cpu->sys + cpu->user + cpu->idle + cpu->wait + cpu->irq;
  18. return SIGAR_OK;
  19. }

方式二:NT系统调用

  1. staticint sigar_cpu_ntsys_get(sigar_t*sigar,sigar_cpu_t*cpu){
  2.     SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX];
  3.     sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation,
  4. &info,sizeof(info),&retval);
  5. if(!retval)returnGetLastError();
  6.     num = retval/sizeof(info[0]);
  7.     SIGAR_ZERO(cpu);
  8. for(i=0; i<num; i++){
  9.         cpu->idle += NS100_2MSEC(info[i].IdleTime.QuadPart);
  10.         cpu->user += NS100_2MSEC(info[i].UserTime.QuadPart);
  11.         cpu->sys  += NS100_2MSEC(info[i].KernelTime.QuadPart-
  12.                                  info[i].IdleTime.QuadPart);
  13.         cpu->irq  += NS100_2MSEC(info[i].InterruptTime.QuadPart);
  14. }
  15.     cpu->total = cpu->idle + cpu->user + cpu->sys;
  16. return SIGAR_OK;
  17. }

3. 进程列表获取

  1. int sigar_os_proc_list_get(sigar_t*sigar,sigar_proc_list_t*proclist){
  2.     DLLMOD_INIT(psapi, FALSE);
  3. if(sigar_EnumProcesses){
  4.         DWORD retval,*pids;
  5.         DWORD size =0, i;
  6. // 动态调整缓冲区大小
  7. do{
  8. if(size ==0){
  9.                 size = perfbuf_init(sigar);
  10. }
  11. else{
  12.                 size = perfbuf_grow(sigar);
  13. }
  14. if(!sigar_EnumProcesses((DWORD *)sigar->perfbuf,
  15.                                      sigar->perfbuf_size,
  16. &retval))
  17. {
  18. returnGetLastError();
  19. }
  20. }while(retval == sigar->perfbuf_size);
  21.         pids =(DWORD *)sigar->perfbuf;
  22.         size = retval /sizeof(DWORD);
  23. for(i=0; i<size; i++){
  24.             DWORD pid = pids[i];
  25. if(pid ==0){
  26. continue;// 跳过系统空闲进程
  27. }
  28.             SIGAR_PROC_LIST_GROW(proclist);
  29.             proclist->data[proclist->number++]= pid;
  30. }
  31. return SIGAR_OK;
  32. }
  33. else{
  34. // 回退到性能计数器方式
  35. return sigar_proc_list_get_perf(sigar, proclist);
  36. }
  37. }

4. 进程信息缓存机制

  1. staticint get_proc_info(sigar_t*sigar,sigar_pid_t pid){
  2. sigar_win32_pinfo_t*pinfo =&sigar->pinfo;
  3. time_t timenow = time(NULL);
  4. // 缓存检查:如果在缓存过期时间内且PID相同,直接返回缓存数据
  5. if(pinfo->pid == pid){
  6. if((timenow - pinfo->mtime)< SIGAR_LAST_PROC_EXPIRE){
  7. return SIGAR_OK;
  8. }
  9. }
  10.     memset(&perf_offsets,0,sizeof(perf_offsets));
  11.     object = get_process_object(sigar,&err);
  12. if(!object)return err;
  13. // ... 获取进程计数器数据并填充 pinfo
  14.     pinfo->pid = pid;
  15.     pinfo->mtime = timenow;
  16. return SIGAR_OK;
  17. }

核心功能模块

1. 内存监控模块

物理内存

  • APIGlobalMemoryStatusEx() 或 GlobalMemoryStatus()
  • 信息:
  • 总物理内存 ( ullTotalPhys)
  • 可用物理内存 ( ullAvailPhys)
  • 文件缓存 ( SystemCacheResidentBytes)

虚拟内存/交换分区

  • APIGlobalMemoryStatusEx()
  • 信息:
  • 总页文件大小 ( ullTotalPageFile)
  • 可用页文件大小 ( ullAvailPageFile)
  • 页面换入/换出速率 ( PagesInput/secPagesOutput/sec)

2. CPU 监控模块

CPU 时间统计

  • User Time: 用户态时间
  • System Time: 内核态时间(不含空闲)
  • Idle Time: 空闲时间
  • Interrupt Time: 中断时间
  • DPC Time: 延迟过程调用时间

多CPU支持

  • 总CPU信息(所有CPU的汇总)
  • 单CPU信息(每个逻辑CPU的独立统计)
  • 逻辑CPU合并(支持超线程的物理核心合并)

3. 进程监控模块

进程列表

  • APIEnumProcesses() (psapi.dll)
  • 备选: 性能计数器 (Process 对象)

进程内存

  • Virtual Bytes: 虚拟内存大小
  • Working Set: 工作集(物理内存)
  • Page Faults: 页面错误数

进程时间

  • User Time: 用户态时间
  • System Time: 内核态时间
  • Start Time: 进程启动时间

进程标识

  • Process ID: 进程ID
  • Parent Process ID: 父进程ID
  • Thread Count: 线程数
  • Handle Count: 句柄数

进程环境

  • 命令行参数: 通过 PEB 或 WMI
  • 可执行文件路径: 通过 PEB 或 WMI
  • 工作目录: 通过 PEB
  • 环境变量: 通过 PEB

4. 网络监控模块

网络接口列表

  • APIGetIfTable() (iphlpapi.dll)
  • 信息:
  • 接口名称和描述
  • MAC 地址
  • MTU
  • 操作状态

IP 配置

  • APIGetAdaptersInfo() 或 GetAdaptersAddresses()
  • 信息:
  • IPv4 地址
  • IPv6 地址
  • 子网掩码
  • 广播地址

网络统计

  • 发送/接收字节数
  • 发送/接收包数
  • 发送/接收错误数
  • 丢弃的包数

网络路由

  • APIGetIpForwardTable()
  • 信息:
  • 目标地址
  • 网络掩码
  • 网关
  • 接口索引
  • 跃点数

5. 文件系统监控模块

磁盘使用

  • APIGetDiskFreeSpaceEx()
  • 信息:
  • 总容量
  • 可用空间
  • 空闲空间
  • 已用空间
  • 使用率

磁盘I/O统计

  • API: 性能计数器 (LogicalDisk 对象)
  • 信息:
  • 磁盘使用时间
  • 读取时间/写入时间
  • 读取/写入字节数
  • 读取/写入次数
  • 当前队列长度

磁盘列表

  • APIGetLogicalDriveStrings()
  • 信息:
  • 驱动器类型
  • 文件系统类型 (NTFS, FAT32等)
  • 挂载选项

性能优化机制

1. 缓存机制

进程信息缓存

  1. typedefstruct{
  2. sigar_pid_t pid;
  3. int ppid;
  4. int priority;
  5. time_t mtime;// 最后更新时间
  6. sigar_uint64_t size;
  7. sigar_uint64_t resident;
  8. char name[SIGAR_PROC_NAME_LEN];
  9. char state;
  10. sigar_uint64_t handles;
  11. sigar_uint64_t threads;
  12. sigar_uint64_t page_faults;
  13. sigar_uint64_t bytes_read;
  14. sigar_uint64_t bytes_written;
  15. }sigar_win32_pinfo_t;

缓存有效期检查: SIGAR_LAST_PROC_EXPIRE(默认60秒)

网络接口缓存

  • netif_adapters: 网卡适配器信息缓存
  • netif_mib_rows: MIB行缓存
  • netif_addr_rows: IP地址行缓存
  • netif_names: 网卡名称映射缓存

2. 动态缓冲区管理

  1. // 缓冲区初始化
  2. static DWORD perfbuf_init(sigar_t*sigar){
  3. if(!sigar->perfbuf){
  4.         sigar->perfbuf = calloc(1, PERFBUF_SIZE);// 初始8KB
  5.         sigar->perfbuf_size = PERFBUF_SIZE;
  6. }
  7. return sigar->perfbuf_size;
  8. }
  9. // 缓冲区增长
  10. static DWORD perfbuf_grow(sigar_t*sigar){
  11.     sigar->perfbuf_size += PERFBUF_SIZE;// 每次增加8KB
  12.     sigar->perfbuf = realloc(sigar->perfbuf, sigar->perfbuf_size);
  13. return sigar->perfbuf_size;
  14. }

3. 延迟初始化

DLL模块按需初始化:

  1. #define DLLMOD_INIT(name, all) \
  2.     sigar_dllmod_init(sigar,(sigar_dll_module_t*)&(sigar->name), all)

参数 all 控制是否所有函数都必须成功加载。

4. 多级回退机制

对于关键功能,实现多级回退:

  1. CPU信息:
  • 优先: NtQuerySystemInformation
  • 备选: 性能计数器
  1. 进程列表:
  • 优先: EnumProcesses() (PSAPI)
  • 备选: 性能计数器
  1. 进程命令行:
  • 优先: PEB (快速)
  • 备选: WMI (慢速但兼容性好)

特殊技术

1. 时间转换

Windows FILETIME 转换为 Unix 时间戳:

  1. #define EPOCH_DELTA INT64_C(11644473600000000)
  2. sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft){
  3. sigar_uint64_t time;
  4.     time = ft->dwHighDateTime;
  5.     time = time <<32;
  6.     time |= ft->dwLowDateTime;
  7.     time /=10;// 100纳秒转为微秒
  8.     time -= EPOCH_DELTA;// 减去1601年到1970年的时间差
  9. return time;
  10. }

2. 权限提升

为了获取更多进程信息,需要提升调试权限:

  1. staticint sigar_enable_privilege(char*name){
  2.     HANDLE handle;
  3.     TOKEN_PRIVILEGES tok;
  4. if(!OpenProcessToken(GetCurrentProcess(),
  5.                           TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
  6. &handle)){
  7. returnGetLastError();
  8. }
  9. if(LookupPrivilegeValue(NULL, name,&tok.Privileges[0].Luid)){
  10.         tok.PrivilegeCount=1;
  11.         tok.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;
  12. if(AdjustTokenPrivileges(handle, FALSE,&tok,0, NULL,0)){
  13.             status = SIGAR_OK;
  14. }
  15. }
  16. CloseHandle(handle);
  17. return status;
  18. }
  19. // 在初始化时调用
  20. sigar_enable_privilege(SE_DEBUG_NAME);

3. 宽字符/ANSI字符转换

支持多语言环境:

  1. #define SIGAR_A2W(lpa, lpw, bytes) \
  2. (lpw[0]=0,MultiByteToWideChar(CP_ACP,0, \
  3.                                      lpa,-1, lpw,(bytes/sizeof(WCHAR))))
  4. #define SIGAR_W2A(lpw, lpa, chars) \
  5. (lpa[0]='\0',WideCharToMultiByte(CP_ACP,0, \
  6.                                         lpw,-1,(LPSTR)lpa, chars, \
  7.                                         NULL, NULL))

4. 网卡名称处理

针对不同网络接口类型的命名:

  1. if(strEQ((constchar*)ifr->bDescr, MS_LOOPBACK_ADAPTER)){
  2. // 微软回环适配器特殊处理
  3.     sprintf(name, NETIF_LA "%d", la++);
  4. }
  5. elseif(ifr->dwType == MIB_IF_TYPE_LOOPBACK){
  6. // 回环接口
  7. if(!sigar->netif_name_short){
  8.         status = sigar_net_interface_name_get(sigar, ifr, address_list, name);
  9. }
  10. if(status != SIGAR_OK){
  11.         sprintf(name,"lo%d", lo++);
  12. }
  13. }
  14. elseif((ifr->dwType == MIB_IF_TYPE_ETHERNET)||
  15. (ifr->dwType == IF_TYPE_IEEE80211)){
  16. // 以太网或无线网卡
  17. if(!sigar->netif_name_short &&
  18. (strstr((constchar*)ifr->bDescr,"Scheduler")== NULL)&&
  19. (strstr((constchar*)ifr->bDescr,"Filter")== NULL))
  20. {
  21.         status = sigar_net_interface_name_get(sigar, ifr, address_list, name);
  22. }
  23. if(status != SIGAR_OK){
  24. if(sigar->netif_name_short){
  25.             sprintf(name,"eth%d", eth++);
  26. }
  27. else{
  28.             snprintf(name, ifr->dwDescrLen,"%s", ifr->bDescr);
  29. }
  30. }
  31. }

5. CPU 核心合并

支持超线程处理器上的逻辑CPU合并:

  1. int core_rollup = sigar_cpu_core_rollup(sigar);
  2. for(i=0; i<num; i++){
  3. sigar_cpu_t*cpu;
  4. if(core_rollup &&(% sigar->lcpu)){
  5. // 合并同一物理核心的逻辑处理器
  6.         cpu =&cpulist->data[cpulist->number-1];
  7. }
  8. else{
  9.         SIGAR_CPU_LIST_GROW(cpulist);
  10.         cpu =&cpulist->data[cpulist->number++];
  11.         SIGAR_ZERO(cpu);
  12. }
  13. // 累加CPU时间
  14.     cpu->sys  += PERF_VAL_CPU(PERF_IX_CPU_SYS);
  15.     cpu->user += PERF_VAL_CPU(PERF_IX_CPU_USER);
  16.     cpu->irq  += PERF_VAL_CPU(PERF_IX_CPU_IRQ);
  17.     get_idle_cpu(sigar, cpu, i, counter_block, perf_offsets);
  18.     cpu->total = cpu->sys + cpu->user + cpu->idle + cpu->irq;
  19. }

兼容性处理

1. 编译器兼容

  1. #if !defined(MSVC) && defined(_MSC_VER)
  2. #define MSVC
  3. #endif
  4. #ifdef MSVC
  5. #define WIN32_LEAN_AND_MEAN
  6. #define snprintf _snprintf
  7. #if _MSC_VER <= 1200
  8. #define SIGAR_USING_MSC6  // Visual Studio 6
  9. #define HAVE_MIB_IPADDRROW_WTYPE 0
  10. #else
  11. #define HAVE_MIB_IPADDRROW_WTYPE 1
  12. #endif
  13. #endif

2. Windows 版本检测

  1. OSVERSIONINFO version;
  2. version.dwOSVersionInfoSize =sizeof(version);
  3. GetVersionEx(&version);
  4. // dwMajorVersion == 4: Windows NT 4.0
  5. // dwMajorVersion == 5: Windows 2000/XP/2003 Server
  6. // dwMajorVersion == 6: Windows Vista/7/8/10/11
  7. sigar->winnt =(version.dwMajorVersion ==4);

3. 结构体兼容性

对于旧版本的 Visual Studio,手动定义缺失的结构体:

  1. #ifdef SIGAR_USING_MSC6
  2. // MEMORYSTATUSEX - 在 VC6 中不存在
  3. typedefstruct{
  4.     DWORD dwLength;
  5.     DWORD dwMemoryLoad;
  6.     DWORDLONG ullTotalPhys;
  7.     DWORDLONG ullAvailPhys;
  8.     DWORDLONG ullTotalPageFile;
  9.     DWORDLONG ullAvailPageFile;
  10.     DWORDLONG ullTotalVirtual;
  11.     DWORDLONG ullAvailVirtual;
  12.     DWORDLONG ullAvailExtendedVirtual;
  13. } MEMORYSTATUSEX;
  14. // SERVICE_STATUS_PROCESS - 在 VC6 中不存在
  15. typedefstruct _SERVICE_STATUS_PROCESS {
  16.     DWORD dwServiceType;
  17.     DWORD dwCurrentState;
  18.     DWORD dwControlsAccepted;
  19.     DWORD dwWin32ExitCode;
  20.     DWORD dwServiceSpecificExitCode;
  21.     DWORD dwCheckPoint;
  22.     DWORD dwWaitHint;
  23.     DWORD dwProcessId;
  24.     DWORD dwServiceFlags;
  25. } SERVICE_STATUS_PROCESS;
  26. #endif/* _MSC_VER */

错误处理

1. Windows 错误码转换

  1. #define SIGAR_ENOENT ERROR_FILE_NOT_FOUND
  2. #define SIGAR_EACCES ERROR_ACCESS_DENIED
  3. #define SIGAR_ENXIO  ERROR_BAD_DRIVER_LEVEL
  4. // 自定义错误码
  5. #define SIGAR_NO_SUCH_PROCESS (SIGAR_OS_START_ERROR+1)

2. WMI 错误映射

  1. int wmi_map_sigar_error(HRESULT hres){
  2. switch(hres){
  3. case S_OK:
  4. return ERROR_SUCCESS;
  5. case WBEM_E_NOT_FOUND:
  6. return ERROR_NOT_FOUND;
  7. case WBEM_E_ACCESS_DENIED:
  8. return ERROR_ACCESS_DENIED;
  9. case WBEM_E_NOT_SUPPORTED:
  10. return SIGAR_ENOTIMPL;
  11. default:
  12. return ERROR_INVALID_FUNCTION;
  13. }
  14. }

总结

libsigar 的 Windows 分支实现展示了以下特点:

  1. 多层次API访问: 从高层 WMI 到底层 NT API 的完整覆盖
  2. 动态兼容性: 通过运行时检测和动态加载支持不同 Windows 版本
  3. 性能优化: 缓存机制、延迟初始化、缓冲区重用等优化策略
  4. 回退机制: 关键功能都有备选实现方案
  5. 跨平台抽象: 统一的 API 接口,隐藏平台特定细节

这种设计使 libsigar 能够在 Windows 平台上稳定、高效地收集系统信息,同时保持代码的可维护性和扩展性。

  • 公众号:安全狗的自我修养

  • vx:2207344074

  • http://gitee.com/haidragon

  • http://github.com/haidragon

  • bilibili:haidragonx

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 跨平台系统信息获取库libsigar源码分析系列(九)

猜你喜欢

  • 暂无文章