日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

工控安全入门(六)——逆向角度看Vxworks

發(fā)布時間:2024/8/24 编程问答 50 如意码农
生活随笔 收集整理的這篇文章主要介紹了 工控安全入门(六)——逆向角度看Vxworks 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

上一篇文章中我們對于固件進(jìn)行了簡單的分析,這一篇我們將會補充一些Vxworks的知識,同時繼續(xù)升入研究固件內(nèi)容。

由于涉及到操作系統(tǒng)的內(nèi)容,建議大家在閱讀本篇前有一定操作系統(tǒng)知識的基礎(chǔ),或者是閱讀我的《Windows調(diào)試藝術(shù)》的文章簡單了解諸如線程、中斷、驅(qū)動等的知識。

本篇為工控安全入門(五)—— plc逆向初探的延伸。

什么是 Vxworks

Vxworks操作系統(tǒng)是由美國Wind River System 公司開發(fā)的一套實時操作系統(tǒng),Vxworks比起linux,有更好的實時性和可裁切性,公司可以根據(jù)自己的需求去定制化Vxworks,我們逆向的固件就是施耐德在Vxworks上進(jìn)行的二次開發(fā)。在國防安全、工業(yè)化方面Vxworks占據(jù)了半壁江山,甚至連愛國者導(dǎo)彈都與Vxworks有關(guān),可見其在工控領(lǐng)域的地位,但是,由于Vxworks目前幾乎是沒有任何的”純新手“入門書籍,所以學(xué)習(xí)起來就較為困難,在《工控安全入門》系列中我將盡可能簡單的介紹該系統(tǒng)的相關(guān)細(xì)節(jié),但是如果想要深入學(xué)習(xí)或者進(jìn)行二次開發(fā)的話,可以參考官方的相關(guān)手冊。

Vxworks的任務(wù)

Vxworks作為RTOS(real time operating system)有一套獨特的任務(wù)體制和調(diào)度方案來保證其實時性,在對固件進(jìn)行進(jìn)一步研究之前,我們有必要把這一部分內(nèi)容搞清楚,保證后續(xù)工作的順利。

在Vxworks中有四種任務(wù)隊列:

  • active隊列,所有的任務(wù)都在這個隊列中,也叫做活動隊列,當(dāng)我們在shell中運行i命令顯示全部任務(wù)時
  • tick隊列,Vxworks中有tsakDelay函數(shù),該函數(shù)的目的是為了讓某個任務(wù)推遲執(zhí)行,也就是暫時不可搶占cpu的任務(wù),這些任務(wù)就保存在tick隊列中,也叫做定時隊列
  • ready隊列,已經(jīng)做好所有準(zhǔn)備、等待cpu的任務(wù),也叫做就緒隊列
  • work隊列,一種特殊的環(huán)形隊列,也叫做內(nèi)核延時隊列

在Vxworks中,有著usrAppInit的函數(shù)(取決于某個宏,我們這里就先認(rèn)為默認(rèn)有了),實際上就相當(dāng)于我們平常理解的main函數(shù)了,我們可以在這”胡作非為“,但是我們作為一個多任務(wù)系統(tǒng),不可能就一個main函數(shù)一個主任務(wù)打天下,所以還需要幾個函數(shù)來進(jìn)行任務(wù)的相關(guān)操作。

Vxworks的任務(wù)有256個優(yōu)先級(0~255),0為最高優(yōu)先級,對于應(yīng)用層的程序,一般使用100到250的優(yōu)先級,驅(qū)動類的程序使用51到99。和其他操作系統(tǒng)一樣,每個任務(wù)都用一種數(shù)據(jù)結(jié)構(gòu)表示,也就是TCB(在之前的《Windows調(diào)試藝術(shù)》中我們詳細(xì)介紹過該結(jié)構(gòu),雖然操作系統(tǒng)不同,但是設(shè)計思想是一致的)。

要注意,在Vxworks5.x版本中,并沒有嚴(yán)格區(qū)分內(nèi)核態(tài)和用戶態(tài)(使用KernelState全局變量來標(biāo)識是否為內(nèi)核,但棧還是一個棧,并沒有從本質(zhì)上區(qū)分),也就是說TCB還是暴露在用戶視野下的,所以一旦存在堆棧溢出的情況,會非常致命,而在6.x之后Vxworks正式區(qū)分了用戶與內(nèi)核,極大改善了安全問題。

int taskSpawn(
char *name,
int priority,
int options,
int stackSize,
FUNCPTR entryPtr,
int agr1,
int agr2,
...
int agr10
)

