阅读go语言工具源码系列之gopacket(谷歌出品)----第一集 DLL的go封装

gopacket项目是google出品的golang第三方库,项目源码地址google/gopacket: Provides packet processing capabilities for Go (github.com)

gopacket核心是对经典的抓包工具libpcap(linux平台)和npcap(windows平台)的go封装,提供了更方便的go语言操作接口,里面如何实现的,接下来的文章中会有介绍。

windows平台和linux平台的go封装有些不一样

我们先从windows平台讲起吧(笔者常用操作系统为windows系统)

第一集 DLL的go封装

windows系统中使用的抓包工具是npcap,请提前到Npcap: Windows Packet Capture Library & Driver下载安装,安装完成后可在安装文件夹中看到

其中wpcap.dll是本集中所要绑定的dll库

DLL(Dynamic Link Library)文件为动态链接库文件,又称"应用程序拓展",是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。

在golang中使用syscall库来进行调用底层操作系统 API 的包。gopacket中采用的方式就是使用syscall来调用DLL文件。

shell 复制代码
├─afpacket          
├─bsdbpf            
├─bytediff
├─defrag
│  └─lcmdefrag      
├─dumpcommand
├─examples
│  ├─bidirectional
│  ├─bytediff
│  ├─httpassembly
│  ├─pcapdump
│  ├─pcaplay
│  ├─pfdump
│  ├─reassemblydump
│  ├─snoopread
│  ├─statsassembly
│  ├─synscan
│  └─util
├─ip4defrag
├─layers
│  └─testdata
│      └─fuzz
│          └─FuzzDecodeFromBytes
├─macs
├─pcap
│  └─gopacket_benchmark
├─pcapgo
│  └─tests
│      ├─be
│      └─le
├─pfring
├─reassembly
├─routing
└─tcpassembly
    └─tcpreader

在项目文件中pcap -> pcap_windows.go中即是对wpcap.dll的go封装代码

我们来看一下里面的构造

不知道大家看golang工具源码的时候是怎么一个顺序,个人比较喜欢按照执行顺序来先了解大致要干啥的逻辑,所以首先我们看一下init函数:

go 复制代码
// init函数是每个文件首先执行的,甚至于在main.go 中也会早于main函数执行
func init() {  
	LoadWinPCAP()  
}

这个函数其实点明了本文件的主旨LoadWinPCAP导入winpcap。

按照执行顺序执行到了LoadWinPCAP()函数

