Android12 User版本開啟adb root, adb remount, su, 關閉selinux

開啟adb root

直接看adb源碼:

  • __android_log_is_debuggable就是判斷ro.debuggable屬性值,感興趣可以在 源碼下grep下實現看看。
  • auth_required :在adb源碼下定義的全局變量,默認等于true,。看名字就是是否需要用戶授權的flag, 這里不再繼續跟代碼,只要知道這個值在ro.debuggable=1時,會等于ro.adb.secure這個屬性即可。我們不想開啟adb時彈出用戶授權,因此要將這個屬性改為0.
// /packages/modules/adb/daemon/main.cpp
int adbd_main(int server_port) {...#if defined(__ANDROID__)// If we're on userdebug/eng or the device is unlocked, permit no-authentication.bool device_unlocked = "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");if (__android_log_is_debuggable() || device_unlocked) {auth_required = android::base::GetBoolProperty("ro.adb.secure", false);}
#endif...
}

當在PC端執行adb root時,android系統會走到下面代碼,可以看到如果__android_log_is_debuggable不為true的話,就return了。
函數最后設置了service.adb.root=1,實際測試了下,adb root后再adb shell,
這個屬性=1,直接adb shell進去,這個屬性=0

// /packages/modules/adb/daemon/restart_service.cppvoid restart_root_service(unique_fd fd) {if (getuid() == 0) {WriteFdExactly(fd.get(), "adbd is already running as root\n");return;}if (!__android_log_is_debuggable()) {WriteFdExactly(fd.get(), "adbd cannot run as root in production builds\n");return;}LOG(INFO) << "adbd restarting as root";android::base::SetProperty("service.adb.root", "1");WriteFdExactly(fd.get(), "restarting adbd as root\n");
}

service.adb.root使用的地方:
PC端執行adb shell后,會走到下面代碼,在main函數里,會判斷should_drop_privileges,然后通過linux capability機制
給進程降低權限,感興趣可以搜一下linux capability機制。這里推測就是是否從root降低權限到shell用戶。

// packages/modules/adb/daemon/main.cpp
int adbd_main(int server_port) {
...
#if defined(__ANDROID__)drop_privileges(server_port);
#endif
...
}
static void drop_privileges(int server_port) {
...if (should_drop_privileges()) {const bool should_drop_caps = !__android_log_is_debuggable();if (should_drop_caps) {minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));}minijail_change_gid(jail.get(), AID_SHELL);minijail_change_uid(jail.get(), AID_SHELL);// minijail_enter() will abort if any priv-dropping step fails.minijail_enter(jail.get());// Whenever ambient capabilities are being used, minijail cannot// simultaneously drop the bounding capability set to just// CAP_SETUID|CAP_SETGID while clearing the inheritable, effective,// and permitted sets. So we need to do that in two steps.using ScopedCaps =std::unique_ptr<std::remove_pointer<cap_t>::type, std::function<void(cap_t)>>;ScopedCaps caps(cap_get_proc(), &cap_free);...
}

關鍵在should_drop_privileges:
ro.secure在user,userdebug版本=1,也就是說adb shell進來 默認是降權的,非root的。
如果**__android_log_is_debuggable && service.adb.root 為true**,就不會降權為非root. 前面說了PC端輸入adb root后, service.adb.root 這個值就會等于1,
綜合上面分析:只要把ro.debuggable設置為1,就可以在user版本下實現adb root

