【Android 內存優化】怎么理解Android PLT hook?

文章目錄

  • 前言
  • 什么是hook?
  • PLT hook
    • 作用
    • 基本原理
    • PLT hook 總體步驟
  • 代碼案例分析
  • 方案預研
    • 面臨的問題
    • 怎么做?
    • ELF
      • ELF 文件頭
      • SHT(section header table)
    • 鏈接視圖(Linking View)和執行視圖(Execution View)
    • 動態鏈接
  • 猜想-解決-驗證
    • 第一次驗證
    • 方案
    • 再次驗證
  • 參考資料

前言

昨晚看完了愛奇藝出品的開源框架xhook中的《Android PLT hook 概述》,內容比較深。主要用到了ELF和動態鏈接這些Linux的知識點,但是總體上還算是理解了基本原理,這篇文章主要記錄下現階段對PLT hook的學習和理解。感覺寫原文的作者很牛逼,內容看的我熱血沸騰,需要看原文的讀者可以翻到文章最后點開原文鏈接。
最后希望感謝你花時間閱讀本文,下面開始學習下面的內容。

什么是hook?

“Hook”(鉤子)是計算機編程中的一種技術,它允許開發者攔截、修改或擴展軟件或系統的行為。通過使用鉤子,開發者可以在特定事件發生時注入自定義代碼,以便修改程序的行為或響應程序的特定事件。

鉤子的使用場景大概有這些:

  • 鍵盤和鼠標事件:攔截鍵盤和鼠標輸入,用于實現自定義的快捷鍵、鼠標手勢或輸入法。
  • 窗口消息:攔截和處理窗口消息,用于實現窗口管理、界面定制等功能。
  • 函數調用:攔截特定函數的調用,用于實現調試、性能分析、代碼注入等功能。
  • 文件操作和網絡請求:攔截文件操作和網絡請求,用于實現文件監控、安全檢測等功能。

鉤子技術可以提供很大的靈活性和功能擴展性,但也需要謹慎使用,因為不正確的使用鉤子可能會導致程序崩潰、安全漏洞或不穩定的行為。

當然,我們本文要討論的場景屬于函數調用場景。那么PLT hook又是什么意思?

PLT hook

作用

PLT (Procedure Linkage Table) hook 是一種在動態鏈接庫(DLL)或共享對象(SO)中實現的技術,用于在運行時修改或攔截函數調用。這種技術通常用于在不修改原始代碼的情況下,對函數的行為進行修改或監視。

基本原理

PLT hook 的基本原理是利用了動態鏈接庫的符號解析機制。

在程序加載動態鏈接庫時,系統會創建一個 PLT 表來保存對動態鏈接函數的調用。這個表中的每個條目實際上是一個跳轉指令,將控制權轉移到動態鏈接庫中的實際函數實現。

PLT hook 就是通過修改 PLT 表中的條目,將原始函數調用指向自定義的函數或者跳轉到其他代碼段,從而實現對函數行為的修改或攔截。

PLT hook 總體步驟

  1. 定位目標函數:確定要 hook 的目標函數,獲取其在動態鏈接庫中的地址。

  2. 修改 PLT 表:通過修改 PLT 表中目標函數對應的條目,將其指向自定義的函數或者其他代碼段。

  3. 處理原始函數調用:在自定義函數中可以執行一些額外的操作,然后再調用原始的目標函數,或者完全替換原始函數的行為。

  4. 恢復原始調用:有時候需要在自定義函數中調用原始的目標函數,以保持程序的正常行為。

有了上述的基本了解,再來看看給出的例子,就會容易理解很多。

代碼案例分析

下面通過給出一個存在明顯內存泄漏的代碼,把它們編譯為動態庫libtest.so。看看最后能不能通過PLT hook把泄漏給解決了。

頭文件 test.h

#ifndef TEST_H
#define TEST_H 1#ifdef __cplusplus
extern "C" {
#endifvoid say_hello();#ifdef __cplusplus
}
#endif#endif

源文件 test.c

