構造超小程序

文章目錄

  • 構造超小程序
    • 1 編譯器-大小優化
    • 2 編譯器-移除 C++ 異常
    • 3 鏈接器-移除所有依賴庫
    • 4 移除所有函數依賴
      • _RTC_InitBase() _RTC_Shutdown()
      • __security_cookie __security_check_cookie()
      • __chkstk()
    • 5 鏈接器-移除清單文件
    • 6 鏈接器-移除調試信息
    • 7 鏈接器-關閉隨機基址
    • 8 移除異常目錄
    • 9 小結
  • 附錄
    • 附錄1 超小 Hello world 程序

構造超小程序

為了更方便查看編譯結果的大小, 可以在 項目屬性頁>配置屬性>生成事件>生成后事件>命令行 添加

powershell -Command "Write-Output 目標大小:%24((Get-Item '$(TargetPath)').Length)"

鏈接器警告 LINK : 已指定 /LTCG,但不需要生成代碼;從鏈接命令行中移除 /LTCG 以提高鏈接器性能
可以到 項目屬性頁>配置屬性>鏈接器>優化>鏈接時間代碼生成 切換 默認配置 來關閉警告

從一個打印 “Hello world!” 的程序開始

#include <print>int main() {std::println("Hello world!");
}

Release 下構建結果大小: 18432B

1 編譯器-大小優化

  • 項目屬性頁>配置屬性>C/C++>優化>優化 選 最大優化(優選大小) (/O1)
  • 項目屬性頁>配置屬性>C/C++>優化>優選大小或速度 選 代碼大小優先 (/Os)

2 編譯器-移除 C++ 異常

通知編譯器禁用 C++ 異常

  • 項目屬性頁>配置屬性>C/C++>代碼生成>啟用C++異常 選 否 (移除 /EH)

通知 C/C++ 庫不使用 C++ 異常

  • 項目屬性頁>配置屬性>C/C++>預處理器>預處理器定義 添加 _HAS_EXCEPTIONS=0

3 鏈接器-移除所有依賴庫

  • 項目屬性頁>配置屬性>鏈接器>輸入>附加依賴項 清空, 手動輸入下面要依賴的 kernel32.dll
  • 項目屬性頁>配置屬性>鏈接器>輸入>忽略所有默認庫 選 是 (/NODEFAULTLIB)

只在代碼里用 pragma 添加 /NODEFAULTLIB 并不夠, 默認情況下新項目會通過附加依賴項直接指名鏈接庫, 這些庫不是通過選項 /DEFAULTLIB 附加的, 用 /NODEFAULTLIB 不能消除依賴

此時鏈接會報錯, 下面來解決鏈接錯誤

4 移除所有函數依賴

std::println() 函數依賴 ucrtbase.dll 中的函數, CRT 庫相關代碼和依賴比較龐大

  • 改用 Windows API WriteConsole 函數 來輸出

所用到的函數可以依賴 kernel32.dll

#include <Windows.h>int __stdcall mainCRTStartup(void* teb) {HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE);WriteConsoleA(output, "Hello world!\n", 13, NULL, NULL);return 0;
}

現在 Release 大小: 3584B
用 Denpendencies 可以看到導入符號列表現在變得非常干凈

_RTC_InitBase() _RTC_Shutdown()

Debug 下默認會開啟基本運行時檢查, 引入 _RTC_InitBase() 和 _RTC_Shutdown() 兩個函數依賴

  • 項目屬性頁>配置屬性>C/C++>代碼生成>基本運行時檢查 選 默認值 (移除 /RTC)

通常這兩個函數隨 msvcrt.lib 鏈接進入程序

__security_cookie __security_check_cookie()

部分函數尾部會被插入 cookie 檢查函數, 引入 __security_check_cookie() 函數和 __security_cookie 變量依賴

  • 項目屬性頁>配置屬性>C/C++>代碼生成>安全檢查 選 禁用安全檢查 (/GS-)

通常這兩個函數和變量隨 msvcrt.lib 鏈接進入程序, 其中 __security_cookie 定義于 gs_cookie.c 中

__chkstk()

當棧空間占用可能超過 8KB 時(包括局部變量和 _alloca() 調用), 會引入 __chkstk() 函數依賴, 用于提交棧空間
屬性頁中沒有相關配置開關, 需要手動填寫選項來控制這個棧空間大小閾值

  • 項目屬性頁>配置屬性>C/C++代碼生成>命令行 填 /GsN 其中N是足夠大的值