該函數(shù)用來創(chuàng)建一個新的任務(wù),返回的是任務(wù)的”身份證“,同時也是個內(nèi)存地址,指向該任務(wù)的TCB,也被稱為tid

  • name,執(zhí)行任務(wù)的名字
  • priority,優(yōu)先級,也就是上面提到的那256個
  • options,控制任務(wù)的某些行為,比如VX_DSP_TASK意思是要使用DSP處理器來支持該任務(wù)
  • stackSize,也就是該任務(wù)要使用的棧的大小
  • entryPtr,任務(wù)要執(zhí)行的函數(shù)的指針,一般稱為入口函數(shù)
  • args,入口函數(shù)所需要的參數(shù),如果多于10個還可以使用指針進(jìn)行結(jié)構(gòu)體或數(shù)組傳輸?shù)姆绞?/li>
int taskCreate(
...
)

該函數(shù)參數(shù)與taskSpawn完全一致,返回的同樣是tid,但是它創(chuàng)建的任務(wù)并沒有做好運行的準(zhǔn)備(也就是說沒有進(jìn)入ready隊列),需要使用taskActivate來喚醒它。

STATUS taskDelete(int tid)

該函數(shù)用來刪除一個任務(wù),但是該函數(shù)非常非常危險,一般是不使用的。舉個栗子,假設(shè)我們有一個扳手,現(xiàn)在有一個任務(wù)在用,后面還有幾個任務(wù)在排隊等待使用,但是突然你把人家delete了,就相當(dāng)于連人帶扳手都沒了,但后面幾個任務(wù)就傻眼了,成了無限等待了。

當(dāng)然,關(guān)于的任務(wù)的函數(shù)還有很多很多,這里只是簡單地說明,之后碰到了我們再去看。

Vxworks的啟動

上一篇文章中我們用到了sysStartType(系統(tǒng)啟動類型)這個參數(shù),但是由于篇幅所限沒有詳細(xì)說明,這里就先來看一下。

Vxworks簡單來說有兩種不同的啟動方式:

  • bootram啟動,類似我們pc的BIOS,先有個小的操作系統(tǒng),這個操作系統(tǒng)再去引導(dǎo)真正要用的操作系統(tǒng)運行。這個小的操作系統(tǒng)就是bootram,它存放在ROM或者是Flash中,它運行后會通過串口或是網(wǎng)口將Vxworks下載到RAM在進(jìn)行啟動工作。
  • ROM啟動,Vxworks映像直接保存在ROM中,直接啟動即可。

bootram啟動(對比Vxworks相關(guān)函數(shù))

因為ROM啟動和bootram實際上在后續(xù)的部分完全一致,所以這里我們挑選更為復(fù)雜的bootram啟動來做講解。

當(dāng)上電時,系統(tǒng)會自動跳轉(zhuǎn)到ROM或是Flash的bootram引導(dǎo)程序,有趣的是,bootram和Vxworks的命名方式非常相似。一個是bootConfig.c,一個是usrConfig.c,而程序中像是usrInit、usrRoot等的函數(shù)名完全一致,當(dāng)然了,功能上也有類似之處,下面說的usrInit、ursRoot沒有特殊說明均為bootram的。

對于bootram來說,又可以按照是否進(jìn)行了壓縮分為bootram.bin和bootram_uncmp.bin等等,因為大體思路相同,這里我們就以bootram.bin為例進(jìn)行說明。

  • romInit,初始化工作。比如初始化內(nèi)存寄存器、初始化寄存器、初始化棧(這個棧是bootram用到的棧,和我們后來的Vxworks沒有關(guān)系)、禁止中斷等等
  • romStart,復(fù)制工作。它將非壓縮(這里非壓縮的就是romInit和romstart)部分復(fù)制到ram的低地址(定義為RAM_LOW_ADRS)部分,將壓縮的部分復(fù)制到ram的高地址(定義為RAM_HIGH_ADRS)部分并進(jìn)行解壓;對于冷啟動(之前文章中提到過,會重置數(shù)據(jù))來說,它還會將ram的數(shù)據(jù)清空;最后再跳轉(zhuǎn)到usrInit。
  • usrInit,和我們之前分析的Vxworks的usrInit有類似之處
void usrInit?(?int startType?)?
{
while (trapValue1 != 0x12348765 || trapValue2 != 0x5a5ac3c3 )?
{?
;?
}?
cacheLibInit (0x02 , 0x02 );?
bzero (edata, end - edata);?
sysStartType = startType;
intVecBaseSet ((FUNCPTR *) ((char *) 0x0) );?
excVecInit ();?
sysHwInit ();
usrKernelInit ();?
cacheEnable (INSTRUCTION_CACHE);?
kernelInit ((FUNCPTR) usrRoot, (24000) ,(char *) (end) ,sysMemTop (), (5000) , 0x0 );?
}

我們這里就把固件的usrInit也拿出來,放一塊對比著學(xué)習(xí)

