1. 程式人生 > >記錄專案中所學到的問題,以及自學記錄

記錄專案中所學到的問題,以及自學記錄

在看hostapd原始碼的過程中,看到接收過濾使用的bpf,下面拷貝hostapd原始碼:

if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
               &msock_filter, sizeof(msock_filter))) {
        perror("SO_ATTACH_FILTER");
        return -1;
}

static struct sock_fprog msock_filter = {
    .len = ARRAY_SIZE(msock_filter_insns),
    .filter = msock_filter_insns,
};

// msock_filter_insns過長,這裡只拷貝一部分
static struct sock_filter msock_filter_insns[] = { /* * do a little-endian load of the radiotap length field */ /* load lower byte into A */ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), /* put it into X (== index register) */ BPF_STMT(BPF_MISC| BPF_TAX, 0), /* load upper byte into A */
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 3), /* left-shift it by 8 */ BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8), ..... }

下面進行分析:

使用方式:
設定BPF過濾器是通過setsockopt呼叫來完成的,格式如下:

    setsockopt(sd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));

這個呼叫的格式大家都很熟悉了,不清楚的在引數Filter的設定上。Filter的定義是struct sock_fprog Filter; 此結構在linux/filter.h當中有定義:

struct sock_fprog      
/*Required for SO_ATTACH_FILTER. */
{
    unsigned short  len;   
    /*Number of filter blocks */
    struct sock_filter   *filter;
};

其中的filter指標指向結構為struct sock_filter的BPF過濾程式碼。結構同樣也在同一個檔案當中定義:

struct sock_filter     
/*
 Filter block */
{
            __u16  code;   /*Actual filter code */
            __u8     jt;   /*Jump true */
            __u8     jf;   /*Jump false */
            __u32    k;    /*Generic multiuse field */
};

這個類似於彙編的樣子。

msock_filter_insns結構體裡面的資料,可以看 libpcap裡面原始碼:

#define BPF_STMT(code, k) { (u_short)(code), 0, 0, k }
#define BPF_JUMP(code, k, jt, jf) { (u_short)(code), jt, jf, k }


/* instruction classes */
#define BPF_CLASS(code) ((code) & 0x07)
#define     BPF_LD      0x00
#define     BPF_LDX     0x01
#define     BPF_ST      0x02
#define     BPF_STX     0x03
#define     BPF_ALU     0x04
#define     BPF_JMP     0x05
#define     BPF_RET     0x06
#define     BPF_MISC    0x07

/* ld/ldx fields */
#define BPF_SIZE(code)  ((code) & 0x18)
#define     BPF_W       0x00
#define     BPF_H       0x08
#define     BPF_B       0x10
/*              0x18    reserved; used by BSD/OS */
#define BPF_MODE(code)  ((code) & 0xe0)
#define     BPF_IMM     0x00
#define     BPF_ABS     0x20
#define     BPF_IND     0x40
#define     BPF_MEM     0x60
#define     BPF_LEN     0x80
#define     BPF_MSH     0xa0
/*              0xc0    reserved; used by BSD/OS */
/*              0xe0    reserved; used by BSD/OS */

/* alu/jmp fields */
#define BPF_OP(code)    ((code) & 0xf0)
#define     BPF_ADD     0x00
#define     BPF_SUB     0x10
#define     BPF_MUL     0x20
#define     BPF_DIV     0x30
#define     BPF_OR      0x40
#define     BPF_AND     0x50
#define     BPF_LSH     0x60
#define     BPF_RSH     0x70
#define     BPF_NEG     0x80
#define     BPF_MOD     0x90
#define     BPF_XOR     0xa0
#define     BPF_JA      0x00
#define     BPF_JEQ     0x10
#define     BPF_JGT     0x20
#define     BPF_JGE     0x30
#define     BPF_JSET    0x40

#define BPF_SRC(code)   ((code) & 0x08)
#define     BPF_K       0x00
#define     BPF_X       0x08

/* ret - BPF_K and BPF_X also apply */
#define BPF_RVAL(code)  ((code) & 0x18)
#define     BPF_A       0x10
/*              0x18    reserved */

/* misc */
#define BPF_MISCOP(code) ((code) & 0xf8)
#define     BPF_TAX     0x00
#define     BPF_TXA     0x80
//這個就是bpf instruction的縮寫了  
struct bpf_insn {  
        u_short code;  
        u_char  jt;  
        u_char  jf;  
        u_long k;  
};  
BPF_LD  //將值拷貝進暫存器(accumulator),沒學過彙編。我就當做賦給一個變量了  
BPF_LDX //將值拷貝進索引暫存器(index register)  
BPF_LD+BPF_W+BPF_ABS    A <- P[k:4]     //將一個Word 即4 byte賦給暫存器(accumulator)  
BPF_LD+BPF_H+BPF_ABS    A <- P[k:2]     //將一個Half Word 即2 byte賦給暫存器(accumulator)  
BPF_LD+BPF_B+BPF_ABS    A <- P[k:1]     //將一個Byte 賦給暫存器(accumulator)  
BPF_LD+BPF_W+BPF_IND    A <- P[X+k:4]   //偏移X暫存器後,將一個Word 即4 byte賦給暫存器(accumulator)  
BPF_LD+BPF_H+BPF_IND    A <- P[X+k:2]  
BPF_LD+BPF_B+BPF_IND    A <- P[X+k:1]  
BPF_LD+BPF_W+BPF_LEN    A <- len        //the packet length 不知道什麼意思 :(  
BPF_LD+BPF_IMM          A <- k          //將常量k賦給暫存器(accumulator)  
BPF_LD+BPF_MEM          A <- M[k]       //將一個Word的地址為k的記憶體部分賦給暫存器(accumulator)  
//下面的部分是將值load進index register 大家自己理解吧  
BPF_LDX+BPF_W+BPF_IMM   X <- k  
BPF_LDX+BPF_W+BPF_MEM   X <- M[k]  
BPF_LDX+BPF_W+BPF_LEN   X <- len  
BPF_LDX+BPF_B+BPF_MSH   X <- 4*(P[k:1]&0xf)  
//來看看兩個巨集  
#define BPF_STMT(code, k) { (unsigned short)(code), 0, 0, k }  
#define BPF_JUMP(code, k, jt, jf) { (unsigned short)(code), jt, jf, k }  
//關鍵的判斷指令忘貼了  
BPF_JMP+BPF_JA          pc += k  
BPF_JMP+BPF_JGT+BPF_K   pc += (A > k) ? jt : jf  
BPF_JMP+BPF_JGE+BPF_K   pc += (A >= k) ? jt : jf  
/* 
 *這個BPF_JEQ用的比較多,就拿它開刀了。 一看就知道,是用來判斷是否相等的東西 
 *這個會判斷我們給出的數,和A(也就是accumulator暫存器)的內容是否相等, 
 *結果就不用我說了,三目運算子。 
 */  