#include <stdlib.h>
#include <stdio.h>void say_hello()
{char *buf = malloc(1024);if(NULL != buf){snprintf(buf, 1024, "%s", "hello\n");printf("%s", buf);}
}

源文件 main.c

#include <test.h>int main()
{say_hello();return 0;
}

執行:

caikelun@debian:~$ adb push ./libtest.so ./main /data/local/tmp
caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main"
caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main"
hello
caikelun@debian:~$

把編譯后的libtest.so 推到Android設備中,給個可執行權限后,執行它。雖然泄漏了,但是還是可以執行的。這正是模擬真實情況的代碼,泄漏了卻不自知。

方案預研

面臨的問題

假如我們發現了泄漏問題,要修復它并不難,問題在于怎么及時發現它們。
問題在于下面2個:
1.假如程序測試覆蓋得不夠的話,怎么及時發現和定位一些已經上線的APP呢?
2.如果libtest.so是第三方庫,而且還是閉源的。我們可以修復它嗎?能否監控它?

怎么做?

這正是hook可以做到的事情,比如 hook malloc,calloc,realloc 和 free,我們就能統計出各個動態庫分配了多少內存,哪些內存一直被占用沒有釋放。
作者提到,hook自己的進程完全是可以的,hook其它的進程需要root權限。因為假如沒有root權限,就無法修改其它進程的內存空間。
好消息是,我們只要hook自己的進程就夠了。

下面本文的主角要出來了。
而要理解PLT hook,首先要了解ELF是什么。

ELF

ELF(Executable and Linkable Format)是一種行業標準的二進制數據封裝格式,主要用于封裝可執行文件、動態庫、object 文件和 core dumps 文件。

在這里插入圖片描述
ELF概述
ELF定義

對于PLT hook,最重要的是了解ELF 文件頭、SHT(section header table)、PHT(program header table)。

ELF 文件頭

ELF 文件的起始處,有一個固定格式的定長的文件頭(32 位架構為 52 字節,64 位架構為 64 字節)。ELF 文件頭以 magic number 0x7F 0x45 0x4C 0x46 開始(其中后 3 個字節分別對應可見字符 E L F)。

libtest.so 的 ELF 文件頭信息:

caikelun@debian:~$ arm-linux-androideabi-readelf -h ./libtest.soELF Header:Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00Class:                             ELF32Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              DYN (Shared object file)Machine:                           ARMVersion:                           0x1Entry point address:               0x0Start of program headers:          52 (bytes into file)Start of section headers:          12744 (bytes into file)Flags:                             0x5000200, Version5 EABI, soft-float ABISize of this header:               52 (bytes)Size of program headers:           32 (bytes)Number of program headers:         8Size of section headers:           40 (bytes)Number of section headers:         25Section header string table index: 24

ELF 文件頭中包含了 SHT 和 PHT 在當前 ELF 文件中的起始位置和長度。例如,libtest.so 的 SHT 起始位置為 12744,長度 40 字節;PHT 起始位置為 52,長度 32字節。

ELF header數據結構如圖:

請添加圖片描述

SHT(section header table)

ELF 以 section 為單位來組織和管理各種信息。ELF 使用 SHT 來記錄所有 section 的基本信息。主要包括:section 的類型、在文件中的偏移量、大小、加載到內存后的虛擬內存相對地址、內存中字節的對齊方式等。

caikelun@debian:~$ arm-linux-androideabi-readelf -S ./libtest.soThere are 25 section headers, starting at offset 0x31c8:Section Headers:[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al[ 0]                   NULL            00000000 000000 000000 00      0   0  0[ 1] .note.android.ide NOTE            00000134 000134 000098 00   A  0   0  4[ 2] .note.gnu.build-i NOTE            000001cc 0001cc 000024 00   A  0   0  4[ 3] .dynsym           DYNSYM          000001f0 0001f0 0003a0 10   A  4   1  4[ 4] .dynstr           STRTAB          00000590 000590 0004b1 00   A  0   0  1[ 5] .hash             HASH            00000a44 000a44 000184 04   A  3   0  4[ 6] .gnu.version      VERSYM          00000bc8 000bc8 000074 02   A  3   0  2[ 7] .gnu.version_d    VERDEF          00000c3c 000c3c 00001c 00   A  4   1  4[ 8] .gnu.version_r    VERNEED         00000c58 000c58 000020 00   A  4   1  4[ 9] .rel.dyn          REL             00000c78 000c78 000040 08   A  3   0  4[10] .rel.plt          REL             00000cb8 000cb8 0000f0 08  AI  3  18  4[11] .plt              PROGBITS        00000da8 000da8 00017c 00  AX  0   0  4[12] .text             PROGBITS        00000f24 000f24 0015a4 00  AX  0   0  4[13] .ARM.extab        PROGBITS        000024c8 0024c8 00003c 00   A  0   0  4[14] .ARM.exidx        ARM_EXIDX       00002504 002504 000100 08  AL 12   0  4[15] .fini_array       FINI_ARRAY      00003e3c 002e3c 000008 04  WA  0   0  4[16] .init_array       INIT_ARRAY      00003e44 002e44 000004 04  WA  0   0  1[17] .dynamic          DYNAMIC         00003e48 002e48 000118 08  WA  4   0  4[18] .got              PROGBITS        00003f60 002f60 0000a0 00  WA  0   0  4[19] .data             PROGBITS        00004000 003000 000004 00  WA  0   0  4[20] .bss              NOBITS          00004004 003004 000000 00  WA  0   0  1[21] .comment          PROGBITS        00000000 003004 000065 01  MS  0   0  1[22] .note.gnu.gold-ve NOTE            00000000 00306c 00001c 00      0   0  4[23] .ARM.attributes   ARM_ATTRIBUTES  00000000 003088 00003b 00      0   0  1[24] .shstrtab         STRTAB          00000000 0030c3 000102 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),y (noread), p (processor specific)
  • .dynamic:供動態鏈接器使用的各項信息,記錄了當前 ELF 的外部依賴,以及其他各個重要 section 的起始位置等信息。
  • .got:Global Offset Table。用于記錄外部調用的入口地址。動態鏈接器(linker)執行重定位(relocate)操作時,這里會被填入真實的外部調用的絕對地址。
  • .plt:Procedure Linkage Table。外部調用的跳板,主要用于支持 lazy binding 方式的外部調用重定位。(Android 目前只有 MIPS 架構支持 lazy binding)

下面用一張圖描述相關的關系,這也是PLT hook的核心原理:

請添加圖片描述

鏈接視圖(Linking View)和執行視圖(Execution View)

  • 連接視圖:ELF 未被加載到內存執行前,以 section 為單位的數據組織形式。
  • 執行視圖:ELF 被加載到內存后,以 segment 為單位的數據組織形式。

請添加圖片描述

而hook操作關系的是動態形式的內存操作,所以關心的是執行視圖,也就是上面的右圖。

動態鏈接

動態鏈接的大體步驟如下:

  • 檢查已經加載的ELF列表
  • 從libtest.so的.dynamic section 中讀取 libtest.so中外部依賴的ELF列表,從列表中剔除已經加載的ELF,得到本次需要加載的ELF
  • 用mmap預留一塊內存,用于后面映射ELF
  • 讀取ELF的PHT,用mmap把所有PT_LOAD類型的segment映射到內存中
  • 從.dynamic section 中讀取各個section的虛擬內存地址,計算&保存各個section的虛擬內存絕對地址。
  • 執行重定位操作(relocate),這是最關鍵的一步。重定位信息可能存在于下面的一個或多個 secion 中:.rel.plt, .rela.plt, .rel.dyn, .rela.dyn, .rel.android, .rela.android。動態鏈接器需要逐個處理這些 .relxxx section 中的重定位訴求。根據已加載的 ELF 的信息,動態鏈接器查找所需符號的地址(比如 libtest.so 的符號 malloc),找到后,將地址值填入 .relxxx 中指明的目標地址中,這些“目標地址”一般存在于.got 或 .data 中。
  • ELF引用計數加一
  • 逐個調用ELF的構造函數,先調用被依賴ELF的構造函數,再調用libtest.so自己的構造函數。

這里是全文最關鍵地地方,分析重定位操作可以推理出:
只要從這些 .relxxx 中獲取到“目標地址”,然后在“目標地址”中重新填上一個新的函數地址,這樣就完成 hook 了

.dynamic section
這是一個十分重要和特殊的 section,其中包含了 ELF 中其他各個 section 的內存位置等信息。在執行視圖中,總是會存在一個類型為 PT_DYNAMIC 的 segment,這個 segment 就包含了 .dynamic section 的內容。
無論是執行 hook 操作時,還是動態鏈接器執行動態鏈接時,都需要通過 PT_DYNAMIC segment 來找到 .dynamic section 的內存位置,再進一步讀取其他各項 section 的信息。

猜想-解決-驗證

原文這部分就是通過讀取libtest.so的匯編代碼,通過NDK的objdump 查出反匯編輸出。接著通過一些計算得出libtest.so中malloc的地址3f90 。

第一次驗證

#include <test.h>void *my_malloc(size_t size)
{printf("%zu bytes memory are allocated by libtest.so\n", size);return malloc(size);
}int main()
{void **p = (void **)0x3f90;*p = (void *)my_malloc; // do hooksay_hello();return 0;
}

運行結果:

caikelun@debian:~$ adb push ./main /data/local/tmp
caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main"
caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main"
Segmentation fault
caikelun@debian:~$

例子驗證了思路是正確的但是需要解決3個問題:

  1. 計算出來的地址是個先對內存地址,需要轉換為絕對地址
  2. 地址很可能沒有寫入權限
  3. 新的函數地址即使可以賦值成功,my_malloc 也不會執行,因為處理器有指令緩存。

方案

上述第一個問題可以通過基地址解決。
第二個問題通過mprotect解決。
第三個問題通過__builtin___clear_cache函數調用解決。

再次驗證

把main.c 修改為:

#include <inttypes.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <test.h>#define PAGE_START(addr) ((addr) & PAGE_MASK)
#define PAGE_END(addr)   (PAGE_START(addr) + PAGE_SIZE)void *my_malloc(size_t size)
{printf("%zu bytes memory are allocated by libtest.so\n", size);return malloc(size);
}void hook()
{char       line[512];FILE      *fp;uintptr_t  base_addr = 0;uintptr_t  addr;//find base address of libtest.soif(NULL == (fp = fopen("/proc/self/maps", "r"))) return;while(fgets(line, sizeof(line), fp)){if(NULL != strstr(line, "libtest.so") &&sscanf(line, "%"PRIxPTR"-%*lx %*4s 00000000", &base_addr) == 1)break;}fclose(fp);if(0 == base_addr) return;//the absolute addressaddr = base_addr + 0x3f90;//add write permissionmprotect((void *)PAGE_START(addr), PAGE_SIZE, PROT_READ | PROT_WRITE);//replace the function address*(void **)addr = my_malloc;//clear instruction cache__builtin___clear_cache((void *)PAGE_START(addr), (void *)PAGE_END(addr));
}int main()
{hook();say_hello();return 0;
}
caikelun@debian:~$ adb push ./main /data/local/tmp
caikelun@debian:~$ adb shell "chmod +x /data/local/tmp/main"
caikelun@debian:~$ adb shell "export LD_LIBRARY_PATH=/data/local/tmp; /data/local/tmp/main"
1024 bytes memory are allocated by libtest.so
hello
caikelun@debian:~$

上述代碼在沒有重新編譯libtest.so的前提下,libtest.so的函數say_hello的函數地址替換成了my_malloc的函數地址。從而完成了native層面的hook。

至此,PLT hook的整體原理介紹完畢。
原文更加詳細和精彩,適合需要更深入理解和實操的朋友,鏈接在下面。

參考資料

參考原文:https://github.com/iqiyi/xHook/blob/master/docs/overview/android_plt_hook_overview.zh-CN.md
https://en.wikipedia.org/wiki/Hooking

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/719060.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/719060.shtml
英文地址,請注明出處:http://en.pswp.cn/news/719060.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2核4G服務器咋收費的?阿里云貴不貴?