首先是做了個死循環(huán),檢查兩個變量的值,很顯然值就是兩個地址,實際上就是檢查romStart的復(fù)制工作是不是成功了。而Vxworks顯然不需要再對RAM的內(nèi)存空間進(jìn)行檢查了,所以沒有這一步。

接著調(diào)用cacheLibInit,可以看到兩個usrInit都有這個函數(shù),xxxLibInit可以視作一類函數(shù),功能是初始化xxx的庫函數(shù),這里就是初始化cache(就是緩存)的庫函數(shù),至于參數(shù)則是初始化中一些選項,不用在意。

bzero (edata, end – edata)上一次也說了,將一段地址的內(nèi)容賦為0,這里實際上就是將BSS段清0

intVecBaseSet、excVecInit 上篇文章中詳細(xì)分析了代碼,就是布置中斷向量表。

sysHwInit ()這個函數(shù)用來初始化設(shè)備,直觀來說就是將各種外設(shè)進(jìn)行簡單的初始化,同時讓他們保持“沉默”,我們都知道CPU通過中斷來響應(yīng)外設(shè),但由于現(xiàn)在我們還沒完全建立起中斷體系(只是簡單地建立了interrupt vector),所以設(shè)備一旦產(chǎn)生中斷,那就會出現(xiàn)沒有中斷處理函數(shù)的尷尬情況,進(jìn)而導(dǎo)致系統(tǒng)出錯,所以需要讓設(shè)備保持“沉默”。

往后走是rootram的cacheEnable和Vxworks的usrCacheEnable,其實類似xxxEnable的函數(shù)都是“使能”的意思,就是數(shù)字電路中的使能端,只有使能了,這個東西才可以用。

最后就是最最最關(guān)鍵的usrKernelInit了,我們先來看看Vxworks的,如下圖:

上來的xxxLibInit我們說過了是初始化函數(shù)庫的,之后是qInit(包括workQInit),全稱是queue init,也就是隊列的初始化,詳細(xì)的我們上面已經(jīng)講過了。

void kernelInit

(

    FUNCPTR rootRtn,            /* 用戶啟動例程 */

    unsigned  rootMemSize,             /*給 TCB 和初始任務(wù)棧分配的內(nèi)存 */

    char *       pMemPoolStart,      /* 內(nèi)存池的起始地址 */

    char *       pMemPoolEnd,         /* 內(nèi)存池的結(jié)束地址 */

    unsigned  intStackSize,    /* 中斷棧大小 */

    int              lockOutLevel    /* 關(guān)中斷級別 (1-7) */

)

主要就是創(chuàng)建并執(zhí)行了一個任務(wù),同時設(shè)置了該任務(wù)的TCB(thread control block,保存線程的相關(guān)信息)、棧、內(nèi)存池等等。這里創(chuàng)建的任務(wù)就是usrRoot,分配的內(nèi)存池起始地址為(sysMemTop – end)/16,即內(nèi)存空間的十六分之一用來存儲,中斷級別為0即禁止任何形式的中斷。

而bootram的usrKernelInit函數(shù)基本一致,只不過將kernelInit放到了外面。

  • usrRoot,不知道大家有沒有注意到,從usrInit到usrRoot,是以任務(wù)創(chuàng)建的方式進(jìn)行的,也就是說,沒有返回值,在進(jìn)一步說,從這一步開始,上下文就變了,之前可以說是活在”石器時代“,一堆函數(shù)所做的只不過是在”石器時代“進(jìn)行操作罷了,而現(xiàn)在正式進(jìn)入”文明時代“了。

如圖為反編譯的Vxworks的usrRoot。

首先開始是usrKernelCoreInit,具體如上圖所示,主要作用是對一些功能進(jìn)行初始化,sem開頭的代表信號量,wd則是看門狗(watch dog,簡單來說就是監(jiān)測系統(tǒng)有沒有嚴(yán)重到無法恢復(fù)的錯誤,有的話將系統(tǒng)重啟)的意思,msgQ則是消息隊列(關(guān)于消息的內(nèi)容在《Windows調(diào)試藝術(shù)》中也見過了,實質(zhì)相同),taskHook則是和hook相關(guān)的內(nèi)容。

接著調(diào)用memInit來初始化系統(tǒng)內(nèi)存堆,這里開始我們就可以使用malloc和free函數(shù)了。往后到了非常重要的一個環(huán)節(jié),也就是sysClkInit函數(shù),它是用來對時鐘進(jìn)行初始化的,而時鐘就肯定要涉及到時鐘的中斷處理(我們在上面說了 sysHwInit等一系列并沒有真正完成硬件設(shè)備的中斷處理注冊工作,現(xiàn)在我們的時鐘還不能正常的工作),我們將升入去研究它。

首先是sysClkConnect函數(shù),它以usrClock作為參數(shù),很可疑,有沒有可能usrClock就是我們要找的中斷處理例程呢?我們深入去看

