小華的部落格: Windows 程式相關

搜尋此網誌

網頁

顯示具有 Windows 程式相關 標籤的文章。 顯示所有文章
顯示具有 Windows 程式相關 標籤的文章。 顯示所有文章

星期四, 9月 12, 2013

DosBox 模擬器 : 如何自己Compiler


        如果各位是BIOS工程師,但有時候可能有需要在 64 bit OS底下去執行Debug32.exe來看看一些組合語言的東西,但是卻發現在64 bit OS底下,Debug32.exe這隻程式是不能被執行的!

為了解決這個問題,我們通常都會去抓DosBox模擬器來使用,而這邊不是教大家去哪抓,而是跟大家說怎麼去自己抓回來Doxbox source code回來,然後使用VisualStudio 2010 自己Compiler 這個好用的工具。

首先,我們需要去抓Doxbox source code跟一些Library,而 DosBox source code 可以透過SVN抓,但是因為這個DosBox在編譯的過程中會去連結一些Library,所以就先就列出來那些會在設定環境時使用到的部分,要大家要自己先去下載:

Dosbox SVN: http://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk (我是抓Revision 3833)
 
SDL2.0:  http://www.libsdl.org/download-2.0.php  (執行DosBox.exe 需要他的SDL2.DLL)
Libpng(libpng.lib): http://www.libpng.org/pub/png/libpng.html     (我是抓1.6.3)
Zlib(zlib.lib) : http://www.zlib.net/  (我是抓Zlib 1.2.8)
WinCurses(Curses.lib): http://www.baldwin.cx/~john/projects/WinCurses.html (Debug用)

 底下是我抓下來後解壓縮放到E:\DosBox的情況:


設定DosBox Compiler環境

1.我們要先產生zlib.lib給LibPng.lib使用,所以透過VS2010 compiler的方式就是先開啟VS2010 commnd prompt,然後鍵入:
 
   E:\DosBox\zlib-1.2.8:\>  Nmake /f win32\makefile.msc
 
在編譯結束後,你會看到zlib.lib被會放在Zlib-1.2.8目錄內。


2.由於DosBox還需要連結libpng.lib,所以我們透過VS2010方案去產生libpng.lib。
   所以在開啟VS2010方案檔案前,要先把預設的ZLIB路徑指向我們前面抓的那一個zlib-1.2.8,所以先去修改zlib.props這個檔案,如下圖:
 
接著開啟方案,然後整個Rebuild Solution.
 
3.當我們需要的LIB都準備好之後,就可以開啟DOSBOX方案
 
   E:\DosBox\visualc_net\dosbox.sln
 
4. 因為我們抓的LIB版本比原本設定的還新,所以要去設定方案內的專案屬性內的C++跟Linker的設定:
 
    a. Project Properties->C++->General->Additional Include Directories -->Edit
        ../include
        ../src/platform/visualc  
        ../SDL-1.2.15/include
        ../SDL2_net-2.0.0/include
        ../lpng163
         ../zlib-1.2.8
 
   b. Project Properties->Linker->Input->Additional Dependencies-->Edit
       opengl32.lib
       winmm.lib
       ../zlib-1.2.8/zlib.lib
       ../lpng163/projects/vstudio/Debug/libpng16.lib
       ../SDL2_net-2.0.0/lib/x86/SDL2_net.lib
       ../SDL-1.2.15/lib/x86/sdl.lib
       ../SDL-1.2.15/lib/x86/sdlmain.lib
       ../curses/curses.lib
       odbc32.lib
       odbccp32.lib
       ws2_32.lib
 
5. 如果環境都設定好了,你就可以開始Rebuild,接著等一下子後你就可以看到產生DosBox.exe 到你的Release 目錄內了.
 
6. 由於產生出來的DosBox.exe 並不能單獨直接執行,還需要一些DLL跟這個執行檔放在同一個目錄內,這些DLL檔案可以從前面抓的那些LIB目錄內找的到,所以我把它集中在同一個目錄內,如下圖所示:


7. 接著你只要執行dosbox.exe (滑鼠點兩下執行)就可以看到模擬器的畫面跑出來,然後我把我自己的E Driver掛在模擬器的K drive,之後就可以在模擬器內的K:\底下找到我放在E:\Debug32.exe並且執行成功:
 
 
我的DosBox Compiler環境是在 Window8 64 bit OS,然後使用 Visual Studio 2010的 32bit compiler,所以可以看到我是選Win32 Release build,設定的目錄都是指向x86而不是x64。

 
希望這邊文章對於想要自己Compiler DosBox但是又一直不成功的人有幫助 ^^
 
 
 
 
 
 


星期三, 2月 15, 2012

