天天看點

Snort源代碼編譯 _ 采坑之旅(for Windows VS2015)

    系統環境:Windows7 sp1 x64

    編譯環境:VS2015 SP1

    Snort版本:2.9.12

    daq版本:2.0.6

    Snort官網:https://www.snort.org/downloads#snort-downloads

背景

    最近需要實作一個類似網絡防火牆的産品,主要參考Snort源碼,先花了點時間簡單讀了下網上比較流行的《snort源碼分析》裡面的資料,然後對着源碼瞅了下,發現從snort2.9開始,snort應該是重構過了一輪,導緻網上的資料都是偏老的,前人的一些源碼分析都是基于2.9之前的版本的。我們的目标是先把源碼在Windows平台上面編譯通過,能調試!

注意事項

    0、開始之前……

    1、大家讀源碼的時候,如果發現網上的分析資料有些出入,應該就是重構導緻的,最大的重構就是daq被當成一個靜态lib剝離出來了,這塊下層跟wibpcap/libpcap互動,上層跟snort互動的一個中間層的,之前這塊的邏輯是跟snort本身糅合在一起的。

    2、Snort的源碼大量基于另外的第三方源碼,需要安裝cygwin(主要是頭檔案依賴和bison.exe等工具依賴)。cygwin固定安裝到C:\cygwin,并且手動把bison、flex、sed這三個開源庫安裝上(為啥裝這個三個開源庫也是參考網上了說法,實際使用中,目前隻看到使用了bison),另外就是安裝cygwin的時候如果在下載下傳階段有問題,可以使用163的鏡像http://mirrors.163.com/.help/cygwin.html。

    3、網上流傳最多的就是獨孤九賤的源碼分析,但是一般隻有7章,我這裡有個完整的版本(https://download.csdn.net/download/magictong/10859695),可以參考下,不過這個源碼分析是基于2.2版本的。

編譯采坑之旅

    注意:

    a、50%是解決與VS2015的相容性問題

    b、有些比較明顯的問題,就不提了,譬如netinet/in.h這種明顯是linux下面的頭檔案但是沒有包含在#ifndef WIN32裡面,譬如編譯選項沖突,譬如檔案缺失找不到等等

    c、編譯參考(注:這篇文章裡面是用VS2013編譯的,坑沒有VS2015多):https://blog.csdn.net/feixi7358/article/details/79817604

0、in6_addr以及相關定義沖突問題,這個是最大的坑。

原因:原因就是開源庫和微軟VS(in6addr.h)對于in6_addr定義是沖突,聯合體裡面有些字段的名字不一樣的,這個問題要解決還挺麻煩的,不過有個簡單的方法就是修改VS的公共頭檔案(解決方案裡面增加紅字部分),看起來有些不優雅,不過實際上沒有問題,因為是一個聯合體,并不會改變結構的記憶體結構。

解決方案:

//

// IPv6 Internet address (RFC 2553)

// This is an 'on-wire' format structure.

//

typedef struct in6_addr {

    union {

        UCHAR       Byte[16];

        USHORT      Word[8];

        UCHAR       u6_addr8[16];

        USHORT      u6_addr16[8];

        UINT            u6_addr32[4];

    } u;

} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR;

1、fatal error C1189: #error: Macro definition of snprintf conflicts with Standard Library function declaration

原因:有很多的開源庫或者程式将 snprintf 函數定義為 _snprintf,而微軟在VS2015出現之前并不支援_snprintf,然而,微軟從VS2015開始定義 snprintf,當然微軟也想到了這個問題,是以給出了這麼一個還算是人性化的錯誤提示。

Reference:https://blog.csdn.net/A1367297309/article/details/52997312

解決方案:

#if defined(_MSC_VER) && _MSC_VER < 1900

#define snprintf _snprintf

#endif

2、reload_api.h打不開(#include "reload_api.h")

原因:MSBuildTool拷貝時遺漏了檔案。

解決方案:找到reload_api.h拷貝到snort-2.9.12\src\dynamic-preprocessors\include下即可。

3、ip6_hops重定義

原因:定義多份,而且含義不一樣,實際上ip6_hops從來沒用過。

解決方案:可以注釋掉

// #define ip6_hops         hop_limit

// #define ip6_hops  ip6_ctlun.ip6_un1.ip6_un1_hlim

4、Warning太多,建議屏蔽部分

#ifdef WIN32

#pragma warning(disable: 4996 4293 4334 4018 4244)

#endif

5、下面這個問題比較明顯,提示%s和後面的dlerror()的傳回值不比對。

#ifdef WIN32

        fprintf(stderr, "%s: %s: %u\n", filename, dlsym_func_name, dlerror());

#else

        fprintf(stderr, "%s: %s: %s\n", filename, dlsym_func_name, dlerror());

#endif

6、關掉SafeSEH:No (/SAFESEH:NO)

7、将\snort-2.9.11.1\src\中的 reload.c/.h 和 pkt_tracer.c/.h 添加到snort項目中。

8、拷貝:reg_test.h到snort-2.9.12\src\dynamic-preprocessors\include

9、拷貝:preprocids.h到snort-2.9.12\src\dynamic-plugins\sf_engine

10、改造:max提示錯誤

原因:Windows下宏沖突

解決方案:

#ifndef WIN32

static uint32_t max(uint32_t a, uint32_t b)

{

    if (a >= b)

        return a;

    return b;

}

#endif // !WIN32

11、dnet.lib裡面提示無法解析外部符号__imp___snprintf,_sprintf等(VS2015的深坑)

2>dnet.lib(intf-win32.obj) : error LNK2019: unresolved external symbol __imp___snprintf referenced in function __ifrow_to_entry

2>dnet.lib(addr-util.obj) : warning LNK4217: locally defined symbol _sprintf imported in function _ip6_ntop

2>dnet.lib(addr.obj) : warning LNK4049: locally defined symbol _sprintf imported

Reference:https://stackoverflow.com/questions/32418766/c-unresolved-external-symbol-sprintf-and-sscanf-in-visual-studio-2015#comment52704958_32418900

解決方案:

#ifdef WIN32

#if defined(_MSC_VER) && _MSC_VER >= 1900

#pragma comment(lib, "legacy_stdio_definitions.lib")

#endif

#endif

12、VS2015再踩一坑:無法識别的外部符号 __imp__iob,__imp__pctype,__imp___mb_cur_max

Reference:

https://blog.csdn.net/travis_bacon/article/details/80017844

https://stackoverflow.com/questions/31546519/visual-studio-15-imp-iob-imp-pctype-imp-mb-cur-max

解決方案:

#ifdef WIN32

#if defined(_MSC_VER) && _MSC_VER >= 1900

#pragma comment(lib, "libmsvcrt.a")

#endif

#endif

上面的這個方法随便編譯連結成功,但是有一堆的問題,後面是用下面的方法曲線救國:

#undef __mb_cur_max

int __cdecl __mb_cur_max(void)

{

    return ___mb_cur_max_func();

}

#undef _pctype

const unsigned short* __cdecl _pctype(void)

{

    return __pctype_func();

}

13、又來一個:unresolved external symbol __imp__pthread_mutex_unlock

Reference:

https://stackoverflow.com/questions/13803795/pthread-mutex-error-lnk2019-unresolved-external-symbol-c

https://www.sourceware.org/pthreads-win32/

解決方案:pthreadVC2.lib pthreadVC2.dll

//

// VS2015之後很多stdio裡面函數改成了inline連結

// Add by Magictong 2018/12/18 15:43:52

// 

#ifdef WIN32

#if defined(_MSC_VER) && _MSC_VER >= 1900

#pragma comment(lib, "legacy_stdio_definitions.lib")

#pragma comment(lib, "libmsvcrt.a")

#pragma comment(lib, "pthreadVC2.lib")

#endif

#endif

//

Tips

    另外,讀源碼的時候,根據我的個人經驗[酷]

    1、在不能調試的情況下,看開源代碼的時候,不要從開頭什麼類似main函數看下去,否則很可能被淹沒在龐大的代碼裡面(其實調試也一樣,如果沒有找到關鍵點,調試也是抓瞎)。

    2、對于我們感興趣的功能點,直接針對的性的去看,帶着目标去看,把相關功能的接口理清晰,搞明白。然後再設定下一個小目标,反複進行。

    3、有些中間的結論,及時的畫一畫,寫下來,否則很容易忘記。很多開源代碼寫得都很天馬行空。