阅读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中的函数。

相关推荐
老猿讲编程21 分钟前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk1 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*1 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue1 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man1 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
萧鼎3 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸3 小时前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农3 小时前
Python 继承、多态、封装、抽象
开发语言·python
^velpro^3 小时前
数据库连接池的创建
java·开发语言·数据库
秋の花3 小时前
【JAVA基础】Java集合基础
java·开发语言·windows