static bool should_drop_privileges() {// The properties that affect `adb root` and `adb unroot` are ro.secure and// ro.debuggable. In this context the names don't make the expected behavior// particularly obvious.//// ro.debuggable://   Allowed to become root, but not necessarily the default. Set to 1 on//   eng and userdebug builds.//// ro.secure://   Drop privileges by default. Set to 1 on userdebug and user builds.bool ro_secure = android::base::GetBoolProperty("ro.secure", true);bool ro_debuggable = __android_log_is_debuggable();// Drop privileges if ro.secure is set...bool drop = ro_secure;// ... except "adb root" lets you keep privileges in a debuggable build.std::string prop = android::base::GetProperty("service.adb.root", "");bool adb_root = (prop == "1");bool adb_unroot = (prop == "0");if (ro_debuggable && adb_root) {drop = false;}// ... and "adb unroot" lets you explicitly drop privileges.if (adb_unroot) {drop = true;}

具體的修改策略:
在編譯版本時,定義一個全局變量,可以把它定義在編譯腳本中,

export USE_ROOT_ADB =1

USE_ROOT_ADB 代表在user版本中啟用adb root

# /build/make/core/main.mk
## user/userdebug ##
# +++ 這里加一個編譯過程中對USE_ROOT_ADB的值的打印。
$(warning USE_ROOT_ADB $(USE_ROOT_ADB))
# user_variant:不是user,就是userdebug
user_variant := $(filter user userdebug,$(TARGET_BUILD_VARIANT))
enable_target_debugging := true
tags_to_install :=
ifneq (,$(user_variant))# Target is secure in user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=1ADDITIONAL_SYSTEM_PROPERTIES += security.perf_harden=1ifeq ($(user_variant),user)# +++ 如果是user版本,并且USE_ROOT_ADB=1,將ro.adb.secure設置為0ifeq ($(USE_ROOT_ADB),true)ADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=0elseADDITIONAL_SYSTEM_PROPERTIES += ro.adb.secure=1endifendififeq ($(user_variant),userdebug)# Pick up some extra useful toolstags_to_install += debugelse# +++ 如果是user版本,并且USE_ROOT_ADB=1,將tags_to_install 追加debug字段,# +++ 還有要注意,源碼未修改前,如果是user版本,enable_target_debugging會被置為空# +++ 這里改為:如果是user版本,enable_target_debugging依然是true.# @@@ 后面繼續分析tags_to_install 和 enable_target_debuggingifeq ($(USE_ROOT_ADB),true)# Pick up some extra useful toolstags_to_install += debugelse# Disable debugging in plain user builds.enable_target_debugging :=endifendif   # Disallow mock locations by default for user buildsADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=0else # !user_variant# Turn on checkjni for non-user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.kernel.android.checkjni=1# Set device insecure for non-user builds.ADDITIONAL_SYSTEM_PROPERTIES += ro.secure=0# Allow mock locations by default for non user buildsADDITIONAL_SYSTEM_PROPERTIES += ro.allow.mock.location=1
endif # !user_variant
# @@@ 如果enable_target_debugging為true,ro.debuggable=1
# @@@ 達到了我們想要的效果
ifeq (true,$(strip $(enable_target_debugging)))# Target is more debuggable and adbd is on by defaultADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=1# Enable Dalvik lock contention logging.ADDITIONAL_SYSTEM_PROPERTIES += dalvik.vm.lockprof.threshold=500
else # !enable_target_debugging# Target is less debuggable and adbd is off by defaultADDITIONAL_SYSTEM_PROPERTIES += ro.debuggable=0
endif # !enable_target_debugging

adb remount 和 su

這兩個放到一起說,上一節說到將tags_to_install 追加debug字段。
還是在/build/make/core/main.mk中,tags_to_install=true,編譯時會帶上PRODUCT_PACKAGES_DEBUG

 
# $(1): product makefile
define product-installed-files$(eval _pif_modules := \$(call get-product-var,$(1),PRODUCT_PACKAGES) \$(if $(filter eng,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_ENG)) \# @@@ 當tags_to_install有debug字段,編譯時會帶上PRODUCT_PACKAGES_DEBUG$(if $(filter debug,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG)) \$(if $(filter tests,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_TESTS)) \$(if $(filter asan,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_ASAN)) \$(if $(filter java_coverage,$(tags_to_install)),$(call get-product-var,$(1),PRODUCT_PACKAGES_DEBUG_JAVA_COVERAGE)) \$(call auto-included-modules) \) \

PRODUCT_PACKAGES_DEBUG定義在/build/make/target/product/base_system.mk, 當然也可以在自己的mk中對PRODUCT_PACKAGES_DEBUG進行追加。su 就在PRODUCT_PACKAGES_DEBUG中,如果再加上remount, 就實現了user版本下使用su和adb remount

383 # Packages included only for eng or userdebug builds, previously debug tagged
384 PRODUCT_PACKAGES_DEBUG := \
385     adb_keys \
386     arping \
387     dmuserd \
388     idlcli \
389     init-debug.rc \
390     iotop \
391     iperf3 \
392     iw \
393     layertracegenerator \
394     libclang_rt.ubsan_standalone \
395     logpersist.start \
396     logtagd.rc \
397     procrank \
398     profcollectd \
399     profcollectctl \
400     record_binder \
401     servicedispatcher \
402     showmap \
403     sqlite3 \
404     ss \
405     start_with_lockagent \
406     strace \# @@@ 已經有su
407     su \
408     sanitizer-status \
409     tracepath \
410     tracepath6 \
411     traceroute6 \
412     unwind_info \
413     unwind_reg_info \
414     unwind_symbols \# +++ 加上remountremount \

另外這里還涉及到selinux,
在system/sepolicy/private下,可以看到su.te中對su的定義:

permissive su;

su被定義為permissive domain,這是什么?
參考redhat上的定義:

Permissive Domains

When SELinux is running in permissive mode, SELinux does not deny access, but denials are logged for actions that would have been denied if running in enforcing mode. Previously, it was not possible to make a single domain permissive (remember: processes run in domains). In certain situations, this led to making the whole system permissive to troubleshoot issues.
Permissive domains allow an administrator to configure a single process (domain) to run permissive, rather than making the whole system permissive. SELinux checks are still performed for permissive domains; however, the kernel allows access and reports an AVC denial for situations where SELinux would have denied access.
Permissive domains have the following uses:
They can be used for making a single process (domain) run permissive to troubleshoot an issue without putting the entire system at risk by making it permissive.
They allow an administrator to create policies for new applications. Previously, it was recommended that a minimal policy be created, and then the entire machine put into permissive mode, so that the application could run, but SELinux denials still logged. audit2allow could then be used to help write the policy. This put the whole system at risk. With permissive domains, only the domain in the new policy can be marked permissive, without putting the whole system at risk.

理解就是可以在強制模式(enforcing mode)下,定義一個寬容模式的domain,跑在Permissive domain下的進程,只會打印avc log,而不會限制它的行為。su這么牛逼的進程,當然是Permissive domain。
但是在user版本下,是不允許定義Permissive domain的。 為此需要修改如下代碼:

# /system/sepolicy/Android.mk$(LOCAL_BUILT_MODULE): $(sepolicy.recovery.conf) $(HOST_OUT_EXECUTABLES)/checkpolicy \$(HOST_OUT_EXECUTABLES)/sepolicy-analyze@mkdir -p $(dir $@)$(hide) $(CHECKPOLICY_ASAN_OPTIONS) $(HOST_OUT_EXECUTABLES)/checkpolicy -M -c \$(POLICYVERS) -o $@.tmp $<$(hide) $(HOST_OUT_EXECUTABLES)/sepolicy-analyze $@.tmp permissive > $@.permissivedomains# +++ 下面的if表示如果是user版本,并且permissivedomains存在的話,就會輸出如下錯誤,并退出。因此需要用USE_ROOT_ADB宏來排除這部分處理。注意:該文件下有2處檢查permissivedomains 的地方,需要用同樣的方式注釋掉。
ifndef  USE_ROOT_ADB$(hide) if [ "$(TARGET_BUILD_VARIANT)" = "user" -a -s $@.permissivedomains ]; then \echo "==========" 1>&2; \echo "ERROR: permissive domains not allowed in user builds" 1>&2; \echo "List of invalid domains:" 1>&2; \cat $@.permissivedomains 1>&2; \exit 1; \fi
endif$(hide) mv $@.tmp $@

關閉selinux

臨時關閉selinux,只要設置setenforce 0即可,但重啟后就會恢復為強制模式。
這里討論永久關閉。
如果不想深究,直接修改IsEnforcing(),讓它永久返回false, 這樣走到security_setenforce,永久關閉selinux。

// /system/core/init/selinux.cppvoid SelinuxSetEnforcement() {bool kernel_enforcing = (security_getenforce() == 1);bool is_enforcing = IsEnforcing();if (kernel_enforcing != is_enforcing) {if (security_setenforce(is_enforcing)) {PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")<< ") failed";}}if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();}
}
bool IsEnforcing() {// MODIFY START 永久返回falsereturn false;// MODIFY ENDif (ALLOW_PERMISSIVE_SELINUX) {return StatusFromProperty() == SELINUX_ENFORCING;}return true;
}

如果想繼續看下是怎么實現的,我們繼續分析,先看security_getenforce函數,這里不貼代碼實現了,源碼在external/selinux下,主要就是讀取/sys/fs/selinux/enforce 的值。可以是1 or 0
/sys/fs/selinux/enforce中的值,又是根據內核的cmdline中的androidboot.selinux來的。可以在自己的mk中修改這個值:

BOARD_KERNEL_CMDLINE += androidboot.selinux=enforcing or permissive

再分析IsEnforcing函數,如果ALLOW_PERMISSIVE_SELINUX為false時,直接返回true。ALLOW_PERMISSIVE_SELINUX在mk中可以看到,如果是user版本,該值就是false, 這里可以看出,如果編譯user版本,一定會開啟selinux,即使上面配置內核啟動參數cmdline中的androidboot.selinux=permissive,也無濟于事。

#/system/core/init/Android.mk# userdebug,eng版本走if,user走else
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))init_options += \-DALLOW_FIRST_STAGE_CONSOLE=1 \-DALLOW_LOCAL_PROP_OVERRIDE=1 \-DALLOW_PERMISSIVE_SELINUX=1 \-DREBOOT_BOOTLOADER_ON_PANIC=1 \-DWORLD_WRITABLE_KMSG=1 \-DDUMP_ON_UMOUNT_FAILURE=1elseinit_options += \-DALLOW_FIRST_STAGE_CONSOLE=0 \-DALLOW_LOCAL_PROP_OVERRIDE=0 \-DALLOW_PERMISSIVE_SELINUX=0 \-DREBOOT_BOOTLOADER_ON_PANIC=0 \-DWORLD_WRITABLE_KMSG=0 \-DDUMP_ON_UMOUNT_FAILURE=0endif

