c#實現虛擬光碟機–應用程式層(簡單涉及驅動部分)

來源:互聯網
上載者:User

(原來寫在CSDN上的,先轉過來一份.要不然開了博不寫點東西。總感覺是浪費哈) 
用c#實現的虛擬光碟機的原始碼(使用了minicd.sys)驅動部分未重新實現    
代碼有點亂。湊合著看吧  
      一直以來對虛擬光碟機的實現都很好奇,也曾想試著做一個,但查遍網上資料,基本上沒找到過什麼有用的。所以一直沒有實現。感於這方面資料的缺少。所以準備研究一下這方面的技術。             
在沒有什麼資料的情況下,要想研究某方面的技術,最好的辦法,當然是逆向。選擇作為逆向目標的軟體。一定不能過大。二要不是商業軟體。所以選了MINICD這款軟體。這款軟體用UPX壓縮了。壓縮後只有幾百K。不過作者對UPX壓縮後的軟體做了些處理,使用UPX –d 是無法解壓縮的。所有只有手脫了。UPX的變化殼手脫幾乎可以講是秒脫。用OD載入。一進來就是一個pushad 往下拉一拉就能看到一個popad 再往下就是一個大跳。跳到的地址就是OEP了。脫殼後發現是使用DELPHI寫的。使用DELPHI寫的。最好的輔助破解工具當然是DEDE。但用DEDE載入後看不到表單資訊,用EXESCOPE開啟資源的時候也會報錯。這是UPX壓縮的東西解壓後的後遺症,使用RESCOPE就可以查看到表單的資訊了。裡面也有按鈕的資訊。利用他就可以找到點擊插入按 鈕的事件處理過程了。找到後就可以開始分析了。當然這篇文章是來講虛擬光碟機的實現原理的。破解過程和分析過程。在這裡不作進一步的說明。會另開一篇文章來描述分析過程。破解過程就不講了。網上到處可以找得到。在這裡。只講分析的結果。     
   MINICD實現虛擬光碟機。有兩個檔案共同完成。一個是使用者層的EXE檔案(MINICD.exe)。一個是驅動檔案(minicd.sys)這個驅動檔案是由minicd.exe在建立裝置失敗並且發現system32\drivers\目錄裡沒有minicd.sys檔案時建立的(此檔案被嵌入到了minicd.exe的內部)
    其中minicd.exe 用於向驅動檔案發送訊息以使驅動工作。驅動則實現虛擬一個裝置。在驅動沒有被安裝的情況下,minicd.exe也負責安裝並啟動驅動。
    故而下面我們將分兩篇文章來講述虛擬光碟機的實現原理。這篇文章只講述使用者層也就是minicd.exe裡所作的事情。Minicd.sys也就是驅動裡所作的事情。在下一篇作描述。
    為了使你們跟著做能夠實現功能,當然我們要做一些準備工作了。首先是從網上下一個 “迷你虛擬光碟片”版本是1.0 在網上搜尋minicd 多的很。我的這個後面加了一個皮魯專版。運行一下。做這個的目的是讓MINICD釋放已經嵌入到他內部的minicd.sys. 運行後。映射一個ISO。成功後。你就會發現在 system32\drivers目錄裡多了一個minicd.sys檔案。這個就是驅動檔案。同時他還幫你安裝了這個驅動。故而我們就可以直接對其進行訪問。以實現虛擬一個光碟機的目的了。下面我們就講一講如何通過程式和minicd.sys進行通訊,以建立一個虛擬光碟機出來(這個也就是minicd.exe裡所作的事了)。至於如何在驅動沒有安裝的情況進行驅動的安裝我準備在講述完通訊後再接著講。因為目前驅動已經被你下載的minicd.exe 安裝好了。
   Minicd.sys 是一個典型的NT驅動檔案,所以要實現和其通訊,也就是一般的和NT驅動通訊的基本步驟。當然Minicd.exe裡也是這樣做的。在驅動已經安裝並啟動的情況下,只須簡單的兩步即可。  