go 复制代码
// LoadWinPCAP attempts to dynamically load the wpcap DLL and resolve necessary functions// 动态导入wpcap.dll库  
func LoadWinPCAP() error {  
// 首先通过pcapLoaded变量来判断winpcap是否导入过,pcapLoaded变量初始化时为bool  
if pcapLoaded {  
return nil  
}  
// syscall.LoadLibrary 来导入kernel32.dll  
kernel32, err := syscall.LoadLibrary("kernel32.dll")  
if err != nil {  
return fmt.Errorf("couldn't load kernel32.dll")  
}  
//延迟释放kernel32.dll  
defer syscall.FreeLibrary(kernel32)  
  
//设置路径为npcap所在路径  
initDllPath(kernel32)  
// 使用syscall.GetProcAddress来获取kernel32中的AddDllDirectory函数  
if haveSearch, _ := syscall.GetProcAddress(kernel32, "AddDllDirectory"); haveSearch != 0 {  
// 如果存在 AddDllDirectory,我们可以将 LOAD_LIBRARY_* 的东西与 LoadLibraryEx 一起使用,以避免 wpcap .dll劫持  
// if AddDllDirectory is present, we can use LOAD_LIBRARY_* stuff with LoadLibraryEx to avoid wpcap.dll hijacking  
// see: https://msdn.microsoft.com/en-us/library/ff919712%28VS.85%29.aspx  
const LOAD_LIBRARY_SEARCH_USER_DIRS = 0x00000400  
const LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800  
wpcapHandle, err = windows.LoadLibraryEx("wpcap.dll", 0, LOAD_LIBRARY_SEARCH_USER_DIRS|LOAD_LIBRARY_SEARCH_SYSTEM32)  
if err != nil {  
return fmt.Errorf("couldn't load wpcap.dll")  
}  
} else {  
// otherwise fall back to load it with the unsafe search cause by SetDllDirectory  
// 否则回退以使用 SetDllDirectory 导致的不安全搜索加载它  
wpcapHandle, err = windows.LoadLibrary("wpcap.dll")  
if err != nil {  
return fmt.Errorf("couldn't load wpcap.dll")  
}  
}  
initLoadedDllPath(kernel32)  
// 导入 msvcrt 动态库  
msvcrtHandle, err = syscall.LoadLibrary("msvcrt.dll")  
if err != nil {  
return fmt.Errorf("couldn't load msvcrt.dll")  
}  
// 引入calloc函数  
callocPtr, err = syscall.GetProcAddress(msvcrtHandle, "calloc")  
if err != nil {  
return fmt.Errorf("couldn't get calloc function")  
}  

// 将wpcap库函数进行绑定
// It returns an error message string corresponding to error.  
pcapStrerrorPtr = mustLoad("pcap_strerror")  
// get a string for an error or warning status code  
pcapStatustostrPtr = mightLoad("pcap_statustostr") // not available on winpcap  
// get a handle for a live capture  
pcapOpenLivePtr = mustLoad("pcap_open_live")  
pcapOpenOfflinePtr = mustLoad("pcap_open_offline")  
pcapClosePtr = mustLoad("pcap_close")  
pcapGeterrPtr = mustLoad("pcap_geterr")  
pcapStatsPtr = mustLoad("pcap_stats")  
pcapCompilePtr = mustLoad("pcap_compile")  
pcapFreecodePtr = mustLoad("pcap_freecode")  
pcapLookupnetPtr = mustLoad("pcap_lookupnet")  
pcapOfflineFilterPtr = mustLoad("pcap_offline_filter")  
pcapSetfilterPtr = mustLoad("pcap_setfilter")  
pcapListDatalinksPtr = mustLoad("pcap_list_datalinks")  
pcapFreeDatalinksPtr = mustLoad("pcap_free_datalinks")  
pcapDatalinkValToNamePtr = mustLoad("pcap_datalink_val_to_name")  
pcapDatalinkValToDescriptionPtr = mustLoad("pcap_datalink_val_to_description")  
pcapOpenDeadPtr = mustLoad("pcap_open_dead")  
pcapNextExPtr = mustLoad("pcap_next_ex")  
pcapDatalinkPtr = mustLoad("pcap_datalink")  
pcapSetDatalinkPtr = mustLoad("pcap_set_datalink")  
pcapDatalinkNameToValPtr = mustLoad("pcap_datalink_name_to_val")  
pcapLibVersionPtr = mustLoad("pcap_lib_version")  
pcapFreealldevsPtr = mustLoad("pcap_freealldevs")  
pcapFindalldevsPtr = mustLoad("pcap_findalldevs")  
pcapSendpacketPtr = mustLoad("pcap_sendpacket")  
pcapSetdirectionPtr = mustLoad("pcap_setdirection")  
pcapSnapshotPtr = mustLoad("pcap_snapshot")  
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions  
pcapTstampTypeValToNamePtr = mightLoad("pcap_tstamp_type_val_to_name")  
pcapTstampTypeNameToValPtr = mightLoad("pcap_tstamp_type_name_to_val")  
pcapListTstampTypesPtr = mightLoad("pcap_list_tstamp_types")  
pcapFreeTstampTypesPtr = mightLoad("pcap_free_tstamp_types")  
pcapSetTstampTypePtr = mightLoad("pcap_set_tstamp_type")  
pcapGetTstampPrecisionPtr = mightLoad("pcap_get_tstamp_precision")  
pcapSetTstampPrecisionPtr = mightLoad("pcap_set_tstamp_precision")  
pcapOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_open_offline_with_tstamp_precision")  
pcapHOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_hopen_offline_with_tstamp_precision")  
pcapActivatePtr = mustLoad("pcap_activate")  
pcapCreatePtr = mustLoad("pcap_create")  
pcapSetSnaplenPtr = mustLoad("pcap_set_snaplen")  
pcapSetPromiscPtr = mustLoad("pcap_set_promisc")  
pcapSetTimeoutPtr = mustLoad("pcap_set_timeout")  
//winpcap does not support rfmon  
pcapCanSetRfmonPtr = mightLoad("pcap_can_set_rfmon")  
pcapSetRfmonPtr = mightLoad("pcap_set_rfmon")  
pcapSetBufferSizePtr = mustLoad("pcap_set_buffer_size")  
//libpcap <1.5 does not have pcap_set_immediate_mode  
pcapSetImmediateModePtr = mightLoad("pcap_set_immediate_mode")  
pcapHopenOfflinePtr = mustLoad("pcap_hopen_offline")  
  
pcapLoaded = true  
return nil  
}