試想,如果我們修改上面的mk,讓ALLOW_PERMISSIVE_SELINUX在user版本下也等于1,會不會也可以實現關閉selinux。繼續分析:
如果ALLOW_PERMISSIVE_SELINUX=1,IsEnforcing函數返回值就依賴
StatusFromProperty() == SELINUX_ENFORCING;
在android12之前,StatusFromProperty函數只讀取cmdline的androidboot.selinux
那么在android12之前,可以通過修改:
ALLOW_PERMISSIVE_SELINUX=1 并且 BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive
實現user版本永久關閉selinux。

從android12開始,StatusFromProperty函數還會判斷Bootconfig的androidboot.selinux值。內核cmdline和Bootconfig的androidboot.selinux值都等于enforcing時,StatusFromProperty會返回enforcing。
那么從android12開始,可以通過修改:
ALLOW_PERMISSIVE_SELINUX=1 并且 ( BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive 或者 BOARD_BOOTCONFIG += androidboot..selinux=permissive)
實現user版本永久關閉selinux。

EnforcingStatus StatusFromProperty() {EnforcingStatus status = SELINUX_ENFORCING;ImportKernelCmdline([&](const std::string& key, const std::string& value) {if (key == "androidboot.selinux" && value == "permissive") {status = SELINUX_PERMISSIVE;}});if (status == SELINUX_ENFORCING) {ImportBootconfig([&](const std::string& key, const std::string& value) {if (key == "androidboot.selinux" && value == "permissive") {status = SELINUX_PERMISSIVE;}});}return status;}

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

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