通常該函數在 kernelbase.dll 中導出

5 鏈接器-移除清單文件

清單文件用于聲明系統本程序在啟動時請求的資源, 包括請求管理員權限, WIndows版本兼容性, 高 DPI 聲明, 視覺主題等, 但現在不需要

  • 項目屬性頁>配置屬性>鏈接器>清單文件>生成清單 選 否 (/MANIFEST:NO)

.rsrc 節 將被移除

6 鏈接器-移除調試信息

調試信息用于幫助編譯器定位每段機器碼在源碼文件中的位置, 移除將導致程序無法在源碼中設置斷點

  • 項目屬性頁>配置屬性>鏈接器>調試>生成調試信息 選 否 (移除 /DEBUG)

用 /NOCOFFGRPINFO 移除調試目錄

  • 項目屬性頁>配置屬性>鏈接器>命令行 添加 /NOCOFFGRPINFO

7 鏈接器-關閉隨機基址

一些防御技術依賴于隨機基址, 關閉后可能導致程序更容易被攻擊, 不要在生產環境關閉隨機基址
關閉隨機基址使得程序默認加載到 0x140000000 處, 可用 /BASE 改變默認基址

  • 項目屬性頁>配置屬性>鏈接器>高級>固定基址 選 是 (/FIXED)
  • 項目屬性頁>配置屬性>鏈接器>高級>隨機基址 選 否 (/DYNAMICBASE:NO)

Debug 下的 .reloc 節 將被移除, 而 Release 下 .reloc 節本身就被優化合并了

8 移除異常目錄

異常目錄即 .pdata 節, 可指定當程序跑在某個函數崩潰后,有相對應的異常處理函數可供調用
移除并不會影響程序的正常運行
這篇 Stackoverflow 的回答指出異常目錄是強制生成的, 但可以用 CFF Explorer 手動移除異常目錄

9 小結

至此我們得到了一個徹底剝離所有基礎設施的開發環境
程序大小從 18KB 縮小到 2KB 左右

想問更小的程序? 有的,兄弟😆
本文附錄 1 給出一個超小程序, 在只使用 MSVC 工具并且不使用十六進制編輯器的前提下做到了 480B 的大小

附錄

附錄1 超小 Hello world 程序

C/C++ 生成的代碼太長了, 用匯編吧

code
mainCRTStartup proc             ; rcx = PEB; rax = mainCRTStartup; r10, rdx, r8, r9  填函數 1~4 參數; rsp+28h ~ rsp+50h 填函數 5~9 參數mov byte ptr [rsp+38h],14   ; rsp+38h = Length        = 14mov ax,0008hmov qword ptr [rsp+30h],rax ; rsp+30h = Buffer        = "Hello world\n" 覆蓋返回地址mov dword ptr [rsp+28h],eax ; rsp+28h = IoStatusBlock = Buffermov r10,qword ptr [rcx+20h]mov r10,qword ptr [r10+28h] ;     r10 = FileHandle    = Peb->ProcessParameter->StandardOutputxor edx,edx                 ;     rdx = Event         = NULLsyscall                     ;      ax = NtWriteFile   = 8ret
mainCRTStartup endp
end

用 /SECTION 先申請一個具有讀, 寫, 執行的全能節
然后用 /MERGE 將所有節包括代碼節和數據節合并

/SECTION:.all,ERW /MERGE:.text=.all /MERGE:.data=.all /MERGE:.rdata=.all

用 /ALIGN 調整節的對齊大小, 默認值 512B 會導致節的尾部留下大量空白, 最小可設為 16 (只有 1 個節時才能非 512B 對齊加載)

/ALIGN:16

用 /BASE 選項設置基址到 0x80000000 用 32 位地址, 方便使用 32 指令節約代碼大小

/BASE:0x80000000

新建一個 stub.txt 做 DOS 存根程序, 用 /STUB 使 stub.txt 替換默認的 DOS 頭

/STUB:stub.txt

stub.txt 輸入以下內容, 順便在這里存放要輸出的字符串

MZ234567Hello world!
012345678901234567890123456789012345678901

NtWriteFile 的 服務號 為 8, 將字符串設置從偏移 8 開始, 方便復用 rax 寄存器
文件以 MZ 起頭, 用多余字符填充到剛好 64B 大小來滿足鏈接器對存根程序的要求

程序大小 480B

用十六進制編輯器修改 PE 頭還可以讓程序更小, 懶得折騰了🐳