Turbo C 不能在64 bit的Windows下執行 (won't work in Windows 64-bit)



由於工作上的需要,偶爾還是會去寫一些DOS的工具,所以常常會遇到比較新版的OS就無法向下相容一些軟體的問題! 像是Turbo C 3.0 就沒辦法在64 bit OS下執行,執行時你會看到上面圖片中的警告訊息。

而市面上也有一些軟體就是針對這些問題去解決,像是DOSBOX就是個模擬器,可以讓你在64 bit OS中去模擬DOS的環境。

使用方式很簡單,在安裝好DOSBOX for 64 bit軟體後直接點兩下執行他,接著要使用MOUNT指令先建立一個磁碟機,例如我的TurboC 3.0是放在E:\TCC30

指令的格式 : mount [隨便的磁碟機]  [你要映對的路徑]
所以我鍵入 mount e:  e:\

接著我就可以切換磁碟機到e: 然後進入我的tcc30的目錄中執行Turbo C.


很簡單吧~ 這個工具的下載位置在下面的連結!
下載 DOSBOX for 64 bit

星期三, 2月 01, 2012

Win7磁碟分割

如果你拿到了一台NB,裡面安裝的Image只有一個硬碟分割區,但是你又想要多分割幾個區域,你會怎麼辦?

1.Ghost/備份原來的磁區,然後重新分割後再還原?
2.使用市面上的分割軟體分割?

其實Win7已經有內建分割功能,只要透過磁碟管理工具選擇你原來的C磁碟機,然後選擇【壓縮磁碟區】把空間釋放出來就可以了,底下的圖示中就可以看到輸入你要壓縮的空間(釋放出來的大小給另一個磁碟使用),很方便吧!

星期五, 7月 15, 2011

鏈結Library/(XXX.LIB)到你的VC專案

一般我們在寫C語言程式的時候可能會使用到外部的函式庫(Library),那要如何Link Lib到你的專案中呢?!

其實,鏈結你自己的Common.lib到你的C專案中的方式有三種,但是第一步動作可能要先把
Common.lib複製到XXX.vcproj放同一個目錄中:


方式1: 直接在你的專案中的Source files那邊按右鍵選Add Exist file(選*.*),把Common.lib拉進來
方式2: 屬性->Linker->Commandline ->鍵入你的 Common.lib
方式3: 在你的.c/.cpp檔案中直接加入 #pragma comment(lib, "Common")  

星期四, 6月 30, 2011

Bluescreen 錯誤碼查詢

底下這個網站的資訊可以幫助你在發生BSOD的時候可以依照Error code來查詢微軟有沒有相關的說明,或是更新檔!

所以大家可以很方便的透過底下這個網站的資訊來解決問題,可以幫助你減少一些搜尋資料的時間:

http://www.bruchmann-web.de/zh-cn/support/windows/bluescreen/0x0000007b/kb-page/2/kb-sort/ms-description--asc/

星期日, 6月 19, 2011

Win7右鍵新增記事本消失

Win7莫名其妙右鍵新增記事本消失了,所以自己找解決方式。

解決的方式就是自己寫一個XXX.Reg的檔案,內容如下,然後在給他點兩下執行後,消失的新增記事本就又回來啦~

不過微軟作業系統老是出現一些莫名其妙的事情,要怎麼在市場跟人家競爭啦>.<




Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\.txt]
@="txtfile"
"Content Type"="text/plain"

[HKEY_CLASSES_ROOT\.txt\ShellNew]
"NullFile"=""

[HKEY_CLASSES_ROOT\txtfile]
@="Notepad"

[HKEY_CLASSES_ROOT\txtfile\shell]
[HKEY_CLASSES_ROOT\txtfile\shell\open]
[HKEY_CLASSES_ROOT\txtfile\shell\open\Command]
@="NOTEPAD.EXE %1"

星期四, 6月 16, 2011

關閉螢幕保護程式計時~

因為有些公司的網管有管制一些本機電腦的Policy...像是每隔一段時間到了,沒人使用電腦就要自動進入螢幕保護程式,以免你電腦資料被竊取。

但是,因為有時候會把工作機拿來打ONLINE GAME,尤其是掛網的時候突然給你斷線就真的OOXX了,因此想要手動關閉螢幕保護程式跟他的登入密碼,但卻發現直接修改Windows所提供的選項是無效的 ><
 
原來,有些網管可以透過你的電腦登入公司網域的時候,再偷偷幫你把一些設定調整回來,像是有些會透過WinExit.scr這個方式來強制系統每隔一段時間就跳出保護程式,看樣子我們家網管應該也是用這個方式不過鍵值被我刪除了^^Y

他調整的地方的鍵值是在:



所以只要輸入開始-->執行-->輸入regedit

把ScreenSaveActive那些值改成0就可以強制關閉螢幕保護程式每15分鐘跳出來的問題~

原來的值是:
ScreenSaveActive 1     (啟動螢幕保護程式)
ScreenSaverIsSecure 1  (螢幕保護程式結束後要詢問密碼)
ScreenSaveTimeOut 900 (15分鐘)
 
另外還有一個預設的scrnSave.scr的鍵值也被我刪除了,不過我想應該是跟這個沒有關係...

星期六, 3月 13, 2010

安裝VB6的問題

有些人可能有些經驗在安裝VB6時會出現 "找不到acme安裝程式" 的錯誤訊息!
這先就提供你一個方式可以解決這個問題.

首先你先建立一個批次檔,例如叫做VB6Setup.bat
然後輸入下面的這些內容:

@ECHO OFF
set VB6FOLDER=F:\vb6
del %temp%\*.* /y
%VB6FOLDER%\SETUP\ACMSETUP.EXE /T VB98ENT.STF /S %VB6FOLDER% /n "" /o "" /k "0000000000" /b 1