相關文章

金融專業高分簡歷撰寫指南

一、金融求職簡歷原則&#xff1a;深度與亮點并存在金融行業求職時&#xff0c;一份出色的簡歷需突出經歷深度與亮點。01 教育背景需如實填寫畢業院校、專業、GPA及所學課程。金融行業不少公司對求職者學校和學歷有嚴格標準&#xff0c;如“985”“211”院校或碩士以上學歷等。…

專題:2025生命科學與生物制藥全景報告:產業圖譜、投資方向及策略洞察|附130+份報告PDF、原數據表匯總下載

原文鏈接&#xff1a;https://tecdat.cn/?p43526 過去一年&#xff0c;全球生命科學VC融資回暖至1021.5億美元&#xff0c;并購交易雖下滑23%卻聚焦關鍵賽道&#xff0c;創新藥管線中GLP-1受體激動劑以170億美元市場規模領跑&#xff0c;AI技術將研發周期縮短60%……這些數據背…

Compose筆記(四十)--ClickableText

這一節主要了解一下Compose中的ClickableText&#xff0c;在Jetpack Compose中&#xff0c;ClickableText是用于創建可點擊文本的組件&#xff0c;其核心功能是通過聲明式語法將文本設置為交互式元素&#xff0c;用戶點擊時可觸發特定操作。簡單總結如下:API含義 text&#xff…

