.NET 7 的 AOT 到底能不能扛反編譯?

来源:https://www.cnblogs.com/huangxincheng/archive/2022/11/23/16917197.html
-Advertisement-
Play Games

一:背景 1.講故事 在B站,公眾號上發了一篇 AOT 的文章後,沒想到反響還是挺大的,都稱贊這個東西能抗反編譯,可以讓破解難度極大提高,可能有很多朋友對逆向不瞭解,以為用 ILSpy,Reflector,DnSpy 這些工具打不開就覺得很安全,其實不然,在 OllyDbg,IDA,WinDBG 這 ...


一:背景

1.講故事

在B站,公眾號上發了一篇 AOT 的文章後,沒想到反響還是挺大的,都稱贊這個東西能抗反編譯,可以讓破解難度極大提高,可能有很多朋友對逆向不瞭解,以為用 ILSpy,Reflector,DnSpy 這些工具打不開就覺得很安全,其實不然,在 OllyDbg,IDA,WinDBG 這些逆向工具面前一樣是裸奔。

既然大家都很感興趣,那這篇就和大家聊一聊。

二:幾個例子

1. 動態修改程式數據

修改程式數據在逆向中再正常不過了,由於目前的 AOT 只能發佈成 x64 ,這裡就用 WinDbg 做下演示,首先看下例子。


    internal class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                Console.WriteLine("hello world!");
                Thread.Sleep(1000);
            }
        }
    }

程式在不斷的輸出,接下來我們將 hello world 中的 world 給去掉,操作手法非常簡單,先記憶體搜索找到 hello world,然後修改 length=5 即可。


0:005> lm
start             end                 module name
00007ff7`95b70000 00007ff7`95e5d000   ConsoleApp1 C (private pdb symbols)  

0:005> s-u 00007ff7`95b70000 L?0x00007ff7`95e5d000 hello
00007ff7`95e1c41c  0068 0065 006c 006c 006f 0020 0077 006f  h.e.l.l.o. .w.o.

0:000> dp 00007ff795e1c41c-0x4 L1
00007ff7`95e1c418  00650068`0000000c

0:000> eq 00007ff7`95e1c418 00650068`00000005
0:000> g

2. 獲取程式托管堆

AOT 再怎麼牛,它終還是個托管程式,既然是托管程式自然就有托管堆,托管堆中就有所有的托管數據,玩過 SOS.dll 朋友應該知道,用 !eeheap -gc 就能把托管堆給顯示出來,比如下麵這樣。


0:022> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x000002414D891030
generation 1 starts at 0x000002414D891018
generation 2 starts at 0x000002414D891000
ephemeral segment allocation context: none
         segment             begin         allocated         committed    allocated size    committed size
000002414D890000  000002414D891000  000002414D8D1FE8  000002414D8D2000  0x40fe8(266216)  0x41000(266240)
Large object heap starts at 0x000002415D891000
         segment             begin         allocated         committed    allocated size    committed size
000002415D890000  000002415D891000  000002415D891018  000002415D892000  0x18(24)  0x1000(4096)
Pinned object heap starts at 0x0000024165891000
0000024165890000  0000024165891000  0000024165899C10  00000241658A2000  0x8c10(35856)  0x11000(69632)
Total Allocated Size:              Size: 0x49c10 (302096) bytes.
Total Committed Size:              Size: 0x42000 (270336) bytes.
------------------------------
GC Allocated Heap Size:    Size: 0x49c10 (302096) bytes.
GC Committed Heap Size:    Size: 0x42000 (270336) bytes.

雖然目前的 AOT 不支持 SOS 擴展,無法顯示出托管堆,但一點關係都沒有,SOS 是通過 DataAccess 去挖的,大不來我手工挖一下就好了哈,接下來就是怎麼挖的問題了,熟悉 CLR 的朋友應該知道所謂的托管堆在內部用的是 generation_table[] 一維數據來維護的,以 的方式來劃分,代的落地是用 heap_segment 來表示的, 參考代碼如下:


generation gc_heap::generation_table [total_generation_count];

enum gc_generation_num
{
    // small object heap includes generations [0-2], which are "generations" in the general sense.
    soh_gen0 = 0,
    soh_gen1 = 1,
    soh_gen2 = 2,
    max_generation = soh_gen2,

    // large object heap, technically not a generation, but it is convenient to represent it as such
    loh_generation = 3,

    // pinned heap, a separate generation for the same reasons as loh
    poh_generation = 4,

    uoh_start_generation = loh_generation,

    // number of ephemeral generations
    ephemeral_generation_count = max_generation,

    // number of all generations
    total_generation_count = poh_generation + 1
};

接下來用 x 命令看下數組內容,代碼如下:


0:000> x ConsoleApp1!WKS::gc_heap::generation_table
00007ff7`95e25010 ConsoleApp1!WKS::gc_heap::generation_table = class WKS::generation [5]