那個VB6FOLDER的路徑你可以設定成你的VB6放置的位置
例如你是放在光碟機,你就可以寫成set VB6FOLDER=E:

然後執行這個批次檔,然後你就會看到開始安裝的畫面了!

星期一, 11月 03, 2008

VS2005 Debugger

最近在學習使用VS2005環境,慢慢熟悉一些介面以及開啟專案...etc.
而這個過程中遇到了一個很基本的問題,害我突然腦袋空白了一陣子.....>.< 這個問題是在Debugger 一個程式碼的時候遇到的... 一般我們C語言中的進入點假設寫成這樣: int main ( int Argc, INT8 *Argv[] ) 我很直覺就是按下F5 開始Debugger ...但是今天突然間要在command line 放一些引數後再讓我的Debugger可以開始Trace code... 於是我腦袋突然空白了...VS2005 要在哪邊設定 ...冏 我在Debugger的Code是EFI的ProcessDSC , 因為平常都是在MakeFile設定,command line , 所以要拿來用Debuuger追code 還真的讓我不知所措了一陣子. 不過後來找到了VS2005設定的地方,也順利的可以繼續往下追了! 這個問題也就解決了! 這邊就把我找到的設定地方做個筆記,方便自己以後可以查閱! 下圖是VS2005 --> Property Page --> Configuration Properties --> Debuggung 的設定
只要在Command Arguments 填入要設定的引數,然後按下F5時就可以帶進去了!

下圖是Debugger中下斷點時可以看到Argv[1]是我設定的引數:

星期日, 8月 17, 2008

WinSAT

最近幫中華隊加油,真是越看越生氣! 今天早上看新聞還看到我們還有機會爭第六名,還一堆記者很高興的寫文章安慰自己,真是曾幾何時我們的棒球淪落到只能拿第六名!

但無論中華隊這次表現如何,他們代表著我們奮戰的精神! 畢竟比賽還沒結束,還是要奮戰到底!
創造另一個奇蹟吧,中華隊!!!

加油歸加油,今天要提到的主題是Vista 6001(SP1)下的一些內建工具中ㄧ個很實用的工具,他的名字叫做"WinSAT"

這個工具內建在Vista之中,你可以先開啟一個管理者權限的cmd,然後直接鍵入WinSAT後就可以執行,不過執行的時候有內建一些參數,所以你必須選擇你要的功能。

而這個工具有什麼用途呢? 這個工具可以當作是Benchmark 工具,用來測試你的系統! 像是CPU/Graphic ...等都可以拿來測試!


因為他是微軟內建的工具,所以由微軟的網站上可以得到相關的使用說明,請參閱下面連結:

詳細的WinSAT使用方式

由上面的連結中,你可以發現其他的內建工具的用法,找找看,或許你會發現更好玩的東西喔!

幾個測試範例:

1. WinSAT cpu d3d -objs C(20) -texshader -totalobj 100 -time 60 -v -compression
2.WinSAT cpu aurora -time 120 -v -encryption
3.WinSAT d3d -texshader -totalobj 300 -time 120 -v

Reference
http://technet.microsoft.com

星期一, 7月 28, 2008

MSN Messenger 8.5 故障排除

前幾天突然MSN 都不能連線,查詢狀態後發現是MSN Server掛掉的問題,所以一堆人早上都不能上線。

本來想說反正等一會兒後就應該可以上線了,看著其他同事們一個個都連上線後我開始懷疑我的系統有問題了。ㄧ開始只是覺得可能是我的MSN是屬於xxx@msn.com 可能伺服器不同於Hotmail ,所以想多在等等看好了,直到下了班回到家使用了較舊版的MSN 後才發現這是MSN 8.5的問題...

因此,自己思考了一下我在這些時間中系統變更了哪些東西:
1.安裝了小紅傘掃毒程式
2.更新了Windows Installer 3.0
3.更新了MSN8.5
4.掃了一些廣告 & Virus
5.安裝了MSNEdit

在判斷後覺得,應該是更新MSN 8.5 的問題,因為其他的部份會有關係的地方大概就是掃毒程式防火牆的設定,但是把防火牆關閉後還是無法用MSN 8.5 登入,可是我使用MSN8.5 工具->選項->連線設定->Http測試後,MSN回報的訊息是"成功,你可以連線至MSN 服務",因此我不覺得我的網路連線有問題(ㄧ般IE/Firefox 也可以正常顯示網頁),也不覺得是MSN伺服器的問題,因為點選MSN登入畫面上的"服務狀態"的回報結果是 【所有系統皆穩定執行中。

如此令人詭異的測試結果讓我覺得應該找個舊版的MSN試試看,因此我拿了XP內建的MSN登入,結果如同預期的是可以正常登入,所以強烈的懷疑是MSN 8.5的設定有問題。

懷疑是MSN 8.5的設定有問題那會是誰造成的? 原本我是懷疑MSNEdit造成的,但是在我安裝MSNEdit前就已經不行登入,所以我還是懷疑是更新了 MSN 8.5 後的設定與我原先的MSN相衝突。

因此,這時候就使用了無敵刪除大法,呼叫出Regedit(開始->執行,鍵入"Regedit") ,然後把下面的鍵值找出來,並且全部刪除看看:

HKEY_CURRENT_USER/Software/Microsoft/MSNMessenger

刪除後,奇妙的事情就發生了,MSN 8.5能登入了....真是令人OOXX#>X@!....

不管如何,MSN又可以正常使用了......^^

MSN 故障排除的幾個要點:
1.確認IE 的Proxy 設定,因為MSN會參考他,另外如果你的Proxy需要使用者名稱/密碼,請至MSN->工具->選項->連線->連線設定, Http 輸入

2.確認IE設定正確 : 工具->網際網路選項->進階 使用SSL2.0與SSL3.0有打勾

3.確認防火牆設定 : 區域連線->內容->進階->Windows防火牆 ->設定值->例外
程式和服務: 新增程式 ->選Windows Live Messenger

如果上述的方式不行,則可以試試看:
1.關閉Proxy
2.關閉防火牆
3.刪除Reg鍵值
4.連絡你們家的MIS

[Note] MSN 8.5 軟體需求
XP SP2 (如果你不是SP2的話可能會不能用)
WindowsInstaller 3.0 (如果你想網路更新的話)

星期四, 5月 15, 2008

Windows下存取4G Memory方式

存取的方式有很多種,我就列幾種比較常看到的方式:

1. WinIo -有開放原始碼,有興趣的可以研究一下,透過Driver層下去做

2. 呼叫NTDll.dll內的函數 - 這是原本我在XP的做法如下!

wchar_t strPath[30]=L"\\device\\physicalmemory";

LoadLibrary("ntdll.dll");

然後使用下列函數:
ZwOpenSection
ZwMapViewOfSection
ZwUnmapViewOfSection

這些函數可以對實體記憶體映射,不過這個方式在Vista下會無法使用,看樣子VISTA基於安全性考量已經這些功能拿掉了!

3.而在Vista下我目前是直接寫IO Driver,然後AP去Call我自己寫的Driver來讀,作法其實是如同WinIO.. ,只是我把我要的功能抽取出來而已,這個方式就是我用來撰寫DumpBIOS這個工具的作法,原本是用方式2,但是Vista不支援因此才改成方式3,你可能會問我為什麼不用方式1 ?
沒為什麼,就是想自己寫個IO Driver而已~~

Reference
Microsoft
Google

星期四, 5月 08, 2008

DIY~~Dump BIOS code

今天幫同事寫了一個Dump BIOS code的程式在Vista下去把BIOS code dump 出來,雖然是一個很簡單的程式但是卻出現一些小問題啦!

目前我是透過VB6去繪製畫面,然後透過C撰寫的IO.DLL與IO.SYS來存取4G頂端記憶體位址線的內容,由於VB每次存取時都是4Bytes 為單位,然後我又會去更新畫面,因此Dump 2MB大小的BIOS要花掉好幾分鐘的時間 ...光是看到就傻眼了!

不過目前還沒去解決這個問題,因為懶的去改C的部份,不過也算是一種體驗啦!
因為沒實做過都不知道自己會遇到什麼問題,呵呵!!!


或許你會問我說BIOS廠商不是提供WinFlash可以去Dump了嗎? 幹麻還自己搞一個,因為多點選擇嘛! (DOS下的還沒寫,找個時間在寫一下 >.<) 而且我目前拿到的P廠商WinFlash還有問題,還沒辦法做這部份的動作,所以就自己DIY囉~~

可能你還會問我說Dump出來幹麻? 因為我要拿來比對BIOS 啦,由於有些設定BIOS是開機後才會回寫回去BIOS ROM,所以進入OS後把BIOS傾印出來後比對原來燒進去的BIOS,這樣子就可以知道在Runtime過程中BIOS回寫了哪些東西回去BIOS ROM(因為遇到DMI字串找不到的問題,所以比對一下目前BIOS是放在哪邊)。

星期三, 4月 30, 2008

S3/S4 測試工具開發

源由:


前幾個星期為了解一個VS Mode (設定1分鐘後關閉螢幕) bug 所以自己寫了一個Win32工具去檢查CMOS RTC alarm 和RTC interrupt 的設定值還有監控RTC Enable bit設定值(PMBase),而這個問題的徵狀是當系統從S4 Resume後,等個10秒後就會看到螢幕被關閉。


追蹤後發現這應該是Vista的計時器的問題,他所謂的一分鐘是指"當OS沒收到任何event後開始計時",所以當進入S4的時候計時器可能已經跑了35秒,而當系統從S4 Resume後計時器會繼續計時,因此等個10幾秒就進入VS mode了。


會被開這條Bug是因為開Bug的人認為計時器應該重新計算,所以參考了一些其他機種做法後決定,使用相同的解決方式去解決(Notify Power button)這個問題。


而在此時我們的T學長看到我個工具,因此問我說有沒有辦法寫一個自動進入S3/S4且可以定時wakeup的工具...


原本在Vista底下就已經有工具可以測試,而我們的客戶也有撰寫一個工具也可達到這個功能,另外ALi也有一個類似的工具也做的到這個功能,因此是有機會可以自行開發出來這個工具。


實驗ㄧ過程:

原先我想的很單純,在BIOS角度就是去設定好RTC alarm時間,然後去改變RTC Enable bit,接著呼叫API讓系統進入S3/S4就應該可以了,不過實驗後發現OS會把RTC Enable 清除,所以就算我設定了也沒有用 >.<


實驗二過程:

秉持著研究的精神,所以我認為OS會去清除RTC enable bit就代表OS才能決定這個bit的狀態,我沒辦法透過Ring 0層的AP直接去修改暫存器值,因此他ㄧ定有方式去控制; 查詢了相關資訊後發現有API可以做這些事情,因此問題就只是在如何用這些API而已。


結論:

經過實驗過後,這些API真的可以達到我需要的功能,所以也只能說Windows真的管很多,想要偷偷修改暫存器都不行, 呵呵!


相關的API:

CreateWaitableTimer
SetWaitableTimer
SetSuspendState
WaitForSingleObject
CancelWaitableTimer
CloseHandle

隨便寫的程式,所以畫面看起來很難看 ^^!

Reference
Microsoft 網站

星期一, 12月 24, 2007

VC++ 與Windows Registry 註冊表

昨天因為工作需要寫了一個小工具去修正註冊表內的鍵值,所以自己留下一些筆記在部落格。

註冊表的存取方式由微軟文件上說明得知,需要做3種步驟:
1. 得到Handle
2. 存取你要的鍵值
3.關閉Handle

而步驟1中,最主要是透過RegCreateKey與RegOepnKey來得到Handle,而這兩種方式的不同點在於RegCreateKey會去搜尋子鍵是否存在,如果不存在時會建立一個新的子鍵,而另一個API則是找不到就找不到。

步驟2中,我們如果要去設定鍵值ㄧ般都會使用RegSetValueEx() 其中它裡面的參數有分成不同型態的鍵值,像是REG_DWORD、REG_SZ...etc,使用時需要步驟1 所得到的Handle。

步驟3中則是去關閉你開啟的Handle,因此這3個步驟必須合在一起做。

底下是我把這3個步驟整理成副程式,使用時直接呼叫就可以了:
1.SetRegValueBy_REG_DWORD();
2.SetRegValueBy_REG_SZ();
3. SetRegValueBy_REG_MULTI_SZ();

void SetRegValueBy_REG_DWORD(LPCSTR szKeyPath,LPCSTR szKeyName,DWORD *dwData)
{
HKEY hk;
if (RegCreateKey(HKEY_LOCAL_MACHINE,szKeyPath, &hk))
OutputDebugString("error!");

if (RegSetValueEx(hk,
szKeyName,
0,
REG_DWORD,
(LPBYTE) dwData,
sizeof(DWORD)))
OutputDebugString("error!");

RegCloseKey(hk);
}

void SetRegValueBy_REG_SZ(LPCSTR szKeyPath,LPCSTR szKeyName,LPCSTR keyValue)
{
HKEY hk;
if (RegCreateKey(HKEY_LOCAL_MACHINE,szKeyPath, &hk))
OutputDebugString("error!");

if (RegSetValueEx(hk,
szKeyName,
0,
REG_SZ,
(BYTE*)(LPCSTR) keyValue,
strlen(keyValue)))
OutputDebugString("error!");

RegCloseKey(hk);
}

void SetRegValueBy_REG_MULTI_SZ(LPCSTR szKeyPath,LPCSTR szKeyName,LPCSTR keyValue)
{
HKEY hk;
if (RegCreateKey(HKEY_LOCAL_MACHINE,szKeyPath, &hk))
OutputDebugString("error!");

if (RegSetValueEx(hk,
szKeyName,

0,
REG_MULTI_SZ,
(BYTE*)(LPCSTR) keyValue,
strlen(keyValue)))
OutputDebugString("error!");

RegCloseKey(hk);
}


由副程式內可以得知,我是存取主鍵"HKEY_LOCAL_MACHINE"內的子鍵,所以如果要存取不同的主鍵時,要修改副程式內的主鍵。

底下是呼叫時的範例:

BOOL MyPatch()
{
LPCSTR szKeyPath1="SOFTWARE\\Microsoft\\WindowsNT\\MyTest";
LPCSTR szKeyName1="Label";
DWORD dwData=11;

SetRegValueBy_REG_DWORD(szKeyPath1,szKeyName1,&dwData);

return 0;
}

我Win32 的程式不好,雖然上面的程式碼可以用,但是如果有發現錯誤的地方,還是請大家幫忙指正並且告訴我~~~感恩喔!

星期四, 8月 30, 2007

WDM 筆記2

自己學習WDM果然還是有許多問題無法解決,也只能邊學邊做筆記,做的筆記也不知道是對是錯,只能說WDM真難搞,難怪外面的WDM課程都那麼貴。

//----------------------
// ACP CreateFile
//----------------------
CreateFile() 可以去取得某個Driver的Handle,而這個API使用的是WDM Driver中的外部名稱,但是一般Driver命名方式除了直接給名稱之外還可以使用GUID(一串數字),而這兩種差別在下面比較:
外部名稱:
(1) 直接命名 : \\DeviceDos\\MyDriver
(2) GUID : DEFINE_GUID GUID_DEVICE_MYDRIVER 1234-ABCD-7890123ABCD-....
所以如果Driver是使用GUID,則必須透過一些方式轉換成CreateFile()可以使用的格式。

