寫了兩天的程序,總算把RING3下的搞定了。這個(gè)太強(qiáng)悍了,可以穿透所有的主動(dòng)防御和還原軟件和影子系統(tǒng)~~危害太大,代碼我就不發(fā)了~~關(guān)于RING0驅(qū)動(dòng)過(guò)還原的可以看機(jī)器狗的代碼~ 隨著機(jī)器狗病毒和MJ的Tophet.a的放出,無(wú)論是RING0還是RING3下穿透還原軟件的技術(shù)突然浮現(xiàn)在了大眾的眼前。讓你的毒毒和小馬能穿透還原軟件/卡,的確很吸引人~~
最近學(xué)習(xí)SCSI總線命令,結(jié)合以前的知識(shí)做了一下穿透還原軟件,學(xué)習(xí)是個(gè)曲線前進(jìn)過(guò)程,有可能文中有些觀點(diǎn)不對(duì),主要來(lái)自《SCSI程序員指南》和《scsi總線及IDE接口(協(xié)議、應(yīng)用和編程)》~~
IRP是從文件系統(tǒng)->卷驅(qū)動(dòng) -> 磁盤驅(qū)動(dòng)-> 類驅(qū)動(dòng)-> 端口驅(qū)動(dòng)-> 微端口驅(qū)動(dòng)
ntfs/fat32.sys->partmgr.sys->ftdisk.sys->disk.sys->classpnp.sys->acpi.sys->atapi.sys
其中不少是value-added和filter驅(qū)動(dòng),主要的是文件系統(tǒng)驅(qū)動(dòng)ntfs/fat32.sys,storage驅(qū)動(dòng)disk.sys和總線驅(qū)動(dòng)atapi.sys,我們知道,文件系統(tǒng)驅(qū)動(dòng)做的工作就是把下層讀扇區(qū)得到的RAW數(shù)據(jù)轉(zhuǎn)換成文件,向上提供,磁盤驅(qū)動(dòng)這層就是把上層請(qǐng)求的讀扇區(qū)的IRP換成帶SCSI命令的IRP發(fā)給下層的miniport ATAPI.sys,ATAPI.sys調(diào)用hal中的write_port_char最后來(lái)直接端口I/O了。還原程序是在哪一層呢?是在disk.sys之上插了一個(gè)filter,如果直接繞過(guò)上層的驅(qū)動(dòng)直接發(fā)帶SRB的IRP給Disk.sys或是ATAPI.sys,那么就實(shí)現(xiàn)了穿透。另外,當(dāng)然還有別的方法,譬如端口I/O,但是通用性不好,與硬盤具體的接口有關(guān)(IDE,SCSI等)
kd>bu atapi!IdePortStartIo
上圖可看到,在disk這層的irp處理交給了classpnp了,在disk這層之上有個(gè)DeepFrz的驅(qū)動(dòng),這是我測(cè)試用的冰點(diǎn)還原的驅(qū)動(dòng),可以的確冰點(diǎn)還原的驅(qū)動(dòng)是位于disk之上的,從理論上說(shuō)我們發(fā)SRB給disk是可以繞過(guò)還原的。到了atapi這層就是走atapi!IdePortStartIo->nt!IoStartPacket->IdePortStartIo,之后就分有無(wú)DMA能力區(qū)別對(duì)待了。
首先SCSI總線上設(shè)備數(shù)據(jù)傳輸?shù)倪^(guò)程:
申請(qǐng)—仲裁(建立連接)—消息—命令—數(shù)據(jù)(如果命令產(chǎn)生數(shù)據(jù)傳輸)—消息(通知結(jié)束,發(fā)送狀態(tài)碼)
SCSI有以下幾個(gè)階段:
1總線空閑階段
2總裁階段
3選擇階段
4重新選擇階段
5消息輸出階段
6命令階段(接受CDB)
CDB分為0,1,2,5號(hào)組命令,命令組號(hào)告訴目標(biāo)器在CDB中有多少字節(jié),然后會(huì)產(chǎn)生以下三種階段情況(本段參考《SCSI程序員指南》,比較老了,根據(jù)SCSI SPC-3上有16字節(jié)的等)
0號(hào)組CDB是6字節(jié)長(zhǎng)
1,2號(hào)組CDB是10字節(jié)長(zhǎng)
5號(hào)組CDB是12字節(jié)長(zhǎng)
CDB的第一個(gè)字節(jié)是描述該命令的操作碼,高三位表示命令所屬的命令組0-7,低五位表示命令碼
CDB的第二個(gè)字節(jié)高三位表示一個(gè)LUN(邏輯單元號(hào))
其中LUN是什么呢?LUN的全稱是Logical Unit Number,也就是邏輯單元號(hào)。我們知道SCSI總線上可掛接的設(shè)備數(shù)量是有限的,一般為6個(gè)或者15個(gè),我們可以用Target ID(也有稱為SCSI ID的)來(lái)描述這些設(shè)備,而實(shí)際上我們需要用來(lái)描述的對(duì)象,是遠(yuǎn)遠(yuǎn)超過(guò)該數(shù)字的,于是我們引進(jìn)了LUN的概念,也就是說(shuō)LUN ID的作用就是擴(kuò)充了Target ID。每個(gè)Target下都可以有多個(gè)LUN Device,我們通常簡(jiǎn)稱LUN Device為L(zhǎng)UN,這樣就可以說(shuō)每個(gè)設(shè)備的描述就有原來(lái)的Target x變成Target x LUN y了,那么顯而易見(jiàn)的,我們描述設(shè)備的能力增強(qiáng)了。
從第三字節(jié)開始是命令參數(shù)字段,對(duì)直接存取設(shè)備來(lái)說(shuō)是邏輯塊地址,對(duì)傳輸數(shù)據(jù)的命令是傳輸長(zhǎng)度
最后一個(gè)字段是Control字段,如Link標(biāo)志指出該CDB是否是一系列連接命令的一部分,F(xiàn)lag標(biāo)志決定一條連接的命令成功執(zhí)行后目標(biāo)器返回的狀態(tài)碼(6-7廠商自定,2-5保留,1標(biāo)志位,0連接位)
強(qiáng)制命令(適用于所有設(shè)備的命令,采用大端點(diǎn)數(shù))
00H Test unitReady
簡(jiǎn)單的報(bào)告設(shè)備是否已經(jīng)為執(zhí)行命令做好準(zhǔn)備,6字節(jié)組(高三位為0)
03H Request Sense(讀取)
12H Inquiry
查詢?cè)O(shè)備的制造商,模型信息等
1DH Send Diagnostic
設(shè)備類型特定命令
mode sense(獲取目標(biāo)機(jī)操作參數(shù)信息)和mode select(改變參數(shù))有6字節(jié)和10字節(jié)
Read和Write命令有6字節(jié)和10字節(jié)版本,分屬與0號(hào)組和2號(hào)組
數(shù)據(jù)階段
狀態(tài)階段
消息輸入階段
SCSI miniport驅(qū)動(dòng)器實(shí)現(xiàn)了對(duì)SCSI接口適配器的直接控制。miniport驅(qū)動(dòng)器對(duì)SCSI適配器進(jìn)行初始化,并向硬件傳輸I/O請(qǐng)求,處理中斷的產(chǎn)生,執(zhí)行適配器水平的錯(cuò)誤修復(fù)和記錄。miniport驅(qū)動(dòng)器是一種小型的仿制的SCSI I/O模型,它隱藏了SCSI適配器硬件水平上的細(xì)節(jié)內(nèi)容。它提供了帶有連續(xù),低層次的借口的高水平的SCSI模型,而根本不用考慮實(shí)際的硬件接口。只要實(shí)現(xiàn)了規(guī)定的SCSI miniport 接口,SCSI miniport驅(qū)動(dòng)器就不必對(duì)傳統(tǒng)的SCSI適配器進(jìn)行控制,這就允許外設(shè)制造商在他們的硬件上使用不同的總線接口。ATAPI設(shè)備擁有一套幾乎與SCSI完全一致的命令,但是它們進(jìn)行的數(shù)據(jù)傳輸卻是基于IDE總線的。windows中的ATAPI的miniport驅(qū)動(dòng)接受了低層次的SCSI命令,并把它們通過(guò)IDE總線發(fā)送出去
SCSIPORT驅(qū)動(dòng)器對(duì)于系統(tǒng)中的所有SCSI請(qǐng)求提供了唯一的入口點(diǎn),對(duì)系統(tǒng)中各種各樣的miniport驅(qū)動(dòng)器進(jìn)行初始化,把系統(tǒng)特有的SCSI I/O請(qǐng)求轉(zhuǎn)化成標(biāo)準(zhǔn)的SRB,并把這些請(qǐng)求發(fā)送給適當(dāng)?shù)膍iniport驅(qū)動(dòng)器。由于硬件細(xì)節(jié)被隱藏在了miniport驅(qū)動(dòng)器中,所以高層次的驅(qū)動(dòng)器可以調(diào)用SCSIPORT驅(qū)動(dòng)器來(lái)執(zhí)行所有的SCSI I/O操作,而不必在乎所使用的硬件接口,在windows NT中,有很多種標(biāo)準(zhǔn)的SCSI類驅(qū)動(dòng)器,包括用來(lái)處理磁盤驅(qū)動(dòng)器(disk.sys)和CD-ROM驅(qū)動(dòng)器的類驅(qū)動(dòng)器。文件系統(tǒng)的驅(qū)動(dòng)器可以調(diào)用磁盤類驅(qū)動(dòng)器來(lái)執(zhí)行高層次,面向塊的I/O請(qǐng)求。磁盤類驅(qū)動(dòng)會(huì)把文件系統(tǒng)的請(qǐng)求轉(zhuǎn)化成一系列的SCSI I/O請(qǐng)求,然后它們會(huì)被傳送到SCSIPORT驅(qū)動(dòng)。
sense data(檢測(cè)數(shù)據(jù)) :當(dāng)一個(gè)SCSI設(shè)備(通常是一個(gè)LUN)發(fā)現(xiàn)它自己處于異常狀態(tài)時(shí),它就拒絕執(zhí)行下面的命令,并返回一個(gè)check condition狀態(tài)。產(chǎn)生至少18個(gè)字節(jié)長(zhǎng)的數(shù)據(jù),包括經(jīng)過(guò)編碼的關(guān)于錯(cuò)誤的信息,稱為檢測(cè)數(shù)據(jù)。
IOCTL_SCSI_PASS_THROUGH
IOCTL_SCSI_PASS_THROUGH_DIRECT
NTFS使用邏輯簇號(hào)(Logical Cluster Number,LCN)和虛擬簇號(hào)(Virtual Cluster Number,VCN)來(lái)對(duì)簇進(jìn)行定位。LCN是對(duì)整個(gè)卷中所有的簇從頭到尾所進(jìn)行的簡(jiǎn)單編號(hào)。用卷因子乘以LCN,NTFS就能夠得到卷上的物理字節(jié)偏移量,從而得到物理磁盤地址。VCN則是對(duì)屬于特定文件的簇從頭到尾進(jìn)行編號(hào),以便于引用文件中的數(shù)據(jù)。VCN可以映射成LCN,而不必要求在物理上連續(xù)。
總之,不用擔(dān)心下層的具體硬件接口是什么,是IDE還是SCSI,都可以用SCSI命令發(fā)給ATAPI,實(shí)現(xiàn)文件的讀寫。其實(shí)正常的文件讀寫也是這樣的流程,只不過(guò)我們自己繞過(guò)上層,自己構(gòu)造SRB而已~
首先看驅(qū)動(dòng)發(fā)SRB
參見(jiàn)http://www.osronline.com/DDKx/storage/k306_0hte.htm
typedef struct _SCSI_REQUEST_BLOCK {
USHORT Length; // 該SRB的長(zhǎng)度
UCHAR Function; // 功能碼,如果想發(fā)SCSI命令,設(shè)置為SRB_FUNCTION_EXECUTE_SCSI
UCHAR SrbStatus; // SRB發(fā)送后返回的狀態(tài),一般SRB發(fā)送后不會(huì)立即執(zhí)行,會(huì)放入一個(gè)隊(duì)列中,所以通常返回
SRB_STATUS_PENDING
UCHAR ScsiStatus; // 是否成功發(fā)送,與上面一樣均為返回值
UCHAR PathId; // 指明是總線ID,(PathId,TargetId,Lun)來(lái)確定一個(gè)設(shè)備
UCHAR TargetId; //
UCHAR Lun; //
UCHAR QueueTag; // 隊(duì)列TAG
UCHAR QueueAction; // 與上述結(jié)構(gòu)有關(guān)
UCHAR CdbLength; // SCSI命令塊長(zhǎng)度
UCHAR SenseInfoBufferLength; // 存儲(chǔ)返回經(jīng)過(guò)編碼的關(guān)于錯(cuò)誤的信息的緩沖區(qū)的長(zhǎng)度
ULONG SrbFlags; // Srb的相關(guān)參數(shù),SRB_FLAGS_DATA_IN為讀出,SRB_FLAGS_DATA_OUT為寫入設(shè)備
ULONG DataTransferLength; // 指出傳輸數(shù)據(jù)的大小
ULONG TimeOutValue; // 設(shè)定超時(shí)
PVOID DataBuffer; // 數(shù)據(jù)緩沖區(qū)
PVOID SenseInfoBuffer; // 檢測(cè)緩沖區(qū)
struct _SCSI_REQUEST_BLOCK *NextSrb; // 下一個(gè)SRB,一般的命令只要一個(gè)SRB
PVOID OriginalRequest; // 指向原始IRP
PVOID SrbExtension; //
union {
ULONG InternalStatus; //
ULONG QueueSortKey; //
};
UCHAR Cdb[16]; // SCSI總線命令了,這里不一定是16字節(jié),正如上面所說(shuō),具體幾個(gè)字節(jié),得看第一個(gè)字節(jié)。
} SCSI_REQUEST_BLOCK, *PSCSI_REQUEST_BLOCK;
SRB是與操作系統(tǒng)相關(guān)的,即是操作系統(tǒng)定義的結(jié)構(gòu),不在SCSI規(guī)范里的,相當(dāng)于對(duì)CDB的一層包裹。而真正執(zhí)行命令的是CDB
取自SCSI Primary Commands - 3
因?yàn)橐话阌脖P不會(huì)太大,10字節(jié)的DiskPos是32位的,可以操作4g*512字節(jié)的空間,一般已經(jīng)足夠了,如果要大點(diǎn)的話,可以用12字節(jié),16字節(jié)的CDB(可去參考SCSI Primary Commands - 3)具體結(jié)合機(jī)器狗的AtapiReadWriteDisk函數(shù)進(jìn)行學(xué)習(xí)~
ULONG AtapiReadWriteDisk(PDEVICE_OBJECT dev_object,ULONG MajorFunction, PVOID buffer,ULONG DiskPos, int BlockCount)
{
//dev_object為通過(guò)ObReferenceObjectByName得到的disk.sys設(shè)備對(duì)象,另外說(shuō)句題外話,根
//據(jù)MJ的說(shuō)法,disk.sys對(duì)于上層的SCSI_PASS_THROUGH是直接轉(zhuǎn)發(fā)給總線設(shè)備,那么構(gòu)造
//相應(yīng)的IRP直接發(fā)給總線設(shè)備也可以~這樣貌似更底層
//MajorFunction為自定義的一個(gè)參數(shù),根據(jù)傳輸進(jìn)來(lái)IRP功能IRP_MJ_READ和IRP_MJ_WRITE
//來(lái)轉(zhuǎn)換??梢圆镾CSI Primary Commands - 3的B章的operation codes知道10字節(jié)的寫為2Ah,
//讀為28h,機(jī)器狗代碼中是將2*((UCHAR)MajorFunction+ 17)付給了srb->Cdb[0],我們知道ntd
//dk.h中定義IRP_MJ_READ為0x3,IRP_MJ_WRITE為0x4,其實(shí)這只是個(gè)簡(jiǎn)單的換算而已
//buffer為輸入輸出的緩沖區(qū)
//DiskPos為32位指定的磁盤起始邏輯扇區(qū)號(hào)
//BlockCount為要讀(寫)的扇區(qū)數(shù)
......
srb->Length=sizeof(SCSI_REQUEST_BLOCK);
srb->Function=0; //0即是SRB_FUNCTION_EXECUTE_SCSI
srb->DataBuffer=buffer;
srb->DataTransferLength=BlockCount<<9;//因?yàn)槊可葏^(qū)是512字節(jié)
srb->QueueAction=SRB_FLAGS_DISABLE_AUTOSENSE;
srb->SrbStatus=0;
srb->ScsiStatus=0;//這兩個(gè)都是輸出,隨便填
srb->NextSrb=0;
srb->SenseInfoBuffer=sense;
srb->SenseInfoBufferLength=sizeof(SENSE_DATA);
if(MajorFunction==IRP_MJ_READ)
srb->SrbFlags=SRB_FLAGS_DATA_IN;
else
srb->SrbFlags=SRB_FLAGS_DATA_OUT;
if(MajorFunction==IRP_MJ_READ)
srb->SrbFlags|=SRB_FLAGS_ADAPTER_CACHE_ENABLE;
srb->SrbFlags|=SRB_FLAGS_DISABLE_AUTOSENSE;
srb->TimeOutValue=(srb->DataTransferLength>>10)+1;
srb->QueueSortKey=DiskPos;//指定從目標(biāo)設(shè)備的開始位置
srb->CdbLength=10;
srb->Cdb[0]=2*((UCHAR)MajorFunction+ 17);//參見(jiàn)上圖10字節(jié)CDB的格式
srb->Cdb[1]=srb->Cdb[1] & 0x1F | 0x80;//高三位是保留位,與0x1f清0高三位,高位置1
srb->Cdb[2]=(unsigned char)(DiskPos>>0x18)&0xFF; //大端點(diǎn)數(shù),右移24位,取高8位放入Cdb
srb->Cdb[3]=(unsigned char)(DiskPos>>0x10)&0xFF; //
srb->Cdb[4]=(unsigned char)(DiskPos>>0x08)&0xFF; //
srb->Cdb[5]=(UCHAR)DiskPos; //填寫sector位置
srb->Cdb[7]=(UCHAR)BlockCount>>0x08;
srb->Cdb[8]=(UCHAR)BlockCount;
......
}
當(dāng)然SRB也是屬于IRP中一部分,自己發(fā)IRP給總線設(shè)備,當(dāng)然還得自己填充IRP包,這個(gè)就不具體討論了
總的思路是:先發(fā)IoControlCode=FSCTL_GET_RETRIEVAL_POINTERS的IRP給文件系統(tǒng)驅(qū)動(dòng),得到某個(gè)文件的起始邏輯扇區(qū)號(hào)和大小,然后得到disk.sys的dev,再發(fā)構(gòu)造好的SRB的IRP給disk.sys就ok了,這樣就繞過(guò)了還原軟件
RING3發(fā)SCSI_PASS_THROUGH:
遍歷符號(hào)鏈接找到總線設(shè)備對(duì)應(yīng)的符號(hào)鏈接,發(fā)送SCSI_PASS_THROUGH_DIRECT給總線設(shè)備實(shí)現(xiàn)文件讀寫,從而繞過(guò)主動(dòng)防御,其中思路MJ0011說(shuō)了,不過(guò)他給的代碼基本沒(méi)用,好幾個(gè)暗樁~