記錄專案中所學到的問題,以及自學記錄
阿新 • • 發佈:2019-01-28
在看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));