//------------------------
// 外部名稱的取得
//------------------------
想要對某個Driver送IRP,首先就要利用CreateFile()取得Driver Handle,而這個函數又需要知道Driver的外部名稱,那如何知道Driver的外部名稱是什麼?
這邊又要把Driver分成兩種:
(1) Inbox Driver: 這種型態的驅動程式指的是OS本身提供的,那這些Driver幾乎都是GUID命名,而這個GUID可以在DDK中找到,找xxx.h內的宣告,或是直接搜尋關鍵字 "DEFINE_GUID" or "GUID_DEVICE_HDD"
其中"GUID_DEVICE_HDD"也可以是"GUID_DEVICE_BATTERY",看你是哪個設備的Driver你就打什麼名稱,如果找不到就要另外想辦法囉。

(2) Non-Inbox Driver : 這類的驅動程式就是指廠商自己寫的,例如ATI...etc. 而這類的驅動程式一般廠商會提供GUID,或是在廠商提供的xxx.inf 內搜尋。

//-------------------------------
// Release Driver
//-------------------------------
一般Release Driver會給3個檔案:
(1) xxx.inf : 這是類似Script的東西,用來把Driver的一些資訊告訴OS,也就是會安裝一些資訊到Registry之中(例如 : ...\Service\{ServiceName}),而INF檔案裡面有其格式,可參考MSDN說明,其中就包含Driver的GUID,另外在[DeviceList]下面宣告的就是所謂的Hardware ID。
(2) xxx.sys : Driver COnpiler好之後的檔案,也就是實際的程式碼檔案。
(3) xxx.cat : 當經過WHQL後會有這個檔案可以使用,OS就會認定這個Driver是屬於經過認證的驅動程式,當然也可以做一個假的來測試。

//--------------------------------
// Driver Stack
//--------------------------------
Filter-DO: 過濾Driver
Function-DO : 一般設備的驅動程式都稱為Function-DO
PDO: Bus Driver

一般PDO還可以看做下一層的Driver,因為OS會把Driver堆疊起來,所謂的堆疊就是利用DeviceObject內的某個LowerDeviceObject欄位指向另一個DeviceObject,而LowerDeviceObject 指過去的Driver就是PDO,也就是下一層Driver,如果剛好是Bus Driver,那麼PDO=Bus Driver...

NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
{
NTSTATUS ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo);
....
pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
....
}

由範例程式可以看到當PnP Manager 通知Driver去執行AddDevice()函數時,會跟我們的Driver說下一層的PDO是誰(當成參數傳給AddDevice()),然後我們先去為我們的Driver建立一個DeviceObject,這個DO也就是FDO,然後把我們的FDO 連接(Attach)在某個PDO上面,這樣子是不是就長的很像Stack ?

星期三, 8月 29, 2007

WDM 筆記

最近為了撰寫ACPI Driver所以先把之前WDM Driver的筆記整理一下:
//--------------------------
// IRP 用途
//--------------------------
IRP, I/O Request Packet : 內含command, data的一組封包。系統和Driver溝通時使用。

例如AP端送出IRP,內涵IOCTL_XXXX指令,以及一些參數
對應的Driver會去分析指令然後做對應的動作:
Switch(IRP->Command)
{
case IOCTL_XXXX :
Do Somethings here
case: ...
}
//---------------------------
//WDM Driver的Entry Point
//---------------------------
NTSTATUS
DriverEntry( IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
...
//在這邊去註冊你的Routine到DriverObject資料結構內
DriverObject->DriverUnload = MyIo_Unload;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyIo_CreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyIo_Dispatcher;
...
}

//---------------------------
// WDM Driver 命名方式有兩種:
//---------------------------
1.內部名稱 (在你使用IoCreateDevice會用到) : Kernel 使用
\\Devices Kernel Mode定義
2.外部名稱(symbolic link name,給User應用程式的CreateFile使用) : User端使用
\\DosDevices

#define MYIO_NT_DEVICE_NAME L"\\Device\\MyIO"
#define MYIO_DOS_DEVICE_NAME L"\\DosDevices\\MyIO"

簡單的說就是OS 會把我們寫的Driver載入到記憶體中,然後會幫我們產生一個Driver Object資料結構在記憶體中,然後把這個資料結構位址當作參數傳給我們的DriverEntry(),接著我們就會設定資料結構中的一些函數指標指向我們Driver記憶體中的函數位址。EX: DriverObject->DriverUnload = MyIo_Unload;

當Driver Object結構設定好之後(或稱註冊)我們會去呼叫IoCreateDevice()函數,這個函數會在OS管理的Namespace中插入一個名稱,然後Kernel Driver層如果要找我們Driver時候就會去找這個名稱,而這個名稱就是內部名稱。

另外當應用程式需要對我們撰寫driver送出一些IRP的時候,其實也是透過"名稱"的方式來找到我們註冊進去的函數,然後OS才會把我們的IRP送給Driver Object內指標所指向的函數來處理,因此為了不要混亂掉,所以用另一種命名方式給應用程式使用,這就是外部名稱。

