【VS2015】新版CRT下的編譯環境污染

CRT的前世今生

CRT定義
CRT(C-RunTime)也稱運行時,對應的庫叫做運行時庫,提供 C 程序運行時必備的接口實現。舉個例子就是簡單HelloWorld需要使用printf,printf的具體實現就在運行時庫中,除此之外還包含很多其它常用的函數實現!當然運行時庫不止提供了函數實現,還會幫助程序找到入口點,出口點以及處理異常等

舊版CRT
爲什麼要區分舊版和新版的 CRT 呢?因爲微軟在發佈VS2015之前重構了整個CRT,所以 VS2015 之前的所有 VS 版本用的就稱爲舊版 CRT 了!舊版 CRT 有這幾個特點

  • 動態庫只有一個,每個版本的 VS 都只對應一個形如 msvcrxxx.dll 的動態庫,比如:VS2012 對應的動態庫爲msvcr110.dll,Debug 的版本爲msvcr110d.dll
  • 每種編譯選項下對應的靜態庫也只有一個,比如:/MT選項對應libcmt.lib/MD選項對應msvcrt.lib/MTd選項對應libcmtd.lib/MDd選項對應msvcrtd.lib
  • 根據大佬的文章可以知道,舊版 CRT 中調用main的是crtexe.c

注意:C++與C對應的運行時庫是不同的

新版CRT
在 VS2015 發佈之前,微軟打算是把運行時庫由一個msvcrxx.dll拆分成三個vcruntime140.dll,appcrt140.dll以及desktopcrt140.dll,它們各自的作用我就不詳細敘述了,感興趣可以自己搜搜看!但是最後發佈的時候微軟將後兩個合併成了一個,所以目前我們用的15,17版的 VS 的動態庫有兩個vcruntime140.dllurctbase.dll!大名鼎鼎的ucrt就是這麼來的了!注意:ucrt並不單單隻有一個庫,還包含到大量諸如api-ms-*.dll的庫,其實是爲了dll的延遲加載從而節省資源

新版的 CRT 不僅動態庫規劃的很好,靜態 lib 也較之前做出了很大的改變,不同編譯選項下鏈接 lib 的數量都從一個增加至三個,具體:

Release Static (/MT):libcmt.lib libvcruntime.lib libucrt.lib
Debug Static (/MTd):libcmtd.lib libvcruntimed.lib libucrtd.lib
Release DLL (/MD):msvcrt.lib vcruntime.lib ucrt.lib
Debug DLL (/MDd):msvcrtd.lib vcruntimed.lib ucrtd.lib

由於運行時的靜態庫和動態庫發生了較大改變,相應的 CRT 流程也發生了巨大改變,下面我們將以 VS2015 爲例具體跟跟程序的流程,先寫個 HelloWorld

clipboard.png

然後修改編譯選項爲 /MD,如果用 /MDd 需要自己去找ucrtbased.dll放在Wow64目錄下!現在我們只是跟蹤 CRT 的執行流程可以不必在意 Release 還是 Debug,/MD 還是 /MDd。在 return 後下斷,然後點擊開始調試

clipboard.png

再轉到反彙編窗口,一直 F10 執行到 ret 之後

clipboard.png

此時停在call main的下一行即 return 的地方,往上面翻然後轉到源碼可以看到我們是在exe_common.inl這個源碼中調用的main

clipboard.png

接着看看附近是誰調用的exe_common.inl,發現是exe_main.cpp包含的exe_common.inl文件

clipboard.png

所以整個 CRT 的函數調用可以歸納爲:
mainCRTStartup調用__scrt_common_main
__scrt_common_main調用__scrt_common_main_seh
__scrt_common_main_seh調用invoke_main
invoke_main調用_main

重現攻擊

參考文章:深入分析CCleaner後門代碼 - 編譯環境污染類的供應鏈攻擊案例

直接修改obj
P.S.爲了方便測試均採用 /MD 編譯 Release 版本的程序
VS2015 採用 /MD 選項編譯時靜態鏈接了三個庫,經過提取 obj 分析,證實了 CRT 的執行流程在exe_main.obj中,而exe_main.objmsvcrt.lib中,所以我們先把 msvcrt.lib 中的 obj 文件全部提取出來,用到的 bat 批處理如下

@echo off
for /f %%i in ('lib /list msvcrt.lib') do (lib /extract:%%i msvcrt.lib)
pause

可以看到確實存在exe_main.obj

clipboard.png

然後用 IDA 打開exe_main.obj,在 main 之前 patch 兩個字節的 0xCC

clipboard.png

最後需要重新生成 msvcrt.lib 文件,可以用之前大佬文章中的lib /remove:xx.obj msvcrt.lib配合lib msvcrt.lib xx.obj來替換修改的 obj 文件。也可以像我這樣做,直接將 patch 後的 obj 文件和之前提取的所有 obj 文件歸檔爲 lib 文件,用到的命令爲lib *.obj /out:msvcrt.lib,然後將該 lib 文件拿去替換 VS 目錄下的 msvcrt.lib 文件

clipboard.png

再新建一個 HelloWorld 項目來調試,成功在 main 之前觸發了 CC 斷點,注意:此時需要Release和/MD

clipboard.png

重新編譯obj
踩坑記錄:千萬不要試圖用cl /c去生成exe_main.obj文件,依賴項太多普通的編譯指令搞不定,正準備打算學習下makefile的時候,用另外一種方法搞定了

第一步用 VS2015 新建一個空項目,然後複製整個運行時庫的所有源碼到項目目錄下

clipboard.png

第二步點擊 VS 中顯示所有文件,然後右鍵複製過來的目錄選擇包括在項目中

clipboard.png

第三步找到exe_common.inl,在調用 main 前內聯彙編兩個int 3

clipboard.png

第四步找到exe_main.cpp,點擊生成-編譯,發現報了好多錯!報錯原因是頭文件包含的問題,因爲頭文件就在我們的目錄下,所以把所有包含出錯的<>替換爲"",最後還剩個報錯就是#error主動報的錯,把這行代碼刪除就是,最後就能成功編譯了

clipboard.png

第五步在項目目錄中找到新生成的exe_main.obj

clipboard.png

最後一步就是用該obj文件替換msvcrt.lib中的原obj文件並且用修改後的msvcrt.lib替換VS目錄下的msvcrt.lib,具體操作見上方!最最後就是測試了,也能夠成功出發 CC 斷點

clipboard.png

clipboard.png

END

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章