Device = CreateFile(“\\.\\minicd”…….);  // 使用CreateFile建立一個裝置(裝置名稱叫\\.\\minicd),這裡為了說明過程不必要的參數先不列出
DeviceIOControl(Device,…inBuffer,..,outBuffer,…..);  // 使用DeviceIOControl 和裝置進行通訊,主要通過 inBuffer 和 outBuffer 在這裡只需要使用outBuffer即可。
知道步驟後,下面結合一下。Minicd.exe的實際例子詳細的對這兩個過程進行描述一下。
第一步:
HANDLE device = CreateFile("\\\\.\\MINICD",
        GENERIC_READ,       
        FILE_SHARE_READ,   
        0,
        OPEN_EXISTING,       
        0,
        0);
CreateFile 是一個萬能的API函數。能幹很多事情。建立檔案,建立管道等。當然也可以建立一個驅動。他的原形如下
HANDLE CreateFile(
  LPCTSTR lpFileName, // 如果是檔案就是檔案名稱,如果是裝置當然是裝置名稱
  DWORD dwDesiredAccess,  // 對建立的對象的存取權限 GENERIC_READ GENERIC_WRITE
  DWORD dwShareMode, // 共用模式
  LPSECURITY_ATTRIBUTES lpSecurityAttributes, // 安全屬性,一般傳null預設就行了
  DWORD dwCreationDisposition, // 對象建立方式
  DWORD dwFlagsAndAttributes, // 設定一些其它屬性或者標誌 預設即可
  HANDLE hTemplateFile // 預設即可
);

我在注釋裡已經大致講了每個參數的作用。這裡只作些必要的補充。
lpFileName 在這裡用的是 \\.\\minicd 這個是裝置的符號名。是在驅動裡使用IoCreateSymbolicLink 建立的。主要用於使用者層程式訪問驅動用的。注:符號名稱和裝置名稱是不同的(從IoCreateSymbolicLink就可以看出來,其實符號名就類似於裝置的一個連結,這個連結是給使用者層程式使用的)。當然這裡不明白也不要緊。在這裡只知道怎麼用的就行了。
dwDesiredAccess 存取權限可以是 GENERIC_READ(唯讀) GENERIC_WRITE(唯寫)
或者  GENERIC_READ | GENERIC_WRITE 讀寫
dwShareMode 共用模式 主要是用於其它程式也使用這個對象時設定共用的方式。這裡設定的是FILE_SHARE_READ。也就是其它進程仍然可以對這個裝置進行讀。
lpSecurityAttributes 標誌預設就可以了。只要用於設定許可權用的。很多API都用到這個。例如要實現關機,你就必須講你當前進程的安全屬性設定成允許關機的許可權才可以實現關機。否則普通的使用者層進程的安全許可權是無法使用ExitWindows這個函數的。
dwCreationDisposition 建立方式,設定一些諸如檔案不存在是建立還是返回錯誤等的一些方式而已。當然如果是建立裝置的不存在剛才講的這些問題。設定成OPEN_EXISTING 即可。就是如果驅動沒有安裝就返回失敗。
其它兩個預設即可,是些額外的屬性和標誌設定。對於建立檔案,這裡可以設定諸如唯讀等的額外屬性或者標誌等。和dwDesiredAccess的區別是dwDesiredAccess的許可權是運行時的許可權。而這裡設定的是檔案自身的許可權。當然對裝置來講不需要這兩個屬性。傳0即可

裝置建立成功後,就要和 裝置通訊了。和裝置通訊(就是和驅動通訊)要用到DeviceIOControl (主要用於一些自訂的或者擴充的操作,一般性的讀和寫有專門的常式)在minicd.exe 裡這個參數的調用是這樣的

WCHAR outBuffer[501] = L"MF:\\DevelopeTool\\SQL2000SP4.ISO";
DWORD bufferSize = 0x3EA;

DeviceIoControl(
            device,
            0x22008,
            NULL,
            0,
            outBuffer,
            sizeof(outBuffer),
            &bufferSize,
NULL)

這個函數的原型
BOOL DeviceIoControl(
  HANDLE hDevice,    // 裝置控制代碼,也就剛才CreateFile的傳回值
  DWORD dwIoControlCode,  // 控制碼 驅動可以接收到
  LPVOID lpInBuffer,   // 輸入緩衝
  DWORD nInBufferSize, // 輸入緩衝大小
  LPVOID lpOutBuffer,  // 輸出緩衝
  DWORD nOutBufferSize, // 輸出緩衝大小  
  LPDWORD lpBytesReturned,  // 這是一個傳回值,返回驅動儲存在輸出緩衝裡的資料的大小
  LPOVERLAPPED lpOverlapped  // 直接設null  保留用的吧
);
hDevice 注釋裡很明白了
dwIoControlCode 使用者層發給驅動程式的指令碼(發給IRP_MJ_DEVICE_CONTROL常式),可以自訂。驅動程式根據傳過來的指令碼,進行相應的操作。在minicd.exe 裡這個指令碼 是通過 移位和或操作得到的。其實就是CTL_CODE 宏。在這裡就不用管那麼多了,直接傳 0x22008即可。
lpInBuffer lpOutBuffer 用於和驅動交換資料用的。這裡只用到了lpOutBuffer. LpOutBuffer 指向的是一個LPVOID。你需要傳的是一個UNICODE的字串即可。字串如下面的樣子(前面已列出)
WCHAR outBuffer[501] = L"MF:\\DevelopeTool\\SQL2000SP4.ISO";
第一個字元M。是你求出來的目前可用的盤符。也就是你要映射的盤符。當然這個是要你自己去得出來的。不可能寫死在這裡,我只是測試用的而已。這方面的資料很多,你可以上網去查。緊跟後面的就是你要映射的ISO檔案的全   路徑 。 其實這樣的輸入應該放在lpInBuffer裡,但這裡放在了lpOutBuffer 感覺不是很爽。但不影響,因為,在驅動裡無論是 inBuffer和outBuffer都是一視同仁的。都可以往這兩上緩衝區裡,寫或者 讀

如果這個執行成功後。光碟機已經被虛擬出來了。

分析minicd.sys得知 在IRP_MJ_DEVICE_CONTROL常式裡接收到的指令碼是0x22008時。做的事情其實很簡單。

1.使用IoCreateDevice 建立一個名為 minicd+盤符索引 的裝置 例如 如果是M盤則 建立了個minicd12的裝置 其中12 就是(M-A)的值。裝置類型為 FILE_DEVICE_UNKNOWN  型的,一般虛擬設備都是這種類型

2.使用 IoCreateSymbolicLink  建立一個 符號 連結 和 這個裝置關聯起來 。 符號連結 的名字 為 你要建立的盤符名。

如要建立 M盤,則符號名為 M 。 當使用此函數時,符號名若為 A-Z 則系統會自動通知 資源管理員 建立相應的盤符。

當然 這個函數是在驅動層使用的。在使用者層有一個 和他類似的函數,也可以實現同樣的功能。就是

DefineDosDevice(
  __in          DWORD dwFlags,
  __in          LPCTSTR lpDeviceName,
  __in          LPCTSTR lpTargetPath
);

其中第二個參數 就是 符號名 (如果符號為 A-Z 則同樣的系統會自動通知資源管理員建立相應的盤符)

其中第三個參數 是裝置名稱
經過上面兩步。就已經建立了 相應的 盤符,而且系統會將對於此盤符的所有操作指令(例如 讀資料,寫資料等等)全部發給 他所關聯的裝置。也就是前面講的諸如minicd12 這樣的裝置。而發給裝置其實就是發給建立裝置的驅動(一個驅動可建立多個裝置)。所以系統會把對於新建立的盤符的所有操作都發給由minicd.sys所對應的驅動對象。並準確分發到驅動的各個常式。因此,此時只要在minicd.sys的各常式中對所發過來的操作指令進行類比。並按規定返回相應的資料即可實現虛擬了。例如。當對盤符進行雙擊查看等操作時,其實,系統會將此操作封裝成一個IRP。並發送給 minicd.sys裡的 IRP_MJ_READ 常式。這樣你只須在這個常式裡,分析 IRP 裡的 指令,並對 ISO檔案進行操作,並將得到的結果返回給作業系統就行了      

雖然到這裡,虛擬工作已經做完了。但要想正常工作還得使用  
BroadcastSystemMessage  向系統廣播訊息 
在minicd.exe 的實際調用是這樣的
DEV_BROADCAST_VOLUME minicd;
minicd.dbcv_size = 0x00000012;
minicd.dbcv_devicetype = 0x00000002;
minicd.dbcv_reserved = 0x00000001;
minicd.dbcv_unitmask = 0x00001000;
minicd.dbcv_flags = 0x0002;
PDEV_BROADCAST_VOLUME pminicd = &minicd;
BroadcastSystemMessage(
      BSF_IGNORECURRENTTASK,
      BSM_ALLCOMPONENTS,
       WM_DEVICECHANGE,
       0x8000,  //DBT_DEVICEARRIVAL
(LPARAM)pminicd);

 這個函數的原型
long BroadcastSystemMessage(     

    DWORD dwFlags,  // 標誌值,用於設定一些廣播方式
    LPDWORD lpdwRecipients, // 指定訊息的接收者
    UINT uiMessage, // 訊息編號(訊息類型)
    WPARAM wParam, // 擴充訊息的資訊
    LPARAM lParam // 一般根據wParam的不同傳不同
);

這個函數其實沒什麼好講的。和經常使用的SendMessage 等訊息處理函數沒什麼區別。在這裡只根據實際的參數作些說明。
在minicd.exe 裡使用這個函時
dwFlags 傳的是 BSF_IGNORECURRENTTASK 就是訊息不發給自己。
lpdwRecipients是 BSM_ALLCOMPONENTS 就是向所有的組件廣播訊息,主要是向 WINDOWS資源管理員廣播了。
uiMessage是 WM_DEVICECHANGE 也就是講訊息類型是 裝置改變 訊息。這個訊息是系統的內部訊息。
WParam 是 0x8000 你也可以直接寫DBT_DEVICEARRIVAL.不過要引用一下dbt.h標頭檔 就是裝置已經到達的意思。
LParam 是一個 DEV_BROADCAST_VOLUME 類型的指標,DEV_BROADCAST_VOLUME 類型是對 DEV_BROADCAST_HDR的擴充。定義如下
typedef struct _DEV_BROADCAST_VOLUME {
  DWORD dbcv_size;
  DWORD dbcv_devicetype;
  DWORD dbcv_reserved;
  DWORD dbcv_unitmask;
  WORD dbcv_flags;
} DEV_BROADCAST_VOLUME,
 *PDEV_BROADCAST_VOLUME;
 
DEV_BROADCAST_HDR 的定義如下
typedef struct _DEV_BROADCAST_HDR {
  DWORD dbch_size;
  DWORD dbch_devicetype;
  DWORD dbch_reserved;
} DEV_BROADCAST_HDR,
 *PDEV_BROADCAST_HDR;

其中dbch_size 指示這個結構的大小。如果是用在擴充結構中,其實就是此結構大小加上擴充結構其它部分的大小之和,其實也就是擴充結構的大小了。如在這裡
DEV_BROADCAST_VOLUME minicd;
minicd.dbch_size = 0x00000012;  是 0x12 也就是18個位元組。這明顯是 DEV_BROADCAST_VOLUME 的大小。
dbch_devicetype 是指 裝置類型 。使用的是什麼擴充結構裝置類型就是什麼。在這裡使用的是 DEV_BROADCAST_VOLUME 故而是 DBT_DEVTYP_VOLUME(0x2)
dbch_reserved 保留使用的。這裡 設定 為  0x1

這是 DEV_BROADCAST_HDR 的結構介紹,所有的擴充其的結構的第一項必須是 DEV_BROADCAST_HDR 所以我們看 DEV_BROADCAST_VOLUME的定義的前三項和 DEV_BROADCAST_HDR 是一樣的。只是名字不同而已。
下面再介紹一下。DEV_BROADCAST_VOLUME 擴充的兩個屬性。看 DEV_BROADCAST_VOLUME的第四項。
dbcv_unitmask 這一項很重要。具體的意思不好解釋。應該類似於網路裡的掩碼。這是一個整型值。也就是說有32位。從右往左數。每一位都映射一個盤符。如果右邊第一個位為0
的話,就是指A盤。依次類推,第二個就是B盤。
還記得這句話不
WCHAR outBuffer[501] = L"MF:\\DevelopeTool\\SQL2000SP4.ISO";
從前面介紹知道,我們要映射的盤符是M盤。那麼自然應該是在 M減去A 的那個位上是1. 那麼實際上也就是 1 << (M-A) 也就是 2 的 (M-A) 次冪。所以為了測試,當時並沒有使用公式計算。只是硬寫了一個 minicd.dbcv_unitmask = 0x00001000 進去。 很明顯 0x1000 就是 2 的 (M-A)=12 次冪。 
最後一項  dbcv_flags  可以有兩個選擇
DBTF_MEDIA
0x0001
DBTF_NET
0x0002

這裡選擇 DBTF_NET  直接 寫 0x0002 如果要寫DBTF_NET  需要引用 dbt.h

好了。到了這裡。看一下,是不是M盤已經被映射出來了。當然如果你的M盤已經被佔用了。你就得換一個盤作測試用了。如果換盤的話。知道怎麼換吧。前面講的已經很清楚了。只須在DeviceIOControl的lpOutBuffer裡的UNICODE字串的第一個字元改成相應的盤符名就行了。當然還要記的是要把 BroadCastSystemMessage 的參數lParam DEV_BROADCAST_VOLUME 結構的 dbcv_unitmask 重新計算一下。否則映射會不成功的。
通過他們的對於實際傳進去的參數的介紹。其實這個函SHU在這裡的作用已經很明顯了。就是向系統中除了自己以外的所有的程式啊,驅動啊,反正所有的對象廣播一條訊息。就是講,有裝置改變(WM_DEVICECHANGE)了,是什麼改變呢。是裝置已經到達(DBT_DEVICEARRIVAL)了.那麼他的具體資訊呢。就是 到達的是M盤,是DBT_NET網路型磁碟機等(DEV_BROADCAST_VOLUME結構裡的項)。這樣WINDOWS資源管理員也會接到這樣一個訊息。他就會在我的電腦裡建立一個驅動盤符了
好了。到這裡,在驅動(minicd.sys)已經被安裝和啟動的情況下已經可以建立一個虛擬光碟機了。
雖然有點成就感,但目前還有很多工作沒有做。例如。如果是在一台新機子上,也沒有運行MINICD.EXE 這樣驅動不就沒有安裝也沒有啟動了。這樣我們不就不可以和裝置進行通訊。當然不用怕。後面,我們繼續講。怎麼在沒有驅動的情況下。動態安裝和啟動這個驅動(minicd.sys)。
除了安裝和啟動驅動,還有一個就是映射了怎麼去除映射的問題也留在後面補吧。現在已經快一點了。最近得早點睡。工作要緊啊。要不然明天又沒精力了。
聲明一下:本文僅是用於交流虛擬光碟機的技術。對此所造成的一切後果。本人概不負責      

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.