面試必刷的數組三連:原地刪除與合并

堅持用 清晰易懂的圖解 多語言代碼&#xff0c;讓每道題變得簡單&#xff01; 呆頭個人主頁詳情 呆頭個人Gitee代碼倉庫 呆頭詳細專欄系列 座右銘&#xff1a; “不患無位&#xff0c;患所以立。” 面試必刷的數組三連&#xff1a;原地刪除與合并前言目錄1.移除元素2.刪除有序…

力扣經典算法篇-41-旋轉圖像(輔助數組法,原地旋轉法)

1、題干 給定一個 n n 的二維矩陣 matrix 表示一個圖像。請你將圖像順時針旋轉 90 度。 你必須在 原地 旋轉圖像&#xff0c;這意味著你需要直接修改輸入的二維矩陣。請不要 使用另一個矩陣來旋轉圖像。 示例 1&#xff1a;輸入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]]…

譯|用戶增長策略如何使用因果機器學習的案例

來自上傳文件中的文章《[Causal Machine Learning for Growth: Loyalty Programs, LTV, and What to Do When You Can’t Experiment | by Torty Sivill | Towards AI]》 本文探討了當 A/B 測試不可行時&#xff0c;如何利用因果推斷從歷史數據中獲取洞察。技術亮點在于通過構建…

java~final關鍵字

final關鍵字final基本介紹final的使用細節final基本介紹 final是最終的意思&#xff0c;可以修飾類&#xff0c;屬性&#xff0c;方法&#xff0c;局部變量什么時候會要使用到final呢&#xff1f; 1.想要類不被繼承時 2.不希望類的某個屬性的值被改變時 3.不想父類的某個方法被…

Node.js(四)之數據庫與身份認證

數據庫與身份認證 目錄 數據庫與身份認證 十三、數據庫的基本概念 13.1 什么是數據庫 13.2 常見的數據庫及分類 13.3 傳統型數據庫的數據組織結構 1. Excel 的數據組織結構 2. 傳統型數據庫的數據組織結構 3. 實際開發中庫、表、行、字段的關系 十四、安裝并配置MySQ…

SpringBoot+SpringMVC常用注解

文章目錄發展歷程項目創建項目結構入門案例配置文件的兩種方式&#xff1a;只能使用一種創建項目二入門案例常用知識及注解Controller:類上面加&#xff0c;SpringMVC的注解GetMapping:方法上面加Spring框架的兩項核心功能Component:組件。控制反轉&#xff0c;加在業務類上面&…

標準GS相位恢復算法

標準GS相位恢復算法詳解與MATLAB實現 Gerchberg-Saxton (GS) 算法是一種經典的相位恢復方法&#xff0c;廣泛應用于光學成像、衍射成像和全息技術等領域。該算法通過迭代過程從未知相位的強度測量中恢復相位信息。 算法原理 GS算法的核心思想是利用傅里葉變換關系在空間域和頻率…

【Linux網絡編程基礎--socket地址API】

