<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Linux內(nèi)核調(diào)試?yán)鳎黭probe 原理與實(shí)現(xiàn)

          共 17748字,需瀏覽 36分鐘

           ·

          2022-06-17 15:35

          在《Linux 內(nèi)核調(diào)試?yán)?| kprobe 的使用》一文中,我們介紹過怎么使用 kprobe 來追蹤內(nèi)核函數(shù),而本文將會(huì)介紹 kprobe 的原理和實(shí)現(xiàn)

          kprobe 原理

          kprobe 可以用來跟蹤內(nèi)核函數(shù)中某一條指令在運(yùn)行前和運(yùn)行后的情況。

          我們只需在 kprobe 模塊中定義好指令執(zhí)行前的回調(diào)函數(shù) pre_handler() 和執(zhí)行后的回調(diào)函數(shù) post_handler(),那么內(nèi)核將會(huì)在被跟蹤的指令執(zhí)行前調(diào)用 pre_handler() 函數(shù),并且在指令執(zhí)行后調(diào)用 post_handler() 函數(shù)。如下圖所示:

          (圖1)

          那么,內(nèi)核是怎樣做到在被跟蹤指令執(zhí)行前調(diào)用 pre_handler() 函數(shù)和指令執(zhí)行后調(diào)用 post_handler() 函數(shù)的呢?

          如果你讀過我們之前寫的一篇文章《斷點(diǎn)的原理》,那么就比較容易理解 kprobe 的原理了,因?yàn)?kprobe 使用了類似于斷點(diǎn)的機(jī)制來實(shí)現(xiàn)的。

          如果不了解斷點(diǎn)的原理,那么請先看看這篇文章《斷點(diǎn)的原理》。

          當(dāng)使用 kprobe 來跟蹤內(nèi)核函數(shù)的某條指令時(shí),kprobe 首先會(huì)把要追蹤的指令保存起來,然后把要追蹤的指令替換成 int3 指令。如下圖所示:


          (圖2)

          被追蹤的指令替換成 int3 指令后,當(dāng)內(nèi)核執(zhí)行到這條指令時(shí),將會(huì)觸發(fā) do_int3() 異常處理例程。

          do_int3() 異常處理例程的執(zhí)行過程如下:

          1. 首先調(diào)用 kprobe 模塊的 pre_handler() 回調(diào)函數(shù)。
          2. 然后將 CPU 設(shè)置為單步調(diào)試模式。
          3. 接著從異常處理例程中返回,并且執(zhí)行原來的指令。

          我們通過下圖來展示 do_int3() 函數(shù)的執(zhí)行過程:

          (圖3)

          由于設(shè)置了單步調(diào)試模式,當(dāng)執(zhí)行完原來的指令后,將會(huì)觸發(fā) debug異常(這是 Intel x86 CPU 的一個(gè)特性)。

          當(dāng) CPU 觸發(fā) debug異常 后,內(nèi)核將會(huì)執(zhí)行 debug 異常處理例程 do_debug(),而 do_debug() 異常處理例程將會(huì)調(diào)用 kprobe 模塊的 post_handler() 回調(diào)函數(shù)。

          下圖展示了 kprobe 的執(zhí)行流程:

          (圖4)

          kprobe 實(shí)現(xiàn)

          了解了 kprobe 的原理后,現(xiàn)在我們開始分析 kprobe 的代碼實(shí)現(xiàn)。

          由于 kprobe 的細(xì)節(jié)很多,本文只會(huì)對(duì) kprobe 整個(gè)大體實(shí)現(xiàn)方式進(jìn)行分析,有些細(xì)節(jié)需要讀者自行閱讀源碼了解。

          1. kprobe 初始化

          一個(gè)功能的實(shí)現(xiàn),一般都需要先初始化其所使用的資源和環(huán)境,kprobe 功能也不例外。

          下面我們來看看 kprobe 的初始化過程,kprobe 的初始化由 init_kprobes() 函數(shù)實(shí)現(xiàn):

          static int __init init_kprobes(void)
          {
              int i, err = 0;
              unsigned long offset = 0, size = 0;
              char *modname, namebuf[128];
              const char *symbol_name;
              void *addr;
              struct kprobe_blackpoint *kb;

              // 1) 初始化用于存儲(chǔ) kprobe 模塊的哈希表
              for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
                  INIT_HLIST_HEAD(&kprobe_table[i]);
                  ...
              }

              // 2) 初始化 kprobe 的黑名單函數(shù)列表(不能被 kprobe 跟蹤的函數(shù)列表)
              for (kb = kprobe_blacklist; kb->name != NULL; kb++) {
                  kprobe_lookup_name(kb->name, addr);
                  if (!addr)
                      continue;

                  kb->start_addr = (unsigned long)addr;
                  symbol_name = kallsyms_lookup(kb->start_addr, &size, &offset, &modname,
                                                namebuf);
                  if (!symbol_name)
                      kb->range = 0;
                  else
                      kb->range = size;
              }
              ...

              kprobes_all_disarmed = false;

              // 3) 初始化CPU架構(gòu)相關(guān)的環(huán)境(x86架構(gòu)的實(shí)現(xiàn)為空)
              err = arch_init_kprobes();

              // 4) 注冊die通知鏈(這個(gè)比較重要)
              if (!err)
                  err = register_die_notifier(&kprobe_exceptions_nb);

              // 5) 注冊模塊通知鏈
              if (!err)
                  err = register_module_notifier(&kprobe_module_nb);
              ...
              return err;
          }

          上面代碼精簡了一些與 kprobe 功能無關(guān)的代碼(如 kretprobe 的功能代碼)。

          init_kprobes() 函數(shù)主要完成 5 件事情:

          1. 初始化用于存儲(chǔ) kprobe 模塊的哈希表。
          2. 初始化 kprobe 的黑名單函數(shù)列表(不能被 kprobe 跟蹤的函數(shù)列表)。
          3. 初始化CPU架構(gòu)相關(guān)的環(huán)境(x86 CPU架構(gòu)的實(shí)現(xiàn)為空)。
          4. 注冊die通知鏈(重要)。
          5. 注冊模塊通知鏈。

          kprobe模塊哈希表

          我們在《Linux 內(nèi)核調(diào)試?yán)?| kprobe 的使用》一文中介紹過,一個(gè) kprobe 模塊是由一個(gè) struct kprobe 結(jié)構(gòu)來描述的。我們再來重溫一下這個(gè)結(jié)構(gòu):

          struct kprobe {
              // 用于保存到 kprobe 模塊哈希表
              struct hlist_node hlist;
              ...
              kprobe_opcode_t *addr;
              const char *symbol_name;
              unsigned int offset;
              // 回調(diào)函數(shù)
              kprobe_pre_handler_t pre_handler;
              kprobe_post_handler_t post_handler;
              ...

              kprobe_opcode_t opcode;
              struct arch_specific_insn ainsn;
              u32 flags;
          };

          struct kprobe 結(jié)構(gòu)的 hlist 字段用于把當(dāng)前結(jié)構(gòu)存放到 kprobe 模塊 哈希表中,如下圖所示:

          (圖5)

          內(nèi)核把跟蹤的指令地址作為鍵,然后將 kprobe 結(jié)構(gòu)保存到哈希表中,這樣就能通過指令的地址快速查找到對(duì)應(yīng)的 kprobe 結(jié)構(gòu)。

          注冊 die 通知鏈

          通知鏈 機(jī)制是內(nèi)核用于做一些事件回調(diào)操作的功能,比如說:當(dāng)關(guān)機(jī)時(shí),需要把內(nèi)存中的數(shù)據(jù)寫入到磁盤,就可以通過 通知鏈 來實(shí)現(xiàn)。

          kprobe 在初始化階段,會(huì)把 kprobe_exceptions_notify() 回調(diào)函數(shù)注冊到 die 通知鏈中。代碼如下:

          static struct notifier_block kprobe_exceptions_nb = {
              .notifier_call = kprobe_exceptions_notify,
              ...
          };

          static int __init init_kprobes(void)
          {
              ...
              if (!err)
                  err = register_die_notifier(&kprobe_exceptions_nb);
              ...
          }

          init_kprobes() 通過調(diào)用 register_die_notifier() 函數(shù)將 kprobe_exceptions_notify() 回調(diào)函數(shù)注冊到 die 通知鏈中。

          當(dāng) CPU 觸發(fā)斷點(diǎn)異常時(shí)(執(zhí)行 int3 指令),內(nèi)核將會(huì)執(zhí)行 do_int3() 異常處理例程,而 do_int3() 例程將會(huì)調(diào)用 die 通知鏈中的回調(diào)函數(shù)。此時(shí),kprobe_exceptions_notify() 回調(diào)函數(shù)將會(huì)被執(zhí)行。

          關(guān)于 kprobe_exceptions_notify() 回調(diào)函數(shù)的執(zhí)行流程下面將會(huì)介紹。

          2. 注冊 kprobe 實(shí)例

          在《Linux 內(nèi)核調(diào)試?yán)?| kprobe 的使用》一文中介紹過,編寫好的 kprobe 模塊需要通過調(diào)用 register_kprobe() 函數(shù)來注冊到內(nèi)核。

          我們來看看 register_kprobe() 函數(shù)的實(shí)現(xiàn):

          int __kprobes register_kprobe(struct kprobe *p)
          {
              ...
              // 1) 獲取要跟蹤的指令的內(nèi)存地址
              addr = kprobe_addr(p);       
              ...
              p->addr = addr;
              ...
              // 2) 檢測跟蹤點(diǎn)是否合法
              ret = check_kprobe_address_safe(p, &probed_mod); 
              ...
              // 3) 保存被跟蹤指令的值
              ret = prepare_kprobe(p);
              ...
              // 4) 將 kprobe 結(jié)構(gòu)添加到 kprobe 模塊哈希表中
              hlist_add_head_rcu(&p->hlist,
                                 &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);

              // 5) 將要跟蹤的指令替換成 int3 指令
              if (!kprobes_all_disarmed && !kprobe_disabled(p))
                  arm_kprobe(p);
              ...
              return ret;
          }

          經(jīng)過精簡后,上面代碼只留下了主要流程。

          從上面代碼可以看出,register_kprobe() 函數(shù)主要完成 5 件事情:

          1. 獲取要跟蹤的內(nèi)核函數(shù)中的指令內(nèi)存地址(跟蹤點(diǎn))。
          2. 檢測跟蹤點(diǎn)地址是否合法。
          3. 保存被跟蹤指令的值。
          4. 將當(dāng)前注冊的 kprobe 結(jié)構(gòu)添加到 kprobe 模塊哈希表中。
          5. 將要跟蹤的指令替換成 int3 指令。

          下面說說這 5 件事情分別要完成什么功能:

          獲取跟蹤指令的內(nèi)存地址

          一般來說,我們要跟蹤一個(gè)內(nèi)核函數(shù)的某條指令,都是通過內(nèi)核函數(shù)名去指定的(當(dāng)然也可以直接指定指令的內(nèi)存地址,但這個(gè)方法比較麻煩)。

          所以,內(nèi)核首先需要通過函數(shù)名,來獲取其第一條指令對(duì)應(yīng)的內(nèi)存地址。而內(nèi)核是通過調(diào)用 kprobe_addr() 函數(shù)來獲取跟蹤函數(shù)的內(nèi)存地址。

          而 kprobe_addr() 最終會(huì)調(diào)用 kallsyms_lookup_name() 來獲取跟蹤函數(shù)的內(nèi)存地址。kallsyms_lookup_name() 函數(shù)的實(shí)現(xiàn),本文不再展開細(xì)說,有興趣可以自行閱讀代碼或者查閱其他文獻(xiàn)。

          檢測跟蹤點(diǎn)地址是否合法

          這個(gè)過程主要對(duì)跟蹤指令的內(nèi)存地址進(jìn)行合法檢測,主要檢查幾個(gè)點(diǎn):

          1. 跟蹤點(diǎn)是否已經(jīng)被 ftrace 跟蹤,如果是就返回錯(cuò)誤(kprobe 與 ftrace 不能同時(shí)跟蹤同一個(gè)地址)。
          2. 跟蹤點(diǎn)是否在內(nèi)核代碼段,因?yàn)?kprobe 只能跟蹤內(nèi)核函數(shù),所以跟蹤點(diǎn)必須在內(nèi)核代碼段中。
          3. 跟蹤點(diǎn)是否在 kprobe 的黑名單中,如果是就返回錯(cuò)誤。
          4. 跟蹤點(diǎn)是否在內(nèi)核模塊代碼段中,kprobe 也可以跟蹤內(nèi)核模塊的函數(shù)。

          保存被跟蹤指令的值

          內(nèi)核通過調(diào)用 prepare_kprobe() 函數(shù)來保存被跟蹤的指令,而 prepare_kprobe() 最終會(huì)調(diào)用 CPU 架構(gòu)相關(guān)的 arch_prepare_kprobe() 函數(shù)來完成任務(wù)。

          我們來看看 arch_prepare_kprobe() 函數(shù)的實(shí)現(xiàn):

          int __kprobes arch_prepare_kprobe(struct kprobe *p)
          {
              ...
              // 1) 申請內(nèi)存空間,用于存放原指令的數(shù)據(jù)
              p->ainsn.insn = get_insn_slot();
              ...
              // 2) 保存原來指令的值
              return arch_copy_kprobe(p);
          }

          最終結(jié)果如 圖2 所示。

          將當(dāng) kprobe 結(jié)構(gòu)添加到哈希表中

          將當(dāng)前 kprobe 結(jié)構(gòu)添加到 kprobe 模塊哈希表中,主要為了能夠通過跟蹤點(diǎn)的內(nèi)存地址快速查找到對(duì)應(yīng)的 kprobe 結(jié)構(gòu),如 圖5 所示。

          將跟蹤點(diǎn)替換成 int3 指令

          將跟蹤點(diǎn)替換成 int3 指令的目的是,當(dāng) CPU 執(zhí)行到跟蹤點(diǎn)時(shí),將會(huì)觸發(fā)產(chǎn)生斷點(diǎn)中斷,這時(shí)內(nèi)核將會(huì)調(diào)用 do_int3() 處理異常,如 圖2 所示。

          將跟蹤點(diǎn)替換成 int3 指令是由 arm_kprobe() 函數(shù)完成,其調(diào)用鏈如下:

          arm_kprobe()
          └→ __arm_kprobe()
             └→ arch_arm_kprobe()

          從上面的調(diào)用可以看到,arm_kprobe() 最終會(huì)調(diào)用 arch_arm_kprobe() 函數(shù)來完成替換工作,我們來看看 arch_arm_kprobe() 函數(shù)的實(shí)現(xiàn):

          #define BREAKPOINT_INSTRUCTION  0xcc

          void __kprobes arch_arm_kprobe(struct kprobe *p)
          {
              text_poke(p->addr, ((unsigned char []){BREAKPOINT_INSTRUCTION}), 1);
          }

          從上面可以看出,arch_arm_kprobe() 函數(shù)把跟蹤點(diǎn)地址處的數(shù)據(jù)替換成 0xcc(也就是 int3 指令)。

          3. kprobe 回調(diào)

          前面說過,當(dāng) CPU 執(zhí)行到 int3 指令時(shí),將會(huì)觸發(fā)斷點(diǎn)異常。此時(shí),內(nèi)核將會(huì)調(diào)用 do_int3() 函數(shù)來處理異常。

          do_int3() 函數(shù)對(duì) kprobe 處理的調(diào)用鏈如下:

          do_int3()
          └→ notify_die()
             └→ atomic_notifier_call_chain()
                └→ __atomic_notifier_call_chain()
                   └→ notifier_call_chain()
                      └→ kprobe_exceptions_notify()

          從上面的調(diào)用鏈可以看出,do_int3() 最終會(huì)調(diào)用 kprobe_exceptions_notify() 函數(shù)來處理 kprobe 的流程。

          我們來看看 kprobe_exceptions_notify() 函數(shù)的實(shí)現(xiàn):

          int __kprobes 
          kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, void *data)
          {
              struct die_args *args = data;
              int ret = NOTIFY_DONE;

              // 1) 如果是用戶態(tài)觸發(fā),直接返回,因?yàn)橛脩魬B(tài)不能使用 kprobe
              if (args->regs && user_mode_vm(args->regs))
                  return ret;

              switch (val) {
              // 2) 如果異常是由 int3 指令觸發(fā)的,則調(diào)用 kprobe_handler() 處理異常
              case DIE_INT3:
                  if (kprobe_handler(args->regs)) 
                      ret = NOTIFY_STOP;
                  break;
              ...
              default:
                  break;
              }

              return ret;
          }

          從上面代碼可以看出,當(dāng)異常是由 int3 指令觸發(fā)的,將會(huì)調(diào)用 kprobe_handler() 函數(shù)處理異常。

          我們來分析下 kprobe_handler() 函數(shù)的實(shí)現(xiàn):

          static int __kprobes 
          kprobe_handler(struct pt_regs *regs)
          {
              ...
              // 1) 獲取觸發(fā)異常的指令內(nèi)存地址
              addr = (kprobe_opcode_t *)(regs->ip - sizeof(kprobe_opcode_t));
              ...
              // 2) 通過內(nèi)存地址獲取 kprobe 結(jié)構(gòu)(在注冊階段將其添加到哈希表中)
              p = get_kprobe(addr);
              if (p) {
                      ...
                      // 3) 如果 kprobe 模塊定義了 pre_handler() 回調(diào),那么調(diào)用 pre_handler() 回調(diào)函數(shù)
                      if (!p->pre_handler || !p->pre_handler(p, regs))
                          // 4) 設(shè)置單步調(diào)試模式
                          setup_singlestep(p, regs, kcb, 0);
                      return 1;
                      ...
              }
              ...
              return 0;
          }

          kprobe_handler() 函數(shù)會(huì)處理幾種情況,本文我們主要按照最常見的情況分析,就是上面代碼的流程。

          從上面代碼可以看到,kprobe_handler() 函數(shù)主要完成 4 件事情:

          1. 獲取觸發(fā)異常的指令內(nèi)存地址(也就是 int3 指令的內(nèi)存地址)。
          2. 通過內(nèi)存地址獲取 kprobe 結(jié)構(gòu)(在注冊階段將其添加到哈希表中)。
          3. 如果 kprobe 模塊定義了 pre_handler() 回調(diào),那么調(diào)用 pre_handler() 回調(diào)函數(shù)。
          4. 設(shè)置單步調(diào)試模式。

          從上面的分析可以知道,在 do_int3() 異常處理例程中調(diào)用了 kprobe 模塊的 pre_handler() 回調(diào)函數(shù),但 post_handler() 回調(diào)函數(shù)在什么地方調(diào)用呢?

          我們知道,kprobe 模塊的 post_handler() 回調(diào)函數(shù)是在被跟蹤指令執(zhí)行完后被調(diào)用的。所以,在 do_int3() 異常處理例程中調(diào)用是不合適的。

          為了解決這個(gè)問題,Linux 內(nèi)核使用單步調(diào)試模式來處理這種情況。設(shè)置單步調(diào)試模式由 setup_singlestep() 函數(shù)完成,我們來分析其實(shí)現(xiàn):

          static void __kprobes 
          setup_singlestep(struct kprobe *p, struct pt_regs *regs,
                           struct kprobe_ctlblk *kcb, int reenter)

          {
              ...
              // 1) 將 flags 寄存器的 TF 標(biāo)志位設(shè)置為1,進(jìn)入單步調(diào)試模式
              regs->flags |= X86_EFLAGS_TF;
              regs->flags &= ~X86_EFLAGS_IF;

              // 2) 設(shè)置異常返回后執(zhí)行的下一條指令的地址
              if (p->opcode == BREAKPOINT_INSTRUCTION)
                  regs->ip = (unsigned long)p->addr;
              else
                  regs->ip = (unsigned long)p->ainsn.insn;
          }

          setup_singlestep() 函數(shù)主要完成兩件事情:

          1. 將 flags 寄存器的 TF 標(biāo)志位設(shè)置為1,進(jìn)入單步調(diào)試模式(可以參考 Intel 的手冊)。
          2. 設(shè)置異常處理例程(do_int3() 函數(shù))返回后,執(zhí)行下一條指令的地址(執(zhí)行原來的指令)。

          設(shè)置完單步調(diào)試模式后,內(nèi)核就從 do_int3() 異常處理例程中返回,接著執(zhí)行原來的指令。

          4. 單步調(diào)試

          由于設(shè)置了單步調(diào)試模式后,CPU 每執(zhí)行一條指令,都會(huì)觸發(fā)一次 debug 異常。這時(shí),內(nèi)核將會(huì)調(diào)用 do_debug() 異常處理例程來處理 debug 異常。

          然而,在 do_debug() 異常處理例程中,會(huì)通過調(diào)用 kprobe_exceptions_notify() 函數(shù)來執(zhí)行 kprobe 模塊的 post_handler() 回調(diào)函數(shù)。我們來看看其調(diào)用鏈:

          do_debug()
          └→ notify_die()
             └→ atomic_notifier_call_chain()
                └→ __atomic_notifier_call_chain()
                   └→ notifier_call_chain()
                      └→ kprobe_exceptions_notify()
                         └→ post_kprobe_handler()
                            └→ post_handler()

          從上面的調(diào)用鏈可以看出,do_deubg() 也是通過調(diào)用 kprobe_exceptions_notify() 函數(shù)來處理 kprobe 機(jī)制的流程。

          下面我們來分析 kprobe_exceptions_notify() 函數(shù)對(duì) debug 異常的處理過程,代碼如下:

          int __kprobes 
          kprobe_exceptions_notify(struct notifier_block *self, unsigned long val, 
                                   void *data)

          {
              struct die_args *args = data;
              int ret = NOTIFY_DONE;

              // 1) 如果是用戶態(tài)觸發(fā)的異常,那么直接返回
              if (args->regs && user_mode_vm(args->regs))
                  return ret;

              switch (val) {
              ...
              // 2) 如果是 debug 異常觸發(fā)的,那么就調(diào)用 post_kprobe_handler() 進(jìn)行處理
              case DIE_DEBUG:
                  if (post_kprobe_handler(args->regs)) {
                      ...
                  }
                  break;
              ...
              default:
                  break;
              }
              return ret;
          }

          從上面代碼可知,如果當(dāng)前發(fā)生的異常是 debug 異常,那么將會(huì)調(diào)用 post_kprobe_handler() 函數(shù)進(jìn)行處理。

          我們來看看 post_kprobe_handler() 函數(shù)的實(shí)現(xiàn):

          static int __kprobes post_kprobe_handler(struct pt_regs *regs)
          {
              ...
              // 如果 kprobe 模塊實(shí)現(xiàn)了 post_handler() 回調(diào)函數(shù),那么就執(zhí)行 post_handler() 回調(diào)函數(shù)
              if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
                  ...
                  cur->post_handler(cur, regs, 0);
              }
              ...
              return 1;
          }

          如果 kprobe 模塊實(shí)現(xiàn)了 post_handler() 回調(diào)函數(shù),那么 post_kprobe_handler() 將會(huì)執(zhí)行它。

          總結(jié)

          本文主要介紹了 kprobe 的原理與實(shí)現(xiàn),正如本文開始時(shí)所說,kprobe 機(jī)制的細(xì)節(jié)很多,所以本文不可能對(duì)所有細(xì)節(jié)進(jìn)行分析。

          如果大家對(duì) kprobe 的所有實(shí)現(xiàn)細(xì)節(jié)有興趣,可以自行閱讀源碼。

          瀏覽 190
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  人妻无码中文字幕蜜桃 | 91精品秘 一区二区三区 | 毛片毛片毛片毛片毛片 | 免费淫秽视频 | 日本五十肥熟交尾 |