这一段代码是将wpcap代码进行进行绑定的关键

首先导入kernel.dll库

go 复制代码
kernel32, err := syscall.LoadLibrary("kernel32.dll")  

然后从kernel.dll中调用AddDllDirectory方法,并以此为判断是使用LoadLibraryEx函数还是LoadLibrary函数来进行wpcap.dll调用

LoadLibraryEx函数相比于LoadLibrary函数多了一个LOAD_LIBRARY_* 标识,来防止dll劫持攻击。

导入wpcap.dll库

go 复制代码
wpcapHandle, err = windows.LoadLibraryEx("wpcap.dll", 0,LOAD_LIBRARY_SEARCH_USER_DIRS|LOAD_LIBRARY_SEARCH_SYSTEM32)  

导入了wpcap.dll库后,然后将该动态库的函数都进行了绑定,在文件中它封装了两个load函数如下:

go 复制代码
// 必须导入
func mustLoad(fun string) uintptr {  
addr, err := windows.GetProcAddress(wpcapHandle, fun)  
if err != nil {  
panic(fmt.Sprintf("Couldn't load function %s from %s", fun, loadedDllPath))  
}  
return addr  
}  
// 可能导入  
func mightLoad(fun string) uintptr {  
addr, err := windows.GetProcAddress(wpcapHandle, fun)  
if err != nil {  
return 0  
}  
return addr  
}

它导入的函数有以下几种