一、主機字節序和網絡字節序主機字節序&#xff08;Host Byte Order&#xff09;&#xff1a;你當前電腦的內存字節順序&#xff08;比如 x86 是小端&#xff09;網絡字節序&#xff08;Network Byte Order&#xff09;&#xff1a;統一規定為大端序&#xff08;高位字節在高位…

Linux路徑MTU發現(Path MTU Discovery, PMTU)

Linux路徑MTU發現&#xff08;Path MTU Discovery, PMTU&#xff09;機制是TCP/IP協議棧中確保數據包高效傳輸的核心技術。其核心目標是動態探測源主機到目的主機路徑上的最小MTU&#xff08;Maximum Transmission Unit&#xff09;&#xff0c;從而避免IP分片&#xff0c;提升…

【MySQL進階】------MySQL程序

MySQL程序簡介 MySQL安裝完成通常會包含如下程序&#xff1a; Linux系統程序?般在 /usr/bin?錄下&#xff0c;可以通過命令查看&#xff1a; windows系統?錄&#xff1a;你的安裝路徑\MySQL Server 8.0\bin&#xff0c;可以通過命令查看&#xff1a; 每個 MySQL 程序都有許…

Linux大頁內存導致服務內存不足

Linux大頁內存導致服務內存不足的解決方法 大頁內存&#xff08;Huge Pages&#xff09;是Linux內核提供的一種機制&#xff0c;用于減少TLB&#xff08;轉換后備緩沖區&#xff09;的壓力&#xff0c;提高內存訪問性能。然而&#xff0c;如果配置不當&#xff0c;大頁內存可能…

超寬帶測距+測角+無線通信一體化模組:智能門鎖、智能遙控器、AR頭戴、智能穿戴

超寬帶測距測角無線通信一體化模組&#xff1a;智能門鎖、智能遙控器、AR頭戴、智能穿戴UWB測距測角技術&#xff0c;因其高精度、低延遲、抗干擾能力&#xff0c;正廣泛應用于“人-物-設備”的空間感知場景&#xff0c;成為構建智能空間和精準互動的重要底層技術。代表廠商與產…

基于單片機空氣質量檢測/氣體檢測系統

傳送門 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目速選一覽表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品題目功能速覽 概述 隨著環境污染問題日益嚴重&#xff0c;空氣質量監測成為社會關注的焦點。基于單片機的空氣質量檢…

網絡安全 | 從 0 到 1 了解 WAF:Web 應用防火墻到底是什么?

&#x1f914; 寫在前面 2020年 我參加公司的安全技能大賽&#xff0c;隊友在實操環節啟用了 WAF 防火墻&#xff0c;這是我第一次接觸到 Web 應用防火墻。作為一個 Web 開發老鳥&#xff0c;真是羞愧呀&#x1f602;。 &#x1f510; Web應用防火墻 WAF 全稱是 Web Applica…

服務器突然之間特別卡,什么原因?

原因總結&#xff1a;1.一般是本地網速的問題&#xff0c;服務器網速的問題&#xff0c;服務器CPU被占滿的問題今天發現另一個會導致特別卡的問題&#xff0c;是主存占滿也會導致卡頓。解釋如下&#xff1a;當服務器的主存&#xff08;物理內存&#xff09;被完全占滿時&#x…

AI應用標準詳解:A2A MCP AG-UI

"OpenAI接入MCP&#xff0c;Google推出A2A&#xff0c;微軟與OpenAI緊密綁定"標志著云計算競爭焦點已從"算力"和"模型參數"轉向?Agent標準協議控制權?。在AI快速演進的今天&#xff0c;我們不再僅關注單個AI的智能水平&#xff0c;而是探索多個…

Web安全學習步驟

以下是Web安全專項學習步驟&#xff0c;聚焦實戰能力培養&#xff0c;分為4個階段資源清單**&#xff0c;適合從入門到進階。重點培養漏洞挖掘能力與防御方案設計雙重視角&#xff1a;---階段1&#xff1a;Web技術筑基&#xff08;1-2個月&#xff09; | 領域 | 關鍵…