可以看到,usrClock這個函數(shù)只是被放在內(nèi)存的某個位置,似乎和中斷沒掛鉤,反倒是出現(xiàn)了sysHwInit2,不得不讓人和上面的sysHwInit產(chǎn)生聯(lián)系,我們一步步深入該函數(shù)

最終我們發(fā)現(xiàn)了intConnect,它將ppc860Int注冊為時鐘中斷處理例程,這里實際上和我找到的Vxworks的源碼并不相同,在源碼中intConnect會將sysClkInt注冊為中斷處理例程,然后在sysClkInt去調(diào)用usrClock,最后再去執(zhí)行usrClock的tickAnnounce函數(shù)進(jìn)行具體的任務(wù)調(diào)度,這里不知道是不是施耐德的固件對于具體需要進(jìn)行了調(diào)整,還是說又是Ghidra的一個分析bug。

回到usrRoot,之后調(diào)用usrIosCoreInit,進(jìn)入函數(shù)發(fā)現(xiàn)iosInit,這是Vxworks的io子系統(tǒng),之后我們會具體講解,這里只看看它初始化了啥

?

iosInit的參數(shù)有三個,第一個是支持的最大驅(qū)動數(shù),第二個函數(shù)是系統(tǒng)最多同時打開的文件數(shù),第三個則是一個特殊的文件,所有寫入它的內(nèi)容都會無效(Linux也有類似的文件)

繼續(xù)深入就到了usrSerialInit

這個函數(shù)有些難理解,為了方便大家查看,我將一些變量進(jìn)行了重命名。

首先是tyname為0,也就是為空了,然后將tyname(空的)和/tyCo/連接起來,實際上就是tyCo,然后調(diào)用了一個未解析出來的函數(shù),實際上就是將ix的值變?yōu)樽址賹⑵淦唇拥絫yname上,再加上ix<1的循環(huán),也就是說此時就有了/tyCo/1和/tyCo/0兩個字符串,這個名字實際上就代表了串口,也就是說該plc有兩個串口。

對于這兩個設(shè)備,首先使用ttyDevCreate來創(chuàng)建設(shè)備,這里聽著不太通順,實際上是Vxworks的一個特點,雖然你實際上已經(jīng)有這個串口設(shè)備了,但是對于系統(tǒng)來說,并不知道,需要你基于它調(diào)用xxxDevCreate來”注冊“,關(guān)于這個的詳細(xì)說明會在后面的文章中涉及。注冊后會判斷ix是否為0,也就是對于/tyCo/0在進(jìn)行操作,調(diào)用ioctl函數(shù)來對該串口設(shè)備進(jìn)行操作。

ioctl和linux的ioctl相似,都是因為傳統(tǒng)的open、read、write等基本操作都是將設(shè)備抽象成文件來進(jìn)行(linux崇尚萬物皆文件嘛)的,對于設(shè)備獨有的操作(比如光驅(qū)的彈出等等)就無法完成了,所以有了該函數(shù)

ioctl(int fd,int function,int arg)

fd即為open設(shè)備后返回的文件標(biāo)識符(Vxworks中經(jīng)常叫做consoleFd,別和控制臺搞混了……),function則是要進(jìn)行的操作,arg是操作需要的參數(shù),大家可以在ioLib.h中找到,如下圖所示

最后跳出循環(huán),注意,此時consoleFd為/tyCo/1的fd,利用ioGlobalStdSet標(biāo)準(zhǔn)對輸入、標(biāo)準(zhǔn)輸出、錯誤輸出進(jìn)行重定向,此時,我們的printf之類的函數(shù)就可以用了。

再跳回到usrRoot,剩下的初始化的函數(shù)我們就不看了(有興趣的可以自己看看),只關(guān)心一下usrNetworkInit,為啥呢?因為這有個漏洞,也就是很有名的CVE-2011-4859,這個版本的固件還未修復(fù),我們在下一篇文章會詳細(xì)分析這部分的內(nèi)容

最終由usrRoot調(diào)用usrAppInit,我們總算是來到了所謂的”main“

同樣的,對比bootram系統(tǒng),初始化方面相同,而這些完成之后,bootram也就開始將Vxworks加載到內(nèi)存中,開始將工作托付給Vxworks,啟動也就完成了。

總結(jié)

本篇文章中介紹了Vxworks的任務(wù)機制,并將固件的初始化方面的函數(shù)進(jìn)行了簡單的分析,下一篇我們會研究Vxworks在網(wǎng)絡(luò)方面的初始化以及CVE-2011-4859,并著手開始逆向固件的”main“函數(shù)。

總結(jié)

以上是生活随笔為你收集整理的工控安全入门(六)——逆向角度看Vxworks的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。