go 复制代码
// It returns an error message string corresponding to error.  
pcapStrerrorPtr = mustLoad("pcap_strerror")  
// get a string for an error or warning status code  
pcapStatustostrPtr = mightLoad("pcap_statustostr") // not available on winpcap  
// get a handle for a live capture  
pcapOpenLivePtr = mustLoad("pcap_open_live")  
//  
pcapOpenOfflinePtr = mustLoad("pcap_open_offline")  
pcapClosePtr = mustLoad("pcap_close")  
pcapGeterrPtr = mustLoad("pcap_geterr")  
pcapStatsPtr = mustLoad("pcap_stats")  
pcapCompilePtr = mustLoad("pcap_compile")  
pcapFreecodePtr = mustLoad("pcap_freecode")  
pcapLookupnetPtr = mustLoad("pcap_lookupnet")  
pcapOfflineFilterPtr = mustLoad("pcap_offline_filter")  
pcapSetfilterPtr = mustLoad("pcap_setfilter")  
pcapListDatalinksPtr = mustLoad("pcap_list_datalinks")  
pcapFreeDatalinksPtr = mustLoad("pcap_free_datalinks")  
pcapDatalinkValToNamePtr = mustLoad("pcap_datalink_val_to_name")  
pcapDatalinkValToDescriptionPtr = mustLoad("pcap_datalink_val_to_description")  
pcapOpenDeadPtr = mustLoad("pcap_open_dead")  
pcapNextExPtr = mustLoad("pcap_next_ex")  
pcapDatalinkPtr = mustLoad("pcap_datalink")  
pcapSetDatalinkPtr = mustLoad("pcap_set_datalink")  
pcapDatalinkNameToValPtr = mustLoad("pcap_datalink_name_to_val")  
pcapLibVersionPtr = mustLoad("pcap_lib_version")  
pcapFreealldevsPtr = mustLoad("pcap_freealldevs")  
pcapFindalldevsPtr = mustLoad("pcap_findalldevs")  
pcapSendpacketPtr = mustLoad("pcap_sendpacket")  
pcapSetdirectionPtr = mustLoad("pcap_setdirection")  
pcapSnapshotPtr = mustLoad("pcap_snapshot")  
//libpcap <1.2 doesn't have pcap_*_tstamp_* functions  
pcapTstampTypeValToNamePtr = mightLoad("pcap_tstamp_type_val_to_name")  
pcapTstampTypeNameToValPtr = mightLoad("pcap_tstamp_type_name_to_val")  
pcapListTstampTypesPtr = mightLoad("pcap_list_tstamp_types")  
pcapFreeTstampTypesPtr = mightLoad("pcap_free_tstamp_types")  
pcapSetTstampTypePtr = mightLoad("pcap_set_tstamp_type")  
pcapGetTstampPrecisionPtr = mightLoad("pcap_get_tstamp_precision")  
pcapSetTstampPrecisionPtr = mightLoad("pcap_set_tstamp_precision")  
pcapOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_open_offline_with_tstamp_precision")  
pcapHOpenOfflineWithTstampPrecisionPtr = mightLoad("pcap_hopen_offline_with_tstamp_precision")  
pcapActivatePtr = mustLoad("pcap_activate")  
pcapCreatePtr = mustLoad("pcap_create")  
pcapSetSnaplenPtr = mustLoad("pcap_set_snaplen")  
pcapSetPromiscPtr = mustLoad("pcap_set_promisc")  
pcapSetTimeoutPtr = mustLoad("pcap_set_timeout")  
//winpcap does not support rfmon  
pcapCanSetRfmonPtr = mightLoad("pcap_can_set_rfmon")  
pcapSetRfmonPtr = mightLoad("pcap_set_rfmon")  
pcapSetBufferSizePtr = mustLoad("pcap_set_buffer_size")  
//libpcap <1.5 does not have pcap_set_immediate_mode  
pcapSetImmediateModePtr = mightLoad("pcap_set_immediate_mode")  
pcapHopenOfflinePtr = mustLoad("pcap_hopen_offline")  
//绑定后将pcapLoaded改为true  
pcapLoaded = true  
return nil

整体大致流程:

使用syscall.LoadLibrary先导入kernel.dll库,然后在使用kernel的AddDllDirectory函数做1次判断,然后导入wpcap.dll库并绑定wpcap库函数到go的uintptr变量中,方便下一步的调用。

本集总结:

本集主要介绍了gopacket中对于wpcap.dll这个windows动态链接库进行绑定的方法,使用到了go语言的syscall和golang.org/x/sys/windows两个针对底层系统调用的基础库,在进行绑定的时候首先需要使用syscall.LoadLibrary导入dll,然后使用windows.GetProcAddress获取dll中的函数。

相关推荐
灵感菇_13 分钟前
Java 锁机制全面解析
java·开发语言
wazmlp00188736925 分钟前
python第三次作业
开发语言·python
娇娇乔木26 分钟前
模块十一--接口/抽象方法/多态--尚硅谷Javase笔记总结
java·开发语言
明月醉窗台39 分钟前
qt使用笔记六之 Qt Creator、Qt Widgets、Qt Quick 详细解析
开发语言·笔记·qt
逍遥德39 分钟前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
wangjialelele42 分钟前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
lili-felicity1 小时前
CANN性能调优与实战问题排查:从基础优化到排障工具落地
开发语言·人工智能
独自破碎E1 小时前
【BISHI15】小红的夹吃棋
android·java·开发语言
进阶小白猿1 小时前
Java技术八股学习Day33
java·开发语言·学习
MX_93591 小时前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring