41.安卓逆向2-frida hook技術-過firda檢測(五)-利用ida分析app的so文件中frida檢測函數過檢測

免責聲明:內容僅供學習參考,請合法利用知識,禁止進行違法犯罪活動!

內容參考于:圖靈Python學院

工具下載:

鏈接:https://pan.baidu.com/s/1bb8NhJc9eTuLzQr39lF55Q?pwd=zy89

提取碼:zy89

復制這段內容后打開百度網盤手機App,操作更方便哦

上一個內容:40.安卓逆向2-frida hook技術-過firda檢測(四)(通過攔截so文件的創建和攔截檢測frida函數過檢測)

本次通過查找app中檢測frida的函數,然后使用frida對檢測frida的函數進行hook,通過hook讓它失效來繞過檢測

首先還是通過hook加載so文件的函數,看看它是加載到什么文件時進行的退出,然后使用ida反編譯so文件,下方是檢測app加載so文件的frida代碼

function main(){// 這段代碼是給一個叫"Frida"的工具用的腳本
// Frida的作用是:可以鉆進手機里的APP內部,看看這個APP在偷偷做什么
// 我們這段腳本的具體任務是:盯著APP加載"特殊文件"的行為// 首先,我們要找到APP加載文件時會用到的兩個"工具函數"// 第一個工具函數叫"dlopen"
// 所有運行在Linux或安卓系統上的程序,要加載"動態鏈接庫"(一種特殊文件,后綴通常是.so)時,經常會用到它
// "Module.findExportByName(null, "dlopen")"的作用:
// 1. 在系統的所有功能里找(null表示不限制范圍)
// 2. 找到名字叫"dlopen"的那個功能,記錄下它在內存中的位置
// 3. 把找到的結果存在變量"dlopen"里,方便后面使用
var dlopen = Module.findExportByName(null, "dlopen");// 第二個工具函數叫"android_dlopen_ext"
// 這是安卓系統專門設計的加強版加載工具,功能比dlopen更多一點
// 有些安卓APP會用這個函數來加載特殊的.so文件
// 下面這行代碼的作用和上面類似:找到這個函數的位置,存在變量里
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");// 接下來,我們要給第一個工具函數"dlopen"裝個"監控器"
// 當APP調用dlopen加載文件時,我們就能立刻知道
// "Interceptor.attach"就是Frida提供的"裝監控器"的功能
Interceptor.attach(dlopen, {// 當APP剛開始調用dlopen函數時,會自動執行下面的代碼// 可以理解為:監控器發現"有人要開始用這個工具了"onEnter: function (args) {// "args"是APP傳給dlopen的參數(就像我們給工具傳遞的"指令")// dlopen的第一個參數很重要:它告訴工具"要加載的文件在哪里"// 這里的"args[0]"就是取第一個參數(計算機里計數從0開始)var path_ptr = args[0];// 剛才拿到的"path_ptr"其實是個"內存地址"(類似文件在倉庫里的貨架編號)// 我們需要根據這個編號,找到實際的文件路徑(比如"/data/lib/test.so")// "ptr(path_ptr)"是把編號轉成Frida能識別的格式// ".readCString()"是按照計算機存儲文字的規則,把地址對應的內容讀出來var path = ptr(path_ptr).readCString();// 最后,把我們發現的信息打印到屏幕上// 這樣我們就能清楚地看到:這個APP用dlopen加載了哪個文件console.log("[發現使用dlopen加載文件:] ", path);},// 當APP用完dlopen函數(加載文件完成后),會執行下面的代碼// 可以理解為:監控器發現"這個人用完工具了"onLeave: function (retval) {// 目前這里什么都沒做,留空是因為我們暫時只關心"開始加載"這個動作// 如果以后想知道"加載成功了嗎",可以在這里處理返回值retval}
});// 下面是給第二個工具函數"android_dlopen_ext"裝監控器,原理和上面完全一樣
Interceptor.attach(android_dlopen_ext, {// 當APP剛開始調用這個安卓特有的加載函數時onEnter: function (args) {// 同樣取第一個參數:要加載的文件地址var path_ptr = args[0];// 把地址轉成我們能看懂的文件路徑var path = ptr(path_ptr).readCString();// 打印信息:APP用安卓特有的工具加載了哪個文件console.log("[發現使用安卓專用dlopen_ext加載文件:] ", path);},// 當APP用完這個函數后onLeave: function (retval) {// 這里也暫時什么都不做}
});
/**
總結代碼的效果:就像我們在 APP 的 "文件加載通道" 上裝了兩個攝像頭,一個盯著普通加載通道,一個盯著安卓專用通道。
只要 APP 從這些通道加載文件(特別是.so 格式的文件),攝像頭就會立刻拍下 "文件地址" 并顯示出來,讓
我們清楚知道這個 APP 在運行時偷偷加載了哪些底層文件。
這種監控在分析 APP 的工作原理、查找惡意軟件行為時非常有用。
*/
}
main()

如下圖注入上方的代碼后,在加載了下圖紅框的so文件后,frida退出了,這說明 libmsaoaidsec.so 里面有frida檢測

查看線程,從下圖紅框可以看出,libmsaoaidsec.so創建了三個線程,然后退出了,這說明檢測frida的代碼在這三個線程中,記住這三個值一會要用, 1c544、1b8d4、 26e5c

// 定義一個函數,名字叫 hook_patch,作用是"鉤住"線程創建的行為
function hook_patch() {
// 1. 找到系統里負責創建線程的函數(pthread_create)的地址
// 解釋:
// - pthread_create 是 Linux/Android 系統中創建線程的核心函數,所有程序創建線程都要調用它
// - Module.findExportByName("libc.so", "pthread_create") 意思是:從 libc.so 這個系統庫中,查找導出的 pthread_create 函數的地址
// - libc.so 是系統基礎庫,包含了很多常用的系統函數(比如創建線程、文件操作等)
var patch = Module.findExportByName("libc.so", "pthread_create");// 2. 打印找到的 pthread_create 函數的地址(調試用,確認是否找到了目標函數)
// 比如可能會輸出:[pth_create] 0x7f8a8b2c3d40(這是一個內存地址)
console.log("[pth_create]", patch);// 3. 攔截(Hook)這個 pthread_create 函數,監控它的調用
// Interceptor.attach 是 Frida 的攔截函數,第一個參數是要攔截的函數地址(這里就是上面找到的 patch)
// 第二個參數是一個對象,里面定義了攔截后的行為(進入函數時做什么,離開函數時做什么)
Interceptor.attach(patch, {// onEnter:當被攔截的函數(pthread_create)被調用時,會執行這里的代碼onEnter: function (args) {// args 是一個數組,存放了調用 pthread_create 時傳入的參數// pthread_create 的函數原型是:// int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void*(*start_routine)(void*), void* arg)// 所以 args[0] = 線程ID指針,args[1] = 線程屬性,args[2] = 線程要執行的函數(核心!線程啟動后會跑這個函數),args[3] = 傳給線程函數的參數// 4. 通過線程要執行的函數地址(args[2]),找到它屬于哪個模塊(.so 文件)// Process.findModuleByAddress(地址) 會返回這個地址所在的模塊信息(比如模塊名、路徑等)var module = Process.findModuleByAddress(args[2]);// 5. 檢查是否成功找到模塊(避免空值報錯)if (module != null) {// 打印線程相關信息:// - module.name:模塊的名字(比如 libnative.so,就是這個模塊創建了線程)// - args[2].sub(module.base):計算線程函數在模塊中的偏移量(相對位置)//   偏移量 = 函數的實際地址 - 模塊的基地址(模塊加載到內存的起始地址)//   比如模塊基地址是 0x1000,函數地址是 0x1200,偏移量就是 0x200console.log("開啟線程-->", module.name, args[2].sub(module.base));}},// onLeave:當被攔截的函數(pthread_create)執行完畢,準備返回時,會執行這里的代碼// 這里暫時為空,說明不需要處理函數返回后的邏輯onLeave: function (retval) {}
});
}
function main(){
// 這段代碼是給一個叫"Frida"的工具用的腳本
// Frida的作用是:可以鉆進手機里的APP內部,看看這個APP在偷偷做什么
// 我們這段腳本的具體任務是:盯著APP加載"特殊文件"的行為// 首先,我們要找到APP加載文件時會用到的兩個"工具函數"// 第一個工具函數叫"dlopen"
// 所有運行在Linux或安卓系統上的程序,要加載"動態鏈接庫"(一種特殊文件,后綴通常是.so)時,經常會用到它
// "Module.findExportByName(null, "dlopen")"的作用:
// 1. 在系統的所有功能里找(null表示不限制范圍)
// 2. 找到名字叫"dlopen"的那個功能,記錄下它在內存中的位置
// 3. 把找到的結果存在變量"dlopen"里,方便后面使用
var dlopen = Module.findExportByName(null, "dlopen");// 第二個工具函數叫"android_dlopen_ext"
// 這是安卓系統專門設計的加強版加載工具,功能比dlopen更多一點
// 有些安卓APP會用這個函數來加載特殊的.so文件
// 下面這行代碼的作用和上面類似:找到這個函數的位置,存在變量里
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");// 接下來,我們要給第一個工具函數"dlopen"裝個"監控器"
// 當APP調用dlopen加載文件時,我們就能立刻知道
// "Interceptor.attach"就是Frida提供的"裝監控器"的功能
Interceptor.attach(dlopen, {
// 當APP剛開始調用dlopen函數時,會自動執行下面的代碼
// 可以理解為:監控器發現"有人要開始用這個工具了"
onEnter: function (args) {// "args"是APP傳給dlopen的參數(就像我們給工具傳遞的"指令")// dlopen的第一個參數很重要:它告訴工具"要加載的文件在哪里"// 這里的"args[0]"就是取第一個參數(計算機里計數從0開始)var path_ptr = args[0];// 剛才拿到的"path_ptr"其實是個"內存地址"(類似文件在倉庫里的貨架編號)// 我們需要根據這個編號,找到實際的文件路徑(比如"/data/lib/test.so")// "ptr(path_ptr)"是把編號轉成Frida能識別的格式// ".readCString()"是按照計算機存儲文字的規則,把地址對應的內容讀出來var path = ptr(path_ptr).readCString();// 最后,把我們發現的信息打印到屏幕上// 這樣我們就能清楚地看到:這個APP用dlopen加載了哪個文件console.log("[發現使用dlopen加載文件:] ", path);},
// 當APP用完dlopen函數(加載文件完成后),會執行下面的代碼
// 可以理解為:監控器發現"這個人用完工具了"
onLeave: function (retval) {// 目前這里什么都沒做,留空是因為我們暫時只關心"開始加載"這個動作// 如果以后想知道"加載成功了嗎",可以在這里處理返回值retval
}
});// 下面是給第二個工具函數"android_dlopen_ext"裝監控器,原理和上面完全一樣
Interceptor.attach(android_dlopen_ext, {
// 當APP剛開始調用這個安卓特有的加載函數時
onEnter: function (args) {// 同樣取第一個參數:要加載的文件地址var path_ptr = args[0];// 把地址轉成我們能看懂的文件路徑var path = ptr(path_ptr).readCString();// 打印信息:APP用安卓特有的工具加載了哪個文件console.log("[發現使用安卓專用dlopen_ext加載文件:] ", path);if(path.indexOf("libmsaoaidsec.so")!=-1){// 調用查看線程的函數hook_patch()}
},
// 當APP用完這個函數后
onLeave: function (retval) {// 這里也暫時什么都不做
}
});
/**
總結代碼的效果:就像我們在 APP 的 "文件加載通道" 上裝了兩個攝像頭,一個盯著普通加載通道,一個盯著安卓專用通道。
只要 APP 從這些通道加載文件(特別是.so 格式的文件),攝像頭就會立刻拍下 "文件地址" 并顯示出來,讓
我們清楚知道這個 APP 在運行時偷偷加載了哪些底層文件。
這種監控在分析 APP 的工作原理、查找惡意軟件行為時非常有用。
*/
}
main()

然后把apk進行解壓,找到它的 libmsaoaidsec.so 文件拖到ida中進行反編譯

然后ida加載完后,點擊下圖紅框,然后按CTRL+F

然后搜索1c544、1b8d4、 26e5c這三個,如下圖首先是搜索1c544,雙擊下圖紅框位置就可以跳轉到1c544了,跳轉之后按f5轉偽c代碼

直接把下圖紅框的代碼全選,然后復制給ai大模型,讓它解釋

如下圖ai的解釋,這個 1c544 做的是字符串相關和定時任務(無限循環執行周期性任務),跟檢測frida無關,所以下一個

然后搜索1b8d4,老樣子全選復制給ai大模型

如下圖大模型的解釋,它有點可疑,根據大模型的解釋,1b8d4會檢測,還會暫停,它可能是檢測firda然后暫停frida

然后接著看最后一個 26e5c

大模型的解釋,三個線程都分析完了,現在只有 1b8d4 是最可疑的,接下來通過調用進一步分析(它有一個特征)

再次回到1b8d4 中,鼠標點擊下圖紅框位置,然后按x

可以看誰調用了1b8d4,如下圖有兩個位置調用了它,但都是在1B924中調用的

1B924 調用 1b8d4,然后雙擊上圖任意一個,然后點擊下圖紅框位置,再次按x,查看 1B924 是誰調用的

如下圖只有一個地方調用了 1B924

然后現在的調用棧是 1BEC4 調用 1B924 調用 1b8d4

繼續重復上方的步驟,按 x 查看 1BEC4 誰調用的,如下圖只有一個位置對1BEC4進行了調用,然后雙擊它進入函數

然后特征就來了,如下調用1BEC4的函數叫做init_proc,現在的調用棧 init_proc 調用 1BEC4 調用 1B924 調用 1b8d4

然后 init_proc 不懂沒關系,給ai大模型,讓它解釋,如下圖ai大模型的結束,很詳細,所以 1b8d4 它必然是檢測frida的相關函數,然后 1b8d4 里面只做了暫停操作,沒有關閉frida的操作,所以調用 1b8d4 函數的1B924 函數才是檢測frida的函數,接下來只需要把 1B924 函數進行hook就可以過檢測了

hook代碼,注意 var 構造函數調用偏移 這個值,可能每個手機不一樣,它的找法繼續往下看,寫后面了

// 主函數:根據IDA中獲取的地址信息,替換目標SO中的指定函數
function 根據IDA地址替換函數() {// 1. 獲取linker64模塊的基地址// 來源:linker64是安卓系統自帶的64位動態鏈接器(負責加載所有SO文件),固定名稱為"linker64"// 作用:基地址是模塊加載到內存的起始位置,類似"小區大門的地址"var 鏈接器64基地址 = Module.getBaseAddress("linker64")// 打印基地址用于調試(實際值會隨系統/進程變化,例如0x7f8a0000)console.log("linker64模塊基地址(內存起始位置):", 鏈接器64基地址)// 2. 定義call_constructors函數的偏移量// 來源:這個值必須從IDA中獲取!步驟是://   a. 用IDA打開目標設備的linker64文件(通常在/system/bin/linker64)//   b. 搜索函數名"call_constructors"//   c. 記錄該函數相對于linker64基地址的偏移(例如0x50C00)// 作用:偏移量是函數在模塊內部的位置,類似"小區內某棟樓的門牌號"var 構造函數調用偏移 = 0x50C00  // 這里必須替換為你從IDA中看到的實際值!// 3. 計算call_constructors函數的實際內存地址// 公式:實際地址 = 模塊基地址 + 偏移量(類似"小區大門地址 + 門牌號 = 具體住戶地址")// 例如:基地址0x7f8a0000 + 偏移0x50C00 = 實際地址0x7f8f0C00var 構造函數調用地址 = 鏈接器64基地址.add(構造函數調用偏移)console.log("call_constructors函數實際地址:", 構造函數調用地址)// 4. 攔截call_constructors函數// 原因:這個函數是linker64加載SO文件時,用于初始化SO中構造函數的關鍵函數// 目的:在目標SO(libmsaoaidsec.so)加載完成并初始化時,及時執行替換操作var 攔截器 = Interceptor.attach(構造函數調用地址, {// 當call_constructors函數被調用時(即有SO正在初始化),執行以下代碼onEnter: function (參數列表) {console.log("檢測到SO文件正在初始化(進入call_constructors函數)")// 5. 檢查目標SO是否已加載// 來源:"libmsaoaidsec.so"是你要操作的目標SO文件名(需替換為你的實際SO名)// 作用:確認我們要修改的SO已經被系統加載到內存中var 目標模塊 = Process.findModuleByName("libmsaoaidsec.so")// 6. 如果目標SO已加載,則執行替換if (目標模塊 != null) {console.log("目標SO已加載:" + 目標模塊.name + ",基地址:" + 目標模塊.base)// 7. 替換目標SO中的指定函數// ① 目標函數地址計算://    來源:0x1B924是從IDA中獲取的目標函數偏移量,步驟://      a. 用IDA打開libmsaoaidsec.so//      b. 找到你要替換的函數(例如sub_1B924)//      c. 記錄該函數相對于SO基地址的偏移//    公式:目標函數實際地址 = 目標SO基地址 + 偏移量// ② 新函數定義://    返回值類型"void"和參數列表[]必須與原函數一致(從IDA中查看函數原型獲取)Interceptor.replace(目標模塊.base.add(0x1B924),  // 目標函數的實際內存地址new NativeCallback(function () {  // 替換后的新函數console.log("目標函數(偏移0x1B924)已被成功替換!")// 這里可以添加自定義邏輯,例如:// - 返回固定值(如return 0;)// - 修改原函數參數(需在參數列表中定義)// - 調用原函數后修改返回值}, "void",  // 新函數返回值類型(必須與原函數一致,從IDA中查)[]       // 新函數參數列表(必須與原函數一致,從IDA中查)))// 8. 替換完成后解除攔截// 原因:避免后續加載其他SO時重復執行替換操作攔截器.detach()console.log("已完成替換,解除對call_constructors的攔截")}},// 函數執行結束時的操作(這里不需要,留空)onLeave: function (返回值) {}})
}// 執行主函數,啟動整個替換流程
根據IDA地址替換函數()

如下圖成功繞過檢測,app正常啟動

然后 構造函數調用偏移 值的找法,把下圖紅框的文件,使用 adb pull 下載到電腦上

adb pull /system/bin/linker64 xxxxx

上方的指令執行完后,如下圖,就把 linker64 文件下載到電腦上了

然后把它拖到ida中,然后搜索 constructor,下圖紅框的就是我們要找的,

這個函數的地址50C00

不分析的方式過檢測,直接把 1c544、1b8d4、 26e5c 這三個線程全返回空

// 定義一個函數,用于將指定地址的代碼替換為"直接返回"
// 作用:讓目標函數被調用時直接退出,不執行原來的邏輯
function nop_addr(addr) {// 第一步:修改內存權限為"可讀可寫可執行"(rwx)// 原因:默認情況下代碼段可能沒有寫權限,無法修改指令// 參數說明:// - addr:要修改的內存地址// - 4:修改的內存大小(4字節,足夠存放一條返回指令)// - 'rwx':新的權限(read/write/execute)Memory.protect(addr, 4 , 'rwx');// 第二步:創建一個Arm64架構的指令寫入器// 注意:這里假設目標設備是64位ARM架構(手機幾乎都是ARM)var w = new Arm64Writer(addr);// 第三步:寫入"返回指令"(ret)// 效果:當程序執行到這里時,會直接退出當前函數,不執行后續代碼w.putRet();// 第四步:刷新寫入的指令(確保生效)w.flush();// 第五步:釋放寫入器資源(避免內存泄漏)w.dispose();
}// 定義主函數:Hook動態鏈接器的構造函數調用流程,監控目標SO加載
function hook_call_constructors() {// 聲明變量:用于存儲動態鏈接器(linker)的模塊信息let linker = null;// 判斷當前進程是32位還是64位// Process.pointerSize是指針大小:32位系統為4字節,64位為8字節if (Process.pointerSize === 4) {// 32位系統的動態鏈接器名為"linker"linker = Process.findModuleByName("linker");} else {// 64位系統的動態鏈接器名為"linker64"(大部分現代手機是64位)linker = Process.findModuleByName("linker64");}// 聲明變量:存儲找到的關鍵函數地址// call_constructors_addr:SO初始化函數的地址// get_soname:獲取SO文件名的函數(這里未實際使用)let call_constructors_addr, get_soname;// 枚舉動態鏈接器模塊中的所有符號(符號=函數名/變量名+地址)// 作用:從鏈接器中找到我們需要監控的函數let symbols = linker.enumerateSymbols();// 遍歷所有符號,篩選出需要的函數for (let index = 0; index < symbols.length; index++) {let symbol = symbols[index];// 匹配"__dl__ZN6soinfo17call_constructorsEv"符號// 這個符號對應的函數是:動態鏈接器加載SO時,執行SO內部構造函數的入口// 來源:安卓系統動態鏈接器的標準符號,可通過符號表查詢到if (symbol.name === "__dl__ZN6soinfo17call_constructorsEv") {call_constructors_addr = symbol.address; // 記錄這個函數的內存地址} // 匹配"__dl__ZNK6soinfo10get_sonameEv"符號(可選,備用)// 作用:通過這個函數可以獲取當前正在加載的SO的文件名else if (symbol.name === "__dl__ZNK6soinfo10get_sonameEv") {// 將函數地址包裝為NativeFunction,方便后續調用// 參數說明:// - symbol.address:函數的內存地址// - "pointer":函數返回值類型(返回SO文件名的字符串地址)// - ["pointer"]:函數參數(傳入soinfo結構體的指針)get_soname = new NativeFunction(symbol.address, "pointer", ["pointer"]);}}// 打印找到的call_constructors函數地址(調試用,確認是否找到)console.log("call_constructors函數地址:", call_constructors_addr);// 攔截call_constructors函數:監控所有SO的初始化過程var listener = Interceptor.attach(call_constructors_addr, {// 當call_constructors函數被調用時觸發(有SO正在加載初始化)onEnter: function (args) {console.log("檢測到SO文件正在初始化(進入call_constructors)");// 檢查我們關注的目標SO(libmsaoaidsec.so)是否已加載// 注意:這里的SO文件名需要替換為你實際要操作的SO名稱var module = Process.findModuleByName("libmsaoaidsec.so");// 如果目標SO已經加載到內存中if (module != null) {console.log("找到目標SO:" + module.name + ",基地址:" + module.base);// 對目標SO中的三個關鍵函數執行"替換為返回"操作// 0x1c544、0x1b8d4、0x26e5c是函數在SO中的偏移量// 偏移量來源:通過IDA/ Ghidra等反編譯工具分析目標SO得到// 計算實際地址公式:實際地址 = SO基地址(module.base) + 偏移量nop_addr(module.base.add(0x1c544)); // 處理第一個函數console.log("0x1c544: 已替換為返回指令(函數被跳過)");nop_addr(module.base.add(0x1b8d4)); // 處理第二個函數console.log("0x1b8d4: 已替換為返回指令(函數被跳過)");nop_addr(module.base.add(0x26e5c)); // 處理第三個函數console.log("0x26e5c: 已替換為返回指令(函數被跳過)");// 完成替換后,解除對call_constructors的攔截// 原因:避免后續加載其他SO時重復執行替換操作listener.detach();console.log("所有目標函數處理完畢,已解除攔截");}},// 函數執行結束時的操作(這里不需要處理,留空)onLeave: function (retval) {}});
}// 執行主函數,啟動整個Hook流程
hook_call_constructors();

img

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

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

相關文章

安卓調javaScript Not find method “forceLogout“ implementatidsignature or namesp

核對一下是否實現對應的javaScript或者javaScript的方法參數對不對&#xff0c; 在這里插入圖片描述我這里一開始實現了這個方法但是沒有給參數&#xff0c;一直報異常&#xff0c;后臺說token沒給就查token的問題&#xff0c;最后發現是搞偏了&#xff0c;兩個原因&#xff0c…

【Linux網絡】:UDP(傳輸層協議)

目錄 一、鋪墊知識 1、傳輸層 2、端口號 2.1、五元組表示 一個進程通信 2.2、端口號范圍劃分 2.3、知名端口 2.4、查看端口號 2.5、問題 3、pidof & netstat 命令 ①netsate 命令 ②pidof命令 二、UDP協議 1、UDP協議格式 2、UDP報文 1.1、UDP數據封裝的過…

Effective C++ 條款19: 設計class猶如設計type

Effective C 條款19&#xff1a;設計class猶如設計type核心思想&#xff1a;設計新的class時&#xff0c;應當像語言設計者設計內置類型一樣慎重&#xff0c;考慮對象的創建、銷毀、初始化、拷貝、類型轉換等所有方面。 ?? 1. 類設計的關鍵問題域 對象生命周期管理&#xff1…

《匯編語言:基于X86處理器》第11章 MS-Windows編程(3)

本章展示的是如何用32 位Microsoft Windows API進行控制臺窗口編程。應用編程接口(API:ApplicationProgramming Interface)是類型、常數和函數的集合體&#xff0c;它提供了一種用計算機代碼操作對象的方式。本章將討論文本I/O、顏色選擇、時間與日期、數據文件I/O&#xff0c;…

在 macOS 上通過 Docker 部署DM8 (ARM 架構)

概述 達夢數據庫 (DM8) 無法直接在 Apple macOS 操作系統上原生安裝&#xff0c;通常需要通過虛擬機&#xff08;如 Parallels Desktop、VMware Fusion&#xff09;進行部署。另一種更輕量級且受 macOS 支持的方案是利用 Docker 容器技術來構建開發與測試環境。本文檔將詳細介…

網絡協議之路由是怎么回事?

寫在前面 要想去外面的世界看看, 就離不了路由器&#xff0c;而路由器工作的原理就是路由&#xff0c;那么具體是怎么路由的呢&#xff1f;本文就一起來看下這部分內容。 1&#xff1a;路由的配置 配置一條路由無非就是在配置以下三個信息&#xff1a; 1:包要去哪里&#x…

2106. 摘水果,梳理思路

文章目錄題目概要java 解法詳解題目概要 在一個無限的 x 坐標軸上&#xff0c;有許多水果分布在其中某些位置。給你一個二維整數數組 fruits &#xff0c;其中 fruits[i] [positioni, amounti] 表示共有 amounti 個水果放置在 positioni 上。fruits 已經按 positioni 升序排列…

深入理解消息隊列(MQ)核心原理與設計精髓

引言&#xff1a;從一個“不堪重負”的訂單系統說起想象一個簡化的電商下單流程&#xff1a;用戶點擊“下單”后&#xff0c;系統需要&#xff1a;在訂單數據庫中創建一條記錄。調用庫存服務&#xff0c;扣減商品庫存。調用營銷服務&#xff0c;給用戶發放積分和優惠券。調用通…

前端手撕題總結篇(算法篇——來自Leetcode牛客)

鏈表指定區域反轉 找到區間&#xff08;頭和為 for循環當**時&#xff09;->反轉鏈表&#xff08;返回反轉過后的頭和尾&#xff09;->連接 function reverseBetween( head , m , n ) {//preEnd&cur&nextStart cur.next斷開if(mn)return head;const vHeadNode…

從Excel到工時管理系統:企業如何選擇更高效的工時記錄工具?

還在為手工統計員工工時而頭疼嗎&#xff1f;月末堆積如山的Excel表格、反復核對的數據、層出不窮的差錯&#xff0c;這些問題正在拖慢企業的發展步伐。8Manage工時管理系統發現&#xff0c;傳統手工記錄不僅耗費大量人力&#xff0c;更讓寶貴的工時數據難以轉化為有效的管理決…

Java設計模式之《命令模式》

目錄 1、介紹 1.1、命令模式定義 1.2、對比 1.3、典型應用場景 2、命令模式的結構 2.1、組成部分&#xff1a; 2.2、整體流程 3、實現 3.1、沒有命令模式 3.2、命令模式寫法 4、命令模式的優缺點 前言 java設計模式分類&#xff1a; 1、介紹 1.1、命令模式定義 命…

【動態規劃算法】路徑問題

什么是動態規劃算法動態規劃&#xff08;Dynamic Programming&#xff0c;簡稱 DP&#xff09;是一種通過分解復雜問題為重疊子問題&#xff0c;并存儲子問題的解以避免重復計算&#xff0c;從而高效求解具有特定性質&#xff08;重疊子問題、最優子結構&#xff09;問題的算法…

Java基本技術講解

一、基礎語法三要素 暫時無法在飛書文檔外展示此內容 &#x1f511; 黃金法則?&#xff1a;每個變量都要聲明類型&#xff01;二、程序邏輯控制&#xff08;游戲行為核心&#xff09; 條件判斷&#xff1a;if-else - “岔路口選擇” // 撿到金幣邏輯 if (isTouching(Coin.clas…

【網絡基礎2】路由器的 “兩扇門”:二層接口和三層接口到底有啥不一樣?

目錄 前言:路由器不是只有 “插網線的口” 一、先搞懂一個基礎:路由器是 “網絡交通樞紐” 二、二層接口:“小區內部的單元門”,只認 “住戶身份證” 1. 啥是二層接口? 2. 用 “小區內部串門” 理解二層接口 步驟 1:手機打包數據,寫上 “收件人身份證” 步驟 2:二…

MLIR TableGen

簡介 TableGen 是一種領域特定語言&#xff08;DSL&#xff09;&#xff0c;TableGen 的設計目標是允許編寫靈活的描述&#xff0c;并將記錄的通用特性提取出來&#xff0c;從而減少重復代碼并提高代碼的可維護性。 TableGen的工作流程&#xff1a; 前端解析&#xff1a; Ta…

2、docker容器命令 | 信息查看

1、命令總覽命令作用docker ps查看運行中的容器&#xff08;-a查看所有容器&#xff09;docker logs [CONTAINER]查看容器日志&#xff08;-f實時追蹤日志&#xff09;docker inspect [CONTAINER]查看容器詳細信息&#xff08;JSON格式&#xff09;docker stats [CONTAINER]實時…

【MySQL】MySQL中鎖有哪些?

一、按照粒度分類&#xff1a; 粒度越小&#xff0c;并發度越高&#xff0c;鎖開銷越大。 1.全局鎖&#xff1a; 作用&#xff1a; 鎖定整個MySQL實例(所有數據庫)。適用場景&#xff1a; 全庫邏輯部分。(確保備份期間數據的一致性。)實現方式&#xff1a; 通過 FLUSH TABLES W…

語義分割--deeplabV3+

根據論文網絡結構圖講一下&#xff1a;網絡分為兩部分&#xff1a;encoder和decoder部分。 Encoder&#xff1a;DCNN就是主干網絡&#xff0c;例如resnet&#xff0c;Xception&#xff0c;MobileNet這些&#xff08;主干網絡也要使用空洞卷積&#xff09;&#xff0c;對dcnn的結…

Azure DevOps 中的代理

必知詞匯 深入研究 Azure DevOps 中的代理之前需要掌握的基本概念: 代理:Azure DevOps 中的代理是一個軟件組件,負責執行流水線中的任務和作業。這可能包括數據中心內的物理服務器、本地或云端托管的虛擬機,甚至是容器化環境。這些代理可以在各種操作系統和環境中運行,例如…

AUTOSAR進階圖解==>AUTOSAR_SRS_ADCDriver

AUTOSAR ADC驅動詳解 基于AUTOSAR標準的ADC驅動模塊需求規范分析目錄 ADC驅動模塊概述 關鍵概念定義 ADC驅動架構 ADC驅動在AUTOSAR分層架構中的位置ADC驅動的主要職責 ADC驅動配置結構 通用配置(AdcGeneral)硬件單元配置(AdcHwUnit)通道配置(AdcChannel)通道組配置(AdcChanne…