程序的PE頭其實大部分字段都沒有實際功能, 將程序的幾個頭部進行重疊可以得到更小的程序
Tiny PE 詳細討論了最小程序的構造方法, 得到了 133B 的程序, 文章還提到 97B 的程序, 但鏈接失效
只是現在 Windows 10 上加載器允許的最小程序大小是 268B
TinyPE on Win10: 268B 消息彈窗
runcalc.asm: 268B 啟動附件計算器
smallEXE 收集了一些超小的程序
微軟收錄的文章 里也有關于最小程序的討論
snake-qr: 2953B 貪吃蛇

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

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

相關文章

大語言模型開發框架——LangChain

什么是LangChain LangChain是一個開發由語言模型驅動的應用程序的框架&#xff0c;它提供了一套工具、組件和接口&#xff0c;可以簡化構建高級語言模型應用程序的過程。利用LangChain可以使應用程序具備兩個能力&#xff1a; 上下文感知 將語言模型與上下文&#xff08;提示…

自動化釋放linux服務器內存腳本

腳本說明 使用Linux的Cron定時任務結合Shell腳本來實現自動化的內存釋放。 腳本用到sync系統命令 sync的作用&#xff1a;sync 是一個 Linux 系統命令&#xff0c;用于將文件系統緩存中的數據強制寫入磁盤。 在你執行reboot、poweroff、shutdown命令時&#xff0c;系統會默認執…

Python Websockets庫深度解析:構建高效的實時Web應用

引言 在現代Web開發中&#xff0c;實時通信已經成為許多應用的核心需求。無論是聊天應用、在線游戲、金融交易平臺還是協作工具&#xff0c;都需要服務器和客戶端之間建立持久、雙向的通信通道。傳統的HTTP協議由于其請求-響應模式&#xff0c;無法有效滿足這些實時交互需求。…

【實用技巧】電腦重裝后的Office下載和設置

寫在前面&#xff1a;本博客僅作記錄學習之用&#xff0c;部分圖片來自網絡&#xff0c;如需引用請注明出處&#xff0c;同時如有侵犯您的權益&#xff0c;請聯系刪除&#xff01; 文章目錄 前言下載設置總結互動致謝參考目錄導航 前言 在數字化辦公時代&#xff0c;Windows和…

Node.js 技術原理分析系列 —— Node.js 調試能力分析

Node.js 技術原理分析系列 —— Node.js 調試能力分析 Node.js 作為一個強大的 JavaScript 運行時環境,提供了豐富的調試能力,幫助開發者診斷和解決應用程序中的問題。本文將深入分析 Node.js 的調試原理和各種調試技術。 1. Node.js 調試原理 1.1 V8 調試器集成 Node.js…

【圖論】最短路徑問題總結

一圖勝千言 單源最短路徑 正權值 樸素Dijkstra dijkstra算法思想是維護一個永久集合U&#xff0c;全部點集合V。 循環n -1次 從源點開始&#xff0c;在未被訪問的節點中&#xff0c;選擇距離源點最近的節點 t。 以節點 t 為中間節點&#xff0c;更新從起點到其他節點的最短…

【最佳實踐】win11使用hyper-v安裝ubuntu 22/centos,并配置固定ip,掃坑記錄

文章目錄 場景查看本機的win11版本啟用hyper-vhyper-v安裝ubuntu22虛擬機1.準備好個人的 iso文件。2. hyper-v 快速創建3.編輯設置分配內存自定義磁盤位置設置磁盤大小連接網絡修改虛擬機名稱自定義檢查點位置 和智能分頁件位置虛擬機第一次連接給ubuntu22配置固定ip遇到過的坑…

自然語言處理(25:(終章Attention 1.)Attention的結構?)

系列文章目錄 終章 1&#xff1a;Attention的結構 終章 2&#xff1a;帶Attention的seq2seq的實現 終章 3&#xff1a;Attention的評價 終章 4&#xff1a;關于Attention的其他話題 終章 5&#xff1a;Attention的應用 目錄 系列文章目錄 前言 Attention的結構 一.seq…

Git 命令大全:通俗易懂的指南

Git 命令大全&#xff1a;通俗易懂的指南 Git 是一個功能強大且廣泛使用的版本控制系統。對于初學者來說&#xff0c;它可能看起來有些復雜&#xff0c;但了解一些常用的 Git 命令可以幫助你更好地管理代碼和協作開發。本文將介紹一些常用的 Git 命令&#xff0c;并解釋它們的…

基于yolov11的棉花品種分類檢測系統python源碼+pytorch模型+評估指標曲線+精美GUI界面