BPF_JMP+BPF_JEQ+BPF_K   pc += (A == k) ? jt : jf  
BPF_JMP+BPF_JSET+BPF_K  pc += (A & k) ? jt : jf  
BPF_JMP+BPF_JGT+BPF_X   pc += (A > X) ? jt : jf  
BPF_JMP+BPF_JGE+BPF_X   pc += (A >= X) ? jt : jf  
BPF_JMP+BPF_JEQ+BPF_X   pc += (A == X) ? jt : jf  
BPF_JMP+BPF_JSET+BPF_X  pc += (A & X) ? jt : jf  
//返回指令  
BPF_RET+BPF_A           //接受 A 暫存器中的數量bytes  
BPF_RET+BPF_K           //接受常量 k bytes  
//下面我們就直接看個例項吧  
/* 前提條件,這個filter的前提是我們抓的包是將物理層都抓下來的情況下。 
 * 不懂的 物理頭-IP頭-TCP/UDP頭 的兄弟們結合下面的圖吧,方便理解與記憶。 
 * 這是一個過濾TCP源埠不為79,目的埠為79的包(TCP Finger) 
 */  
struct bpf_insn insns[] = {  
    /*物理頭,偏移12byte後,指向type*/     
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),  
    /*進行比較,是否為IP協議。 true的話 0,  false 10 
     *這裡說一下了,由於本人沒學過彙編,對這玩意開始時相當困惑。對於學過彙編的兄弟,應該是小菜吧。 
     *true,則跳過0條指令執行。 就是繼續執行下面的一條指令 
     *false,則跳過10條指令執行。 數數!結果就數到最後一條。即BPF_STMT(BPF_RET+BPF_K, 0),就返回了 
     *下面的照這個慢慢分析就OK了。 
     */  
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 10),  
    BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 23),  
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_TCP, 0, 8),  
    BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 20),  
    BPF_JUMP(BPF_JMP+BPF_JSET+BPF_K, 0x1fff, 6, 0),  
    BPF_STMT(BPF_LDX+BPF_B+BPF_MSH, 14),  
    BPF_STMT(BPF_LD+BPF_H+BPF_IND, 14),  
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 2, 0),  
    BPF_STMT(BPF_LD+BPF_H+BPF_IND, 16),  
    BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 79, 0, 1),  
    BPF_STMT(BPF_RET+BPF_K, (u_int)-1),  
    BPF_STMT(BPF_RET+BPF_K, 0),  
};  

看到上面的東西,那麼我們不用考慮這麼多 ,可以用就行了。
我們可以直接使用tcpdump直接得到這樣的一組類彙編程式碼

例如 :

$tcpdump ip -d -s 2048 host 192.168.1.2
(000) ldh      [12]
(001) jeq      #0x800           jt 2 jf 7
(002) ld       [26]
(003) jeq      #0xc0a80102      jt 6 jf 4
(004) ld       [30]
(005) jeq      #0xc0a80102      jt 6 jf 7
(006) ret      #2048
(007) ret      #0

$tcpdump ip -dd -s 2048 host 192.168.1.2
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 5, 0x00000800 },
{ 0x20, 0, 0, 0x0000001a },
{ 0x15, 2, 0, 0xc0a80102 },
{ 0x20, 0, 0, 0x0000001e },
{ 0x15, 0, 1, 0xc0a80102 },
{ 0x6, 0, 0, 0x00000800 },
{ 0x6, 0, 0, 0x00000000 },

直接拷貝這樣的資料到sock_filter裡面

struct sock_filter bpf_code[] = {
      { 0x28, 0, 0, 0x0000000c },
      { 0x15, 0, 5, 0x00000800 },
      { 0x20, 0, 0, 0x0000001a },
      { 0x15, 2, 0, 0xc0a80102 },
      { 0x20, 0, 0, 0x0000001e },
      { 0x15, 0, 1, 0xc0a80102 },
      { 0x6, 0, 0, 0x00000800 },
      { 0x6, 0, 0, 0x00000000 }
};

然後直接這樣:

struct sock_fprog filter;
filter.len = sizeof(bpf_code)/sizeof(bpf_code[0]);
filter.filter = bpf_code;
setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));