0:000> dx -r1 (*((ConsoleApp1!WKS::generation (*)[5])0x7ff795e25010))
(*((ConsoleApp1!WKS::generation (*)[5])0x7ff795e25010))                 [Type: WKS::generation [5]]
    [0]              [Type: WKS::generation]
    ...
    [4]              [Type: WKS::generation]

0:000> dx -r1 (*((ConsoleApp1!WKS::generation *)0x7ff795e25010))
(*((ConsoleApp1!WKS::generation *)0x7ff795e25010))                 [Type: WKS::generation]
    [+0x038] start_segment    : 0x25100000000 [Type: WKS::heap_segment *]
    [+0x040] allocation_start : 0x25100001030 : 0x38 [Type: unsigned char *]
    [+0x048] allocation_segment : 0x25100000000 [Type: WKS::heap_segment *]
    [+0x0d0] allocation_size  : 0x0 [Type: unsigned __int64]
    [+0x100] gen_num          : 0 [Type: int]
    ...

0:000> dx -r1 ((ConsoleApp1!WKS::heap_segment *)0x25100000000)
((ConsoleApp1!WKS::heap_segment *)0x25100000000)                 : 0x25100000000 [Type: WKS::heap_segment *]
    [+0x000] allocated        : 0x25100001048 : 0x90 [Type: unsigned char *]
    [+0x008] committed        : 0x25100012000 : Unable to read memory at Address 0x25100012000 [Type: unsigned char *]
    [+0x010] reserved         : 0x25110000000 : 0x18 [Type: unsigned char *]
    [+0x018] used             : 0x25100009fe0 : 0x0 [Type: unsigned char *]
    [+0x020] mem              : 0x25100001000 : 0x38 [Type: unsigned char *]
    [+0x028] flags            : 0x0 [Type: unsigned __int64]
    [+0x030] next             : 0x0 [Type: WKS::heap_segment *]
    ...

上面的這些欄位就描述出了 !eeheap -gc 的結果,接下來想挖什麼,提取什麼我就不過多介紹了。

3. 提取托管線程列表

提取 托管線程 列表也是非常重要的, 它能指示出很多信息,一般用 !t 命令就能顯示,輸出如下:


0:022> !t
ThreadCount:      17
UnstartedThread:  0
BackgroundThread: 6
PendingThread:    0
DeadThread:       0
Hosted Runtime:   no
                                                                                                            Lock  
 DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception
   0    1     4128 000002414BDB8C70    2a020 Preemptive  000002414D8C6108:000002414D8C8000 000002414bdaf8f0 -00001 MTA 
   6    2     4458 000002414BDE5EB0    2b220 Preemptive  0000000000000000:0000000000000000 000002414bdaf8f0 -00001 MTA (Finalizer) 
   7    4     23e8 000002416DDB15C0  102b220 Preemptive  000002414D8C9250:000002414D8CA000 000002414bdaf8f0 -00001 MTA (Threadpool Worker) 
   ...
  20   17     50a8 000002416DE43DD0  102b220 Preemptive  000002414D8BC2D0:000002414D8BDFD0 000002414bdaf8f0 -00001 MTA (Threadpool Worker) 
  21   18     57d4 000002416DE628E0  8029220 Preemptive  000002414D8CC2A8:000002414D8CE000 000002414bdaf8f0 -00001 MTA (Threadpool Completion Port) 

既然目前的 SOS 不支持,同樣可以手工到 CLR 中去挖,熟悉的朋友應該知道 !t 的數據源來自於 ThreadStore::s_pThreadStore 下的 m_ThreadList 集合,它以鏈表的形式串聯了每個線程的 LinkPtr 欄位,但可惜的是,在 AOT 中,這一塊已經重寫了,由 g_pTheRuntimeInstance 全局變數下的 m_ThreadList 來維護了。

為了方便觀察,多生成幾個 Thread。


        static void Main(string[] args)
        {
            Debugger.Break();

            var tasks = Enumerable.Range(0, 10).Select(m => new Thread(() =>
            {
                Console.WriteLine($"tid={Thread.CurrentThread.ManagedThreadId} 已執行!");
                Console.ReadLine();
            }));

            foreach (var item in tasks)
            {
                item.Start();
            }

            Console.ReadLine();
        }

程式跑起來後,深挖 g_pTheRuntimeInstance 全局變數即可。


0:015> x ConsoleApp1!g_pTheRuntimeInstance
00007ff7`0155ee20 ConsoleApp1!g_pTheRuntimeInstance = 0x00000291`cb5b9300
0:015> dx -r1 ((ConsoleApp1!RuntimeInstance *)0x291cb5b9300)
((ConsoleApp1!RuntimeInstance *)0x291cb5b9300)                 : 0x291cb5b9300 [Type: RuntimeInstance *]
    [+0x000] m_pThreadStore   : 0x291cb5b9390 [Type: ThreadStore *]
    ...