【算法介紹】 基于YOLOv11的棉花品種分類檢測系統是一種高效、準確的農作物品種識別工具。該系統利用YOLOv11深度學習模型&#xff0c;能夠實現對棉花主要品種&#xff0c;包括樹棉&#xff08;G. arboreum&#xff09;、海島棉&#xff08;G. barbadense&#xff09;、草棉&a…

論文:Generalized Category Discovery with Clustering Assignment Consistency

論文下載&#xff1a; https://arxiv.org/pdf/2310.19210 一、基本原理 該方法包括兩個階段:半監督表示學習和社區檢測。在半監督表示學習中&#xff0c;使用了監督對比損失來充分地推導標記信息。此外&#xff0c;由于對比學習方法與協同訓練假設一致&#xff0c;研究引入了…

Java高級JVM知識點記錄,內存結構,垃圾回收,類文件結構,類加載器

JVM是Java高級部分&#xff0c;深入理解程序的運行及原理&#xff0c;面試中也問的比較多。 JVM是Java程序運行的虛擬機環境&#xff0c;實現了“一次編寫&#xff0c;到處運行”。它負責將字節碼解釋或編譯為機器碼&#xff0c;管理內存和資源&#xff0c;并提供運行時環境&a…

MySQL 5.7 Online DDL 技術深度解析

14.13.1 在線DDL操作 索引操作主鍵操作列操作生成列操作外鍵操作表操作表空間操作分區操作 索引操作 下表概述了對索引操作的在線DDL支持情況。星號表示有附加信息、例外情況或依賴條件。有關詳細信息&#xff0c;請參閱語法和使用說明。 操作原地執行重建表允許并發DML僅修…

kafka 報錯消息太大解決方案 Broker: Message size too large

kafka-configs.sh --bootstrap-server localhost:9092 \ --alter --entity-type topics \ --entity-name sim_result_zy \ --add-config max.message.bytes10485880 學習營課程

HarmonyOS:ComposeTitleBar 組件自學指南

在日常的鴻蒙應用開發工作中&#xff0c;我們常常會面臨構建美觀且功能實用的用戶界面的挑戰。而標題欄作為應用界面的重要組成部分&#xff0c;它不僅承載著展示頁面關鍵信息的重任&#xff0c;還能為用戶提供便捷的操作入口。最近在參與的一個項目里&#xff0c;我就深深體會…

前端面試題之CSS中的box屬性

前幾天在面試中遇到面試官問了一個關于box的屬性面試題&#xff0c;平時都是直接AI沒有仔細去看過。來說說CSS中的常用box屬性&#xff1a; 1. box-sizing box-sizing 屬性定義了元素的寬度和高度是否包括內邊距&#xff08;padding&#xff09;和邊框&#xff08;border&…

前端開發時的內存泄漏問題

目錄 &#x1f50d; 什么是內存泄漏&#xff08;Memory Leak&#xff09;&#xff1f;&#x1f6a8; 常見的內存泄漏場景1?? 未清除的定時器&#xff08;setInterval / setTimeout&#xff09;2?? 全局變量&#xff08;變量未正確釋放&#xff09;3?? 事件監聽未清除4??…

Java 基礎-30-單例設計模式:懶漢式與餓漢式

在軟件開發中&#xff0c;單例設計模式&#xff08;Singleton Design Pattern&#xff09;是一種常用的設計模式&#xff0c;它確保一個類只有一個實例&#xff0c;并提供一個全局訪問點。這種模式通常用于管理共享資源&#xff08;如數據庫連接池、線程池等&#xff09;或需要…

為 MinIO AIStor 引入模型上下文協議(MCP)服務器

Anthropic 最近宣布的模型上下文協議 &#xff08;MCP&#xff09; 將改變我們與技術交互的方式。它允許自然語言通信替換許多任務的復雜命令行語法。不僅如此&#xff0c;語言模型還可以總結傳統工具的豐富輸出&#xff0c;并以人類可讀的形式呈現關鍵信息。MinIO 是世界領先的…

2023年12月電子學會青少年軟件編程四級考級真題—新“跳7”游戲

此題可點下方去處查看&#xff0c;支持在線編程&#xff0c;獲取源碼&#xff1a; 新“跳7”游戲_scratch_少兒編程題庫學習中心-嗨信奧https://www.hixinao.com/tiku/scratch/show-5109.html?_shareid3 程序演示可點擊下方查看&#xff0c;支持源碼查看&#xff1a;新“跳7…