阿里云2核4G服務器多少錢一年&#xff1f;2核4G配置1個月多少錢&#xff1f;2核4G服務器30元3個月、輕量應用服務器2核4G4M帶寬165元一年、企業用戶2核4G5M帶寬199元一年。可以在阿里云CLUB中心查看 aliyun.club 當前最新2核4G服務器精準報價、優惠券和活動信息。 阿里云官方2…

YOLO-World 簡單無需標注無需訓練直接可以使用的檢測模型

參考: https://github.com/AILab-CVC/YOLO-World YOLO-World 常規的label基本不用訓練,直接傳入圖片,然后寫入文本label提示既可 案例demo: 1)官方提供 https://huggingface.co/spaces/stevengrove/YOLO-World https://huggingface.co/spaces/SkalskiP/YOLO-World 檢測…

基于信息間隙決策理論的碳捕集電廠優化調度程序代碼!

適用平臺&#xff1a;MatlabYalmipCplex 程序在建立電廠與碳捕集裝置協同調度模型的基礎上&#xff0c;引入信息間隙決策理論(information gap decision theory, IGDT)以同時滿足系統的魯棒性和經濟性要求&#xff0c;通過風險追求和風險規避&#xff12;種決策角度得到不同的…

移動端1px問題,使用vant配合rem后需要處理成1.5px或者2,3,等等,不然ios上顯示不出來1px的邊框