//-----------------------
// WDM Driver被載入的時機
//-----------------------
1. OpenSCM 方式
我們自己寫的Driver可以不用到到Windows目錄下,當我們需要這個Driver來使用的時候,可以透過SCM所提供的一些服務,先在登入檔SYSTEM\CurrentControlSet\Services 下面替我們的Driver建立一個鍵值,然後利用SCM 的服務去把我們的Driver載入到記憶體中,接著呼叫DriverEntry(),之後我們就可以透過AP對這個Driver 送出IRP

2. PnP Driver方式
對照 Registry 和 INF 檔,找到對應的driver,因為每個PnP Device都有自己的ID,類似PCI ID或是Vendor ID...等,當系統偵測到Device時,就會去登入檔內找到對應的Driver,所以自己撰寫的Driver也要撰寫一個xxx.inf 用來安裝這些資訊。
(1) 當系統偵測到device,就會將對應的driver載入到記憶體中,接著呼叫driver的DeviceEntry()
(2) 接著系統的PnP Manager 會去呼叫driver中的AddDevice() (註冊在Driver Object中)
(3) 處理AP的IRP或是PnP Manager 的IRP,或是Device 的INT 請求...等

3.非 PnP Driver
也是對照Registry 和INF 檔找到對應的Driver,但是與PnP Driver差別在於沒有PnP ID,因此OS不會自動找到Driver且載入Driver,而是User自己要利用新增設備(Add New Hardware Wizard)來增加Device,接著OS也是對照Registry 和INF 檔找到對應的Driver來載入到記憶體中。

星期一, 7月 30, 2007

動態記憶體(3)-撰寫輔助程式

前面兩篇文章提到如何去找到動態記憶體指標的程式碼位址,然後注入一段程式碼來取得指標ESI的位址,如果你在Softice 中修改後,並且回到遊戲中去驗證沒問題後(驗證的方式就是回到遊戲中隨便更新一下血量,或是重新啟動遊戲讓他重新配置一個指標位址,接著你可以利用GameMaster直接去讀取57BF30h這個位址的內容,這個內容=ESI,所以你要自己加上228h 的位址才會存放你的血量值),你就可以開始動手寫輔助程式了。

ESI+228h=血量位址

如何撰寫輔助程式呢? 這邊會用到的工具:
VC6 or VB6 or...等 (看你自己習慣用哪一種)
Windosw API (自己翻手冊查相關說明)
Softice (用來看機器碼)

簡單說就是利用ReadProcessMemory 或是WriteProcessMemory方式去對記憶體寫入值,而寫入的值是一堆的機器碼(因為你沒辦法像Softice一樣直接修改,所以只好用機器碼的方式注入你的程式碼到修改的位址去),所以你透過SoftIce 去修改好程式碼後,要記得把機器碼抄錄下來(例如: D 44717A),然後用這兩個API寫進去你要修改的位址。

VC6的方式 :
#define MY_CODE1 0xE9
#define MY_CODE2 0x34 //這部分是要寫入的機器碼的常量定義
.........................

//-----------------------------------------------------------------------------//
DWORD A1 =MY_CODE1;
DWORD A2 =MY_CODE2;
..............

//0x0044717A
WriteProcessMemory(nOK,(LPVOID)0x0044717A+0,&A1,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0044717A+1,&A2,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0044717A+2,&A3,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0044717A+3,&A4,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0044717A+4,&A5,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0044717A+5,&A6,1,NULL);
//0057BF00
WriteProcessMemory(nOK,(LPVOID)0x0057BF00+0,&B1,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0057BF00+1,&B2,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0057BF00+2,&B3,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0057BF00+3,&B4,1,NULL);
WriteProcessMemory(nOK,(LPVOID)0x0057BF00+4,&B5,1,NULL);
....

VB6方式:
A_Start = &H0044717A '要修改成JMP的地址
A_End = &H0057BF00 '跳躍JMP到的地址

'&H0044717A
Call WriteProcessMemory(A_Start + 0, &HE9, 1)
Call WriteProcessMemory(A_Start + 1, &H3D, 1)
Call WriteProcessMemory(A_Start + 2, &H31, 1)
Call WriteProcessMemory(A_Start + 3, &H30, 1)
...

動態記憶體(2)-修改流程

前一篇文章中我們介紹了如何透過一些工具去取得你的遊戲程式碼的位址,接著就說明一下整個修改的概念:

1. 先找到程式碼存放的位址,前面我們已經找到了程式碼存放的位址是在44717Ah:
001B:44717A MOV [ESI+228],EDX
001b:44717E ....

2. 修改44717A這個位址的程式碼
001B:44717A JMP 57BF00 <--跳到我們指定的一塊記憶體位址
001B:44717D NOP <--由於修改的JMP 57BF00 指令長度不足原來的,所以增加NOP來補足長度
001b:44717E ....

3. 修改57BF00
001B:57BF00 MOV [ESI+228],EDX <--還原原來被我們破壞掉的程式碼
001B:57BFxx MOV DWORD PTR [57BF30],ESI
001B:57BFxx JMP 44717E
001B:57BFxx