0:015> dx -r1 ((ConsoleApp1!ThreadStore *)0x291cb5b9390)
((ConsoleApp1!ThreadStore *)0x291cb5b9390)                 : 0x291cb5b9390 [Type: ThreadStore *]
    [+0x000] m_ThreadList     [Type: SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> >]
    [+0x008] m_pRuntimeInstance : 0x291cb5b9300 [Type: RuntimeInstance *]
    [+0x010] m_Lock           [Type: ReaderWriterLock]
0:015> dx -r1 (*((ConsoleApp1!SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> > *)0x291cb5b9390))
(*((ConsoleApp1!SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> > *)0x291cb5b9390))                 [Type: SList<Thread,DefaultSListTraits<Thread,DoNothingFailFastPolicy> >]
    [+0x000] m_pHead          : 0x291ed366240 [Type: Thread *]
0:015> dx -r1 ((ConsoleApp1!Thread *)0x291ed366240)
((ConsoleApp1!Thread *)0x291ed366240)                 : 0x291ed366240 [Type: Thread *]
    ...
    [+0x058] m_pNext          : 0x291cb6aeb60 [Type: Thread *]
    [+0x060] m_hPalThread     : 0x204 [Type: void *]
    [+0x068] m_ppvHijackedReturnAddressLocation : 0x0 [Type: void * *]
    [+0x070] m_pvHijackedReturnAddress : 0x0 [Type: void *]
    [+0x078] m_uHijackedReturnValueFlags : 0x0 [Type: unsigned __int64]
    [+0x080] m_pExInfoStackHead : 0x0 [Type: ExInfo *]
    [+0x088] m_threadAbortException : 0x0 [Type: Object *]
    [+0x090] m_pThreadLocalModuleStatics : 0x291cb6aee90 [Type: void * *]
    [+0x098] m_numThreadLocalModuleStatics : 0x1 [Type: unsigned int]
    [+0x0a0] m_pGCFrameRegistrations : 0x0 [Type: GCFrameRegistration *]
    [+0x0a8] m_pStackLow      : 0xf754100000 [Type: void *]
    [+0x0b0] m_pStackHigh     : 0xf754200000 [Type: void *]
    [+0x0b8] m_pTEB           : 0xf7533ba000 : 0x0 [Type: unsigned char *]
    [+0x0c0] m_uPalThreadIdForLogging : 0x2044 [Type: unsigned __int64]
    [+0x0c8] m_threadId       [Type: EEThreadId]
    [+0x0d0] m_pThreadStressLog : 0x0 [Type: void *]
    [+0x0d8] m_interruptedContext : 0x0 [Type: _CONTEXT *]
    [+0x0e0] m_redirectionContextBuffer : 0x0 [Type: unsigned char *]
0:015> dx -r1 (*((ConsoleApp1!EEThreadId *)0x291ed366308))
(*((ConsoleApp1!EEThreadId *)0x291ed366308))                 [Type: EEThreadId]
    [+0x000] m_uiId           : 0x2044 [Type: unsigned __int64]

從CLR 的 Thread 維護的信息來看,這個結構體已經很小了,也說明 AOT 在Thread信息維護上做了很多的精簡。

三:總結

總的來說,AOT 確實能加速程式的初始啟動,一體化的打包機制也非常方便部署,但怎麼變終究還是一個托管程式,需要底層的 C++ 托著它,扛 反編譯 無從談起,所以防小人的話,該加殼的加殼,該混淆的混淆。

圖片名稱
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上篇文章談到BlockingQueue的使用場景,並重點分析了ArrayBlockingQueue的實現原理,瞭解到ArrayBlockingQueue底層是基於數組實現的阻塞隊列。 但是BlockingQueue的實現類中,有一種阻塞隊列比較特殊,就是SynchronousQueue(同步移交隊... ...
  • 1、const修飾變數 被const修飾過的變數相當於常量,它的值不能被賦值改變,在整個作用域內保持固定。所以說它定義的是只讀變數,在定義的時候需要給它賦初值。 1 const int a = 1; 2 a = 2; /*錯誤,常量的值不能改變*/ 3 const int a; /*錯誤,常量定義時 ...
  • 性能檢測分析方法 - 時間、空間衡量方法 Python 自帶模塊 import time 點擊查看代碼 # 僅僅是示範 time 模塊的用法,此段不能直接運行,運行請看測試項目源碼 import time def measure_runtime(func): time_start = time.ti ...
  • 我們寫東西的時候總會遇到lua中要調用java代碼,當然這個用JNI肯定是可以做到的,但是有更加方便的辦法—LuaJavaBridge。 一、luaj 主要特征 * 可以從 Lua 調用 Java Class Static Method * 調用 Java 方法時,支持 int/float/bool ...
  • Python安裝完成之後,我們的電腦都多出了哪些東西? 我們在電腦搜索框中搜索“python”,會顯示出python相關的程式。可以看到,我們的電腦會多出4個應用程式,如下: 接下來介紹下這4個程式的作用。 IDLE (Python 3.11 64-bit) IDLE是Python官方的集成開 ...
  • 這個問題是最近更新.NET 7 進行資料庫遷移發現的,操作步驟很簡單,先看一下SQL Server中的解決方法: 錯誤信息: 解決方案: 在登錄時,更改選項的連接屬性,解決方案、信任伺服器證書選項都選擇或者都不選擇,不能只選一個 這是我們SQL Server的寫法,在我們資料庫遷移中.NET 7 也 ...
  • 環境 伺服器:centos6.5 客戶端:Windows 前言 項目中有一個exe,安裝在客戶端,其中有一個功能是將本地產生的文件上傳至伺服器,這個功能是以服務的方式安裝在客戶端上。之前一切好使,文件能正常上傳到伺服器。但最近發現產生的文件沒有被上傳到伺服器,查看文件上傳服務的事件日誌提示密碼錯誤。 ...
  • ASP.NET Core是啥 ASP.NET Core 是一個跨平臺的高性能開源框架,用於生成啟用雲且連接 Internet 的新式應用。 ASP.NET Core 可以幹啥 生成 Web 應用和服務、物聯網 (IoT) 應用和移動後端。 在 Windows、macOS 和 Linux 上使用喜愛的 ...
一周排行
    -Advertisement-
    Play Games
  • 簡介 本文的初衷是希望幫助那些有其它平臺視覺演算法開發經驗的人能快速轉入Halcon平臺下,通過文中的示例開發者能快速瞭解一個Halcon項目開發的基本步驟,讓開發者能把精力完全集中到演算法的開發上面。 首先,你需要安裝Halcon,HALCON 18.11.0.1的安裝包會放在文章末尾。安裝包分開發和 ...
  • 本文是對Datawhale的動手學數據分析課程的學習總結,記錄了整體的學習過程、答案以及個人感想,代碼較為詳細。 ...
  • JZ7重建二叉樹 描述 給定節點數為 n 的二叉樹的前序遍歷和中序遍歷結果,請重建出該二叉樹並返回它的頭結點。 例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6} 提示: 1.vin.length == pre.length 2.pre 和 vin ...
  • 我們都知道在Java編程中多線程的同步使用synchronized關鍵字來標識,那麼這個關鍵字在JVM底層到底是如何實現的呢。 我們先來思考一下如果我們自己實現的一個鎖該怎麼做呢: 首先肯定要有個標記記錄對象是否已經上鎖,執行同步代碼之前判斷這個標誌,如果對象已經上鎖線程就阻塞等待鎖的釋放。 其次要 ...
  • 目錄 一.OpenGL 色階 1.Windows OpenGL ES 版本 2.Windows OpenGL 版本 二.OpenGL 色階 GLSL Shader 三.猜你喜歡 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 Ope ...
  • 1. 查看Linux伺服器版本信息 # cat /etc/redhat-release CentOS Linux release 7.4.1708 (Core) 2. 禪道開源版安裝包下載 wget http://dl.cnezsoft.com/zentao/9.8.2/ZenTaoPMS.9.8. ...
  • Spring 框架可以為 Java 應用程式開發提供全面的基礎設施支持,它是現在非常流行的 Java 開源框架,對於一個 Java 開發人員來說,熟練掌握 Spring 是必不可少的。 ...
  • 前言 本篇是c++總結的第二篇,關於c++的對象模型,在構造、拷貝虛函數上重點分析,也包含了c++11class的新用法和特性,如有不當,還請指教! c++三大特性 訪問許可權 ​ 在c++中通過public、protected、private三個關鍵字來控製成員變數和成員函數的訪問許可權,它們分別表示 ...
  • 一.小結 1.使用二維數組來存儲表格 2.可以使用以下語法來聲明二維數組變數: 元素類型[ ] [ ]數組變數 3.可以使用以下語法來創建二維數組變數: new 元素類型 [行的個數][列的個數] 4.使用下麵的語法表示二維數組中的每個元素: 數組變數[行下標][列的個數] 5.可使用數組初始化語法 ...
  • typimg是一款為typora編輯器提供圖像自定義上傳服務的工具,該工具將在typora中輸入的網路圖片、本地圖片、剪貼板圖片/截圖上傳到博客園,支持在MacOS、Windiws、Linux三個平臺上運行。 ...