table{td {border: 1.5px solid #ccc;font-family: PingFang SC, PingFang SC;font-weight: 400;font-size: 24px;color: #4E5464;line-height: 28px;text-align: center;empty-cells: show;padding: 20px 10px;height: 80px;white-space: nowrap;} }table的td樣式&#xff0c…

93. 復原 IP 地址(力扣LeetCode)

文章目錄 93. 復原 IP 地址題目描述回溯算法回溯優化&#xff08;在原s字符串上操作&#xff09; 93. 復原 IP 地址 題目描述 有效 IP 地址 正好由四個整數&#xff08;每個整數位于 0 到 255 之間組成&#xff0c;且不能含有前導 0&#xff09;&#xff0c;整數之間用 ‘.’…

真不愧是華為出來的,真的太厲害了。。。

&#x1f345; 視頻學習&#xff1a;文末有免費的配套視頻可觀看 &#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 實習去了博彥科技&#xff08;外包&#xff09;&#xff0c;做的就是螺絲釘的活&#xff0c;后面…

華為---MSTP(一)---MSTP生成樹協議

目錄 1. MSTP技術產生背景 2. STP/RSTP的缺陷 ?編輯 2.1 無法均衡流量負載 2.2 數據使用次優路徑 3. MSTP生成樹協議 3.1 MSTP相關概念 3.2 MSTP樹生成的形成過程 4. MSTP報文 1. MSTP技術產生背景 RSTP在STP基礎上進行了改進&#xff0c;實現了網絡拓撲快速收斂。但…

chisel入門初步2_2——-1/2次方生成器

由之前的GCN網絡的介紹可以得知&#xff0c;我們需要輸入兩個乘數&#xff08;兩個節點的節點度&#xff09;&#xff0c;并輸出他們乘積的-1/2次方&#xff0c;此處由于當時設計的booth編碼的乘法器為有符號數&#xff0c;而此處是無符號數&#xff0c;實在懶得再寫一份了&…

SpringBoot+Maven項目打包

項目的主POM文件里面添加maven打包插件 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.2</version><configuration><sour…

推薦一款新的自動化測試框架:DrissionPage

今天給大家推薦一款基于Python的網頁自動化工具&#xff1a;DrissionPage。這款工具既能控制瀏覽器&#xff0c;也能收發數據包&#xff0c;甚至能把兩者合而為一&#xff0c;簡單來說&#xff1a;集合了WEB瀏覽器自動化的便利性和 requests 的高效率優點。 一、DrissionPage框…

【C++庖丁解牛】默認成員函數

&#x1f4d9; 作者簡介 &#xff1a;RO-BERRY &#x1f4d7; 學習方向&#xff1a;致力于C、C、數據結構、TCP/IP、數據庫等等一系列知識 &#x1f4d2; 日后方向 : 偏向于CPP開發以及大數據方向&#xff0c;歡迎各位關注&#xff0c;謝謝各位的支持 目錄 前言1. 構造函數1.1 …

android系統簽名

系統簽名是指由 Android 系統或設備制造商使用他們的私鑰對應用程序進行數字簽名的過程。在 Android 應用程序開發中&#xff0c;應用程序的 APK 文件需要使用開發者的私鑰進行簽名&#xff0c;以便在安裝和更新時驗證應用程序的真實性和完整性。 系統簽名是一種特殊的簽名&am…

職場高薪 |「中高級測試」面試題

【軟件測試面試突擊班】2024吃透軟件測試面試最全八股文攻略教程&#xff0c;一周學完讓你面試通過率提高90%&#xff01;&#xff08;自動化測試&#xff09; 一.基礎題 1.測試用例你一般是怎么設計的&#xff0c;怎么可以提高覆蓋率&#xff1f; 有沒有形成自己的 一套方法論…

【個人感悟】醫院所見思考怎么做AI+醫療

今天陪家里人去醫院折騰了一上午&#xff0c;從消化科檢查&#xff08;驗血、胸部CT、心電圖&#xff09;&#xff0c; 消化科醫生看完報告&#xff0c;說CT影像看肺部有些問題&#xff0c;又排隊掛號呼吸科折騰&#xff0c; 一上午來回就過去了。 整個過程看似系統信息化程度…

Docker運行時安全之道: 保障容器環境的安全性

引言 Docker作為容器化技術的領軍者,為應用部署提供了靈活性和便捷性。然而,在享受這些優勢的同時,必須重視Docker運行時的安全性。本文將深入研究一些關鍵的Docker運行時安全策略,以確保你的容器環境在生產中得到有效的保護。 1. 使用最小特權原則 保持容器以最小權限運…

Jmeter接口測試---隨機數、加密、cookie鑒權、斷言、CSV參數化

隨機數 第一步&#xff1a;選擇工具-函數助手對話框 第二步&#xff1a;選擇random&#xff0c;設置最大值最小值&#xff0c;復制函數字符串到指定位置 加密接口 類型&#xff1a;AES、DES、Base64、RSA&#xff08;可以解密&#xff09; | MD5、SHA、HmacSHA&#xff08;不…

llama.c代碼2

1、forward 1.1、復習 encode(tokenizer, prompt, 1, 0, prompt_tokens, &num_prompt_tokens); 在encode函數結尾處(gdb) p *n_tokens $3 2(gdb) p *tokens3 $6 {1, 22172, 417} 在encode調用后 (gdb) print num_prompt_tokens $11 2 (gdb) print *prompt_tokens3 $13 …

【Nginx基礎和原理介紹】講解

Nginx基礎和原理介紹 1. 前言2. 基本特性3. 工作原理4. 總結 1. 前言 Nginx&#xff08;發音為“engine-x”&#xff09;是一個高性能的HTTP和反向代理服務器&#xff0c;它還可以作為IMAP/POP3代理服務器使用&#xff0c;Nginx是由Igor Sysoev為俄羅斯訪問量第二的Rambler.ru…

JavaScript 中的類型轉換機制(詳細講解)

文章目錄 一、概述二、顯示轉換Number()parseInt()String()Boolean() 三、隱式轉換自動轉換為布爾值自動轉換成字符串自動轉換成數值 一、概述 前面我們講到&#xff0c;JS中有六種簡單數據類型&#xff1a;undefined、null、boolean、string、number、symbol&#xff0c;以及…

(sub)三次握手四次揮手

TCP的三次握手與四次揮手理解及面試題 序列號seq&#xff1a;占4個字節&#xff0c;用來標記數據段的順序&#xff0c;TCP把連接中發送的所有數據字節都編上一個序號&#xff0c;第一個字節的編號由本地隨機產生&#xff1b;給字節編上序號后&#xff0c;就給每一個報文段指派一…