由上面的範例中可以看到整個修改的流程大致上就是如此,先找到他的程式碼的位址,修改它的程式碼,然後跳躍到我們指定的記憶體中,跳躍後我們先去還原原來的程式碼以免造成原來遊戲程式當機,接著把ESI的值存放到我們指定的記憶體位址,那麼當遊戲程式重新分配記憶體位址時,他的位址會放在ESI,而ESI的值會被我們放到我們指定的記憶體中,而我們寫的遊戲輔助程式就直接去我們指定的記憶體中就可以讀到ESI的值了。

※ Softice 修改程式碼的方式鍵入 "A 44717A" ,跟DOS的Debug.com 很像

在上述的方式中,要如何找到一塊可以使用的記憶體? 下面我就提供一個方式:
進入Softice 中,找到你的遊戲視窗名稱,接著鍵入MAP32 指令,如下所示:

:map32 lineage <--lineage 是你遊戲的名稱,這邊是範例,你看的值會不一樣
Owner Obj Name Obj# Address Size Type
lineage .text 0001 017F:00401000 ....
lineage .data 0002 0187:00560000 ....
lineage .rsrc 0005 0187:0057C000 ....

你會看到他把遊戲程式的PE檔頭的一些資訊列印出來給你看,其中.data 後面的560000h代表你的遊戲會把一些資料從記憶體位置560000h 開始存放,而我的方式是從後面自己找一塊來用,例如下一個區塊是從57C000h開始,因此我找57BF00h~57BFFFh 這一塊來當我要使用的地方。

你可以利用Softice 鍵入D 57BF00的方式去看那一塊記憶體有沒有人使用,如果沒有的話你就可以自己拿來用。

動態記憶體(1)-搜尋程式碼

玩過PC遊戲的都知道市面上有一些遊戲修改器可以做一些修改遊戲記憶體內的資訊來達到你的主角能力值變強或是有用不完的金錢...等。

今天我們要探討的是網路遊戲的修改,其實不能算是修改啦,因為對於網路遊戲來說,你無法使用這種方式去修改相關資訊,因為你的主角資訊是放在伺服器端,然後透過網路傳送資訊到你的主機,接著再更新到你的主機上面的記憶體中,例如: 血量:
CMP EDX,EDX
JZ LABLE1
MOV [ESI+228],EDX
...
LABEL1:
...
例如上面的範例,他會去檢查血量有沒有變化,如果有他才會放進去[ESI+228]的記憶體位址中,因此EDX存放著伺服器端傳送過來的血量資料,然後放到你的主機的記憶體位址[ESI+228]的地方,這邊的記憶體位址指的是位址空間,所謂的位址空間是指每個行程都有4G的虛擬記憶體位址,就好比說VC寫的程式在載入記憶體的時候會被固定放在某個位址上去,然後我們就可以知道某段位址範圍放的是"程式碼"。

上面有看到一個[ESI+228]的記憶體位址,由於網路遊戲怕你很容易就找到他的記憶體位址,所以一般會把這個位址用指標方式來重新配置記憶體,簡單說就是你每次找到血量存放的位址會不一樣,所以稱為動態記憶體方式來存放人物資訊,也因此我們要討論的是如何得到這個指標指向的位址,那麼就不用怕他一直變動了,所以由上面的範例可以知道ESI會一直變動,所以我們的目標就是得到ESI的值。

※ESI+228h = 血量位址,每個遊戲不一樣,所以不見得都是用ESI來存放,另外也不見得是+228h

在進入正題前先說明如何去得知你的遊戲是如何把這些資訊放進去記憶體中,我們會使用的工具如下:
1.WinXP
2.SoftIce
3.任何遊戲修改工具 ex: FPE, GameMaster...等

(Step 1): 首先先開啟你的網路遊戲,然後利用遊戲修改工具找到你的血量的記憶體位址(找的方式跟以前一樣,就是先找一次血量,然後進入遊戲改變一下血量,然後再找一次,一直重複到剩下來的位址,或是利用人物ID去搜尋也可以),例如找到的位址是12ABAC00h, 然後按下Ctrl+D 進入SoftIce,進入SoftIce後鍵入BPM 12ABAC00 W ,意思是說設定一個中斷點,當這個記憶體位址12ABAC00 被寫入Write的時候要中斷,接著按下Ctrl+D 回到遊戲中

(Step 2): 回到遊戲後隨便改變一下你的血量(喝藥水或是放法術),然後你就會看到SoftIce 產生了一個中斷,然後進入到SoftIce 畫面(如果有攔截到,就會自動進入SoftIce,如果沒攔截到就可以能你找到的位址不對)

(Step 3): 在SoftIce 中你會看到類似下面的程式碼:
001B:44717A MOV [ESI+228],EDX
001b:44717E ....

他所代表的意思是說你的遊戲主程式載入到記憶體後負責更新血量到記憶體位址12ABAC00h的程式碼是被放在xxxx:44717A ,而程式碼是 MOV [ESI+228],EDX,
你可以在Softice 中鍵入指令 D ESI+228 ,他會把ESI+228的記憶體位址傾印出來,所以你可以在螢幕上看到你的血量值。

(Step 4) : 拿筆把44717A 這個位址抄下來,然後把你的遊戲關閉掉然後重新啟動,重新進入遊戲後重複放面的Step1~3 ,如果每次都是停在44717A,那麼就恭喜你,你已經找到他的動態指標的程式碼的地方,接著就可以想辦法取得ESI 的值了。