OllyDBG圖文入門教程_第1頁
OllyDBG圖文入門教程_第2頁
OllyDBG圖文入門教程_第3頁
OllyDBG圖文入門教程_第4頁
OllyDBG圖文入門教程_第5頁
已閱讀5頁,還剩42頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

OllyDBG入門系列(一)-認識OllyDBG

作者:CCDebuger

一、OllyDBG的安裝與配置

OllyDBG1.10版的發(fā)布版本是個ZIP壓縮包,只要解壓到一個目錄下,運行OllyDBG.exe就可以了。漢化版的發(fā)布版本是個RAR壓縮包,同樣只需解壓到一個目錄下運行OllyDBG.exe即可:

圖片1OllyDBG中各個窗口的功能如上圖。簡單解釋一下各個窗口的功能,更詳細的內(nèi)容可以參考TT小組翻譯的中文幫助:

反匯編窗口:顯示被調試程序的反匯編代碼,標題欄上的地址、HEX數(shù)據(jù)、反匯編、注釋可以通過在窗口中右擊出現(xiàn)的菜單界面選項->隱藏標題或顯示標題來進行切換是否顯示。用鼠標左鍵點擊注釋標簽可以切換注釋顯示的方式。

寄存器窗口:顯示當前所選線程的CPU寄存器內(nèi)容。同樣點擊標簽寄存器(FPU)可以切換顯示寄存器的方式。

信息窗口:顯示反匯編窗口中選中的第一個命令的參數(shù)及一些跳轉目標地址、字串等。

數(shù)據(jù)窗口:顯示內(nèi)存或文件的內(nèi)容。右鍵菜單可用于切換顯示方式。

堆棧窗口:顯示當前線程的堆棧。

要調整上面各個窗口的大小的話,只需左鍵按住邊框拖動,等調整好了,重新啟動一下OllyDBG就可以生效了。

啟動后我們要把插件及UDD的目錄配置為絕對路徑,點擊菜單上的選項->界面,將會出來一個界面選項的對話框,我們點擊其中的目錄標簽:因為我這里是把OllyDBG解壓在F:\OllyDBG目錄下,所以相應的UDD目錄及插件目錄按圖上配置。還有一個常用到的標簽就是上圖后面那個字體,在這里你可以更改OllyDBG中顯示的字體。上圖中其它的選項可以保留為默認,若有需要也可以自己修改。修改完以后點擊確定,彈出一個對話框,說我們更改了插件路徑,要重新啟動OllyDBG。在這個對話框上點確定,重新啟動一下OllyDBG,我們再到界面選項中看一下,會發(fā)現(xiàn)我們原先設置好的路徑都已保存了。有人可能知道插件的作用,但對那個UDD目錄不清楚。我這簡單解釋一下:這個UDD目錄的作用是保存你調試的工作。比如你調試一個軟件,設置了斷點,添加了注釋,一次沒做完,這時OllyDBG就會把你所做的工作保存到這個UDD目錄,以便你下次調試時可以繼續(xù)以前的工作。如果不設置這個UDD目錄,OllyDBG默認是在其安裝目錄下保存這些后綴名為udd的文件,時間長了就會顯的很亂,所以還是建議專門設置一個目錄來保存這些文件。

另外一個重要的選項就是調試選項,可通過菜單選項->調試設置來配置:

新手一般不需更改這里的選項,默認已配置好,可以直接使用。建議在對OllyDBG已比較熟的情況下再來進行配置。上面那個異常標簽中的選項經(jīng)常會在脫殼中用到,建議在有一定調試基礎后學脫殼時再配置這里。

除了直接啟動OllyDBG來調試外,我們還可以把OllyDBG添加到資源管理器右鍵菜單,這樣我們就可以直接在.exe及.dll文件上點右鍵選擇“用Ollydbg打開”菜單來進行調試。要把OllyDBG添加到資源管理器右鍵菜單,只需點菜單選項->添加到瀏覽器,將會出現(xiàn)一個對話框,先點擊“添加Ollydbg到系統(tǒng)資源管理器菜單”,再點擊“完成”按鈕即可。要從右鍵菜單中刪除也很簡單,還是這個對話框,點擊“從系統(tǒng)資源管理器菜單刪除Ollydbg”,再點擊“完成”就行了。

OllyDBG支持插件功能,插件的安裝也很簡單,只要把下載的插件(一般是個DLL文件)復制到OllyDBG安裝目錄下的PLUGIN目錄中就可以了,OllyDBG啟動時會自動識別。要注意的是OllyDBG1.10對插件的個數(shù)有限制,最多不能超過32個,否則會出錯。建議插件不要添加的太多。

到這里基本配置就完成了,OllyDBG把所有配置都放在安裝目錄下的ollydbg.ini文件中。

二、基本調試方法

OllyDBG有三種方式來載入程序進行調試,一種是點擊菜單文件->打開(快捷鍵是F3)來打開一個可執(zhí)行文件進行調試,另一種是點擊菜單文件->附加來附加到一個已運行的進程上進行調試。注意這里要附加的程序必須已運行。第三種就是用右鍵菜單來載入程序(不知這種算不算)。一般情況下我們選第一種方式。比如我們選擇一個test.exe來調試,通過菜單文件->打開來載入這個程序,OllyDBG中顯示的內(nèi)容將會是這樣:

圖片1

調試中我們經(jīng)常要用到的快捷鍵有這些:

F2:設置斷點,只要在光標定位的位置(上圖中灰色條)按F2鍵即可,再按一次F2鍵則會刪除斷點。(相當于SoftICE中的F9)

F8:單步步過。每按一次這個鍵執(zhí)行一條反匯編窗口中的一條指令,遇到CALL等子程序不進入其代碼。(相當于SoftICE中的F10)

F7:單步步入。功能同單步步過(F8)類似,區(qū)別是遇到CALL等子程序時會進入其中,進入后首先會停留在子程序的第一條指令上。(相當于SoftICE中的F8)

F4:運行到選定位置。作用就是直接運行到光標所在位置處暫停。(相當于SoftICE中的F7)F9:運行。按下這個鍵如果沒有設置相應斷點的話,被調試的程序將直接開始運行。(相當于SoftICE中的F5)

CTR+F9:執(zhí)行到返回。此命令在執(zhí)行到一個ret(返回指令)指令時暫停,常用于從系統(tǒng)領空返回到我們調試的程序領空。(相當于SoftICE中的F12)

ALT+F9:執(zhí)行到用戶代碼??捎糜趶南到y(tǒng)領空快速返回到我們調試的程序領空。(相當于SoftICE中的F11)

上面提到的幾個快捷鍵對于一般的調試基本上已夠用了。要開始調試只需設置好斷點,找到你感興趣的代碼段再按F8或F7鍵來一條條分析指令功能就可以了。就寫到這了,改天有空再接著灌。OllyDBG入門系列(二)-字串參考

作者:CCDebuger

上一篇是使用入門,現(xiàn)在我們開始正式進入破解。今天的目標程序是看雪兄《加密與解密》第一版附帶光盤中的鏡像打包中的CFFCrackme#3,采用用戶名/序列號保護方式。原版加了個UPX的殼。剛開始學破解先不涉及殼的問題,我們主要是熟悉用OllyDBG來破解的一般方法。我這里把殼脫掉來分析,附件是脫殼后的文件,直接就可以拿來用。先說一下一般軟件破解的流程:拿到一個軟件先別接著馬上用OllyDBG調試,先運行一下,有幫助文檔的最好先看一下幫助,熟悉一下軟件的使用方法,再看看注冊的方式。如果是序列號方式可以先輸個假的來試一下,看看有什么反應,也給我們破解留下一些有用的線索。如果沒有輸入注冊碼的地方,要考慮一下是不是讀取注冊表或Key文件(一般稱keyfile,就是程序讀取一個文件中的內(nèi)容來判斷是否注冊),這些可以用其它工具來輔助分析。如果這些都不是,原程序只是一個功能不全的試用版,那要注冊為正式版本就要自己來寫代碼完善了。有點跑題了,呵呵。獲得程序的一些基本信息后,還要用查殼的工具來查一下程序是否加了殼,若沒殼的話看看程序是什么編譯器編的,如VC、Delphi、VB等。這樣的查殼工具有PEiD和FI。有殼的話我們要盡量脫了殼后再來用OllyDBG調試,特殊情況下也可帶殼調試。下面進入正題:

我們先來運行一下這個crackme(用PEiD檢測顯示是Delphi編的),界面如圖:

這個crackme已經(jīng)把用戶名和注冊碼都輸好了,省得我們動手^_^。我們在那個“Registernow!”按鈕上點擊一下,將會跳出一個對話框:

好了,今天我們就從這個錯誤對話框中顯示的“WrongSerial,tryagain!”來入手。啟動OllyDBG,選擇菜單文件->打開載入CrackMe3.exe文件,我們會停在這里:

我們在反匯編窗口中右擊,出來一個菜單,我們在查找->所有參考文本字串上左鍵點擊:

當然如果用上面那個超級字串參考+插件會更方便。但我們的目標是熟悉OllyDBG的一些操作,我就盡量使用OllyDBG自帶的功能,少用插件。好了,現(xiàn)在出來另一個對話框,我們在這個對話框里右擊,選擇“查找文本”菜單項,輸入“WrongSerial,tryagain!”的開頭單詞“Wrong”(注意這里查找內(nèi)容要區(qū)分大小寫)來查找,找到一處:

在我們找到的字串上右擊,再在出來的菜單上點擊“反匯編窗口中跟隨”,我們來到這里:

見上圖,為了看看是否還有其他的參考,可以通過選擇右鍵菜單查找參考->立即數(shù),會出來一個對話框:

分別雙擊上面標出的兩個地址,我們會來到對應的位置:

*****************************************************************************************

00440F79|.BA8C104400

MOVEDX,CrackMe3.0044108C

;ASCII"WrongSerial,tryagain!"

00440F7E|.A1442C4400

MOVEAX,DWORDPTRDS:[442C44]

00440F83|.8B00

MOVEAX,DWORDPTRDS:[EAX]

00440F85|.E8DEC0FFFF

CALLCrackMe3.0043D068

00440F8A|.EB18

JMPSHORTCrackMe3.00440FA400440F8C|>6A00

PUSH0

00440F8E|.B980104400

MOVECX,CrackMe3.00441080

;ASCII"Beggaroff!"

00440F93|.BA8C104400

MOVEDX,CrackMe3.0044108C

;ASCII"WrongSerial,tryagain!"

00440F98|.A1442C4400

MOVEAX,DWORDPTRDS:[442C44]

00440F9D|.8B00

MOVEAX,DWORDPTRDS:[EAX]

00440F9F|.E8C4C0FFFF

CALLCrackMe3.0043D068

我們在反匯編窗口中向上滾動一下再看看:

00440F2C|.8B45FC

MOVEAX,DWORDPTRSS:[EBP-4]

00440F2F|.BA14104400

MOVEDX,CrackMe3.00441014

;ASCII"RegisteredUser"

00440F34|.E8F32BFCFF

CALLCrackMe3.00403B2C

;關鍵,要用F7跟進去

00440F39|.7551

JNZSHORTCrackMe3.00440F8C

;這里跳走就完蛋

00440F3B|.8D55FC

LEAEDX,DWORDPTRSS:[EBP-4]

00440F3E|.8B83C8020000

MOVEAX,DWORDPTRDS:[EBX+2C8]

00440F44|.E8D7FEFDFF

CALLCrackMe3.00420E20

00440F49|.8B45FC

MOVEAX,DWORDPTRSS:[EBP-4]

00440F4C|.BA2C104400

MOVEDX,CrackMe3.0044102C

;ASCII"GFX-754-IER-954"

00440F51|.E8D62BFCFF

CALLCrackMe3.00403B2C

;關鍵,要用F7跟進去

00440F56|.751A

JNZSHORTCrackMe3.00440F72

;這里跳走就完蛋

00440F58|.6A00PUSH0

00440F5A|.B93C104400

MOVECX,CrackMe3.0044103C

;ASCII"CrackMecrackedsuccessfully"

00440F5F|.BA5C104400

MOVEDX,CrackMe3.0044105C

;ASCII"Congrats!YoucrackedthisCrackMe!"

00440F64|.A1442C4400

MOVEAX,DWORDPTRDS:[442C44]

00440F69|.8B00

MOVEAX,DWORDPTRDS:[EAX]

00440F6B|.E8F8C0FFFF

CALLCrackMe3.0043D068

00440F70|.EB32

JMPSHORTCrackMe3.00440FA4

00440F72|>6A00

PUSH0

00440F74|.B980104400

MOVECX,CrackMe3.00441080

;ASCII"Beggaroff!"

00440F79|.BA8C104400

MOVEDX,CrackMe3.0044108C

;ASCII"WrongSerial,tryagain!"

00440F7E|.A1442C4400

MOVEAX,DWORDPTRDS:[442C44]

00440F83|.8B00

MOVEAX,DWORDPTRDS:[EAX]

00440F85|.E8DEC0FFFF

CALLCrackMe3.0043D068

00440F8A|.EB18

JMPSHORTCrackMe3.00440FA4

00440F8C|>6A00

PUSH0

00440F8E|.B980104400

MOVECX,CrackMe3.00441080

;ASCII"Beggaroff!"

00440F93|.BA8C104400

MOVEDX,CrackMe3.0044108C

;ASCII"WrongSerial,tryagain!"

00440F98|.A1442C4400

MOVEAX,DWORDPTRDS:[442C44]

00440F9D|.8B00

MOVEAX,DWORDPTRDS:[EAX]

00440F9F|.E8C4C0FFFF

CALLCrackMe3.0043D068

大家注意看一下上面的注釋,我在上面標了兩個關鍵點。有人可能要問,你怎么知道那兩個地方是關鍵點?其實很簡單,我是根據(jù)查看是哪條指令跳到“wrongserial,tryagain”這條字串對應的指令來決定的。如果你在調試選項->CPU標簽中把“顯示跳轉路徑”及其下面的兩個“如跳轉未實現(xiàn)則顯示灰色路徑”、“顯示跳轉到選定命令的路徑”都選上的話,就會看到是從什么地方跳到出錯字串處的:

我們在上圖中地址00440F2C處按F2鍵設個斷點,現(xiàn)在我們按F9鍵,程序已運行起來了。我在上面那個編輯框中隨便輸入一下,如CCDebuger,下面那個編輯框我還保留為原來的“754-GFX-IER-954”,我們點一下那個“Registernow!”按鈕,呵,OllyDBG跳了出來,暫停在我們下的斷點處。我們看一下信息窗口,你應該發(fā)現(xiàn)了你剛才輸入的內(nèi)容了吧?我這里顯示是這樣:

堆棧SS:[0012F9AC]=00D44DB4,(ASCII"CCDebuger")

EAX=00000009

上面的內(nèi)存地址00D44DB4中就是我們剛才輸入的內(nèi)容,我這里是CCDebuger。你可以在堆棧SS:[0012F9AC]=00D44DB4,(ASCII"CCDebuger")這條內(nèi)容上左擊選擇一下,再點右鍵,在彈出菜單中選擇“數(shù)據(jù)窗口中跟隨數(shù)值”,你就會在下面的數(shù)據(jù)窗口中看到你剛才輸入的內(nèi)容。而EAX=00000009指的是你輸入內(nèi)容的長度。如我輸入的CCDebuger是9個字符。如下圖所示:

現(xiàn)在我們來按F8鍵一步步分析一下:

00440F2C|.8B45FC

MOVEAX,DWORDPTRSS:[EBP-4]

;把我們輸入的內(nèi)容送到EAX,我這里是“CCDebuger”

00440F2F|.BA14104400

MOVEDX,CrackMe3.00441014

;ASCII"RegisteredUser"

00440F34|.E8F32BFCFF

CALLCrackMe3.00403B2C

;關鍵,要用F7跟進去

00440F39|.7551

JNZSHORTCrackMe3.00440F8C

;這里跳走就完蛋

當我們按F8鍵走到00440F34|.E8F32BFCFF

CALLCrackMe3.00403B2C這一句時,我們按一下F7鍵,進入這個CALL,進去后光標停在這一句:

我們所看到的那些PUSHEBX、PUSHESI等都是調用子程序保存堆棧時用的指令,不用管它,按F8鍵一步步過來,我們只關心關鍵部分:

00403B2C/$53

PUSHEBX

00403B2D|.56

PUSHESI

00403B2E|.57

PUSHEDI

00403B2F|.89C6

MOVESI,EAX

;把EAX內(nèi)我們輸入的用戶名送到ESI

00403B31|.89D7

MOVEDI,EDX

;把EDX內(nèi)的數(shù)據(jù)“RegisteredUser”送到EDI

00403B33|.39D0

CMPEAX,EDX

;用“RegisteredUser”和我們輸入的用戶名作比較

00403B35|.0F848F000000

JECrackMe3.00403BCA

;相同則跳

00403B3B|.85F6

TESTESI,ESI

;看看ESI中是否有數(shù)據(jù),主要是看看我們有沒有輸入用戶名

00403B3D|.7468

JESHORTCrackMe3.00403BA7

;用戶名為空則跳

00403B3F|.85FF

TESTEDI,EDI

00403B41|.746B

JESHORTCrackMe3.00403BAE

00403B43|.8B46FC

MOVEAX,DWORDPTRDS:[ESI-4]

;用戶名長度送EAX

00403B46|.8B57FC

MOVEDX,DWORDPTRDS:[EDI-4]

;“RegisteredUser”字串的長度送EDX

00403B49|.29D0

SUBEAX,EDX

;把用戶名長度和“RegisteredUser”字串長度相減

00403B4B|.7702

JASHORTCrackMe3.00403B4F

;用戶名長度大于“RegisteredUser”長度則跳

00403B4D|.01C2

ADDEDX,EAX

;把減后值與“RegisteredUser”長度相加,即用戶名長度

00403B4F|>52

PUSHEDX

00403B50|.C1EA02

SHREDX,2

;用戶名長度值右移2位,這里相當于長度除以4

00403B53|.7426

JESHORTCrackMe3.00403B7B

;上面的指令及這條指令就是判斷用戶名長度最少不能低于4

00403B55|>8B0E

MOVECX,DWORDPTRDS:[ESI]

;把我們輸入的用戶名送到ECX

00403B57|.8B1F

MOVEBX,DWORDPTRDS:[EDI]

;把“RegisteredUser”送到EBX

00403B59|.39D9

CMPECX,EBX

;比較

00403B5B|.7558

JNZSHORTCrackMe3.00403BB5

;不等則完蛋

根據(jù)上面的分析,我們知道用戶名必須是“RegisteredUser”。我們按F9鍵讓程序運行,出現(xiàn)錯誤對話框,點確定,重新在第一個編輯框中輸入“RegisteredUser”,再次點擊那個“Registernow!”按鈕,被OllyDBG攔下。因為地址00440F34處的那個CALL我們已經(jīng)分析清楚了,這次就不用再按F7鍵跟進去了,直接按F8鍵通過。我們一路按F8鍵,來到第二個關鍵代碼處:00440F49|.8B45FC

MOVEAX,DWORDPTRSS:[EBP-4]

;取輸入的注冊碼

00440F4C|.BA2C104400

MOVEDX,CrackMe3.0044102C

;ASCII"GFX-754-IER-954"

00440F51|.E8D62BFCFF

CALLCrackMe3.00403B2C

;關鍵,要用F7跟進去

00440F56|.751A

JNZSHORTCrackMe3.00440F72

;這里跳走就完蛋

大家注意看一下,地址00440F51處的CALLCrackMe3.00403B2C和上面我們分析的地址00440F34處的CALLCrackMe3.00403B2C是不是匯編指令都一樣???這說明檢測用戶名和注冊碼是用的同一個子程序。而這個子程序CALL我們在上面已經(jīng)分析過了。我們執(zhí)行到現(xiàn)在可以很容易得出結論,這個CALL也就是把我們輸入的注冊碼與00440F4C地址處指令后的“GFX-754-IER-954”作比較,相等則OK。好了,我們已經(jīng)得到足夠的信息了?,F(xiàn)在我們在菜單查看->斷點上點擊一下,打開斷點窗口(也可以通過組合鍵ALT+B或點擊工具欄上那個“B”圖標打開斷點窗口):

為什么要做這一步,而不是把這個斷點刪除呢?這里主要是為了保險一點,萬一分析錯誤,我們還要接著分析,要是把斷點刪除了就要做一些重復工作了。還是先禁用一下,如果經(jīng)過實際驗證證明我們的分析是正確的,再刪不遲?,F(xiàn)在我們把斷點禁用,在OllyDBG中按F9鍵讓程序運行。輸入我們經(jīng)分析得出的內(nèi)容:

用戶名:RegisteredUser

注冊碼:GFX-754-IER-954

點擊“Registernow!”按鈕,呵呵,終于成功了:

OllyDBG入門系列(三)-函數(shù)參考

作者:CCDebuger

現(xiàn)在進入第三篇,這一篇我們重點講解怎樣使用OllyDBG中的函數(shù)參考(即名稱參考)功能。仍然選擇鏡像打包中的一個名稱為CrackHead的crackme。老規(guī)矩,先運行一下這個程序看看:

呵,竟然沒找到輸入注冊碼的地方!別急,我們點一下程序上的那個菜單“Shit”(真是Shit啊,呵呵),在下拉菜單中選“TryIt”,會來到如下界面:

我們點一下那個“CheckIt”按鈕試一下,哦,竟然沒反應!我再輸個“78787878”試試,還是沒反應。再試試輸入字母或其它字符,輸不進去。由此判斷注冊碼應該都是數(shù)字,只有輸入正確的注冊碼才有動靜。用PEiD檢測一下,結果為MASM32/TASM32,怪不得程序比較小。信息收集的差不多了,現(xiàn)在關掉這個程序,我們用OllyDBG載入,按F9鍵直接讓它運行起來,依次點擊上面圖中所說的菜單,使被調試程序顯示如上面的第二個圖。先不要點那個“CheckIt”按鈕,保留上圖的狀態(tài)?,F(xiàn)在我們沒有什么字串好參考了,我們就在API函數(shù)上下斷點,來讓被調試程序中斷在我們希望的地方。我們在OllyDBG的反匯編窗口中右擊鼠標,在彈出菜單中選擇查找->當前模塊中的名稱(標簽),或者我們通過按CTR+N組合鍵也可以達到同樣的效果(注意在進行此操作時要在OllyDBG中保證是在當前被調試程序的領空,我在第一篇中已經(jīng)介紹了領空的概念,如我這里調試這個程序時OllyDBG的標題欄顯示的就是“[CPU-主線程,模塊-CrackHea]”,這表明我們當前在被調試程序的領空)。通過上面的操作后會彈出一個對話框,如圖:

對于這樣的編輯框中輸注冊碼的程序我們要設斷點首選的API函數(shù)就是GetDlgItemText及GetWindowText。每個函數(shù)都有兩個版本,一個是ASCII版,在函數(shù)后添加一個A表示,如GetDlgItemTextA,另一個是UNICODE版,在函數(shù)后添加一個W表示。如GetDlgItemTextW。對于編譯為UNCODE版的程序可能在Win98下不能運行,因為Win98并非是完全支持UNICODE的系統(tǒng)。而NT系統(tǒng)則從底層支持UNICODE,它可以在操作系統(tǒng)內(nèi)對字串進行轉換,同時支持ASCII和UNICODE版本函數(shù)的調用。一般我們打開的程序看到的調用都是ASCII類型的函數(shù),以“A”結尾。又跑題了,呵呵?,F(xiàn)在回到我們調試的程序上來,我們現(xiàn)在就是要找一下我們調試的程序有沒有調用GetDlgItemTextA或GetWindowTextA函數(shù)。還好,找到一個GetWindowTextA。在這個函數(shù)上右擊,在彈出菜單上選擇“在每個參考上設置斷點”,我們會在OllyDBG窗口最下面的那個狀態(tài)欄里看到“已設置2個斷點”。另一種方法就是那個GetWindowTextA函數(shù)上右擊,在彈出菜單上選擇“查找輸入函數(shù)參考”(或者按回車鍵),將會出現(xiàn)下面的對話框:

看上圖,我們可以把兩條都設上斷點。這個程序只需在第一條指令設斷點就可以了。好,我們現(xiàn)在按前面提到的第一條方法,就是“在每個參考上設置斷點”,這樣上圖中的兩條指令都會設上斷點。斷點設好后我們轉到我們調試的程序上來,現(xiàn)在我們在被我們調試的程序上點擊那個“CheckIt”按鈕,被OllyDBG斷下:

00401323|.E84C010000

CALL<JMP.&USER32.GetWindowTextA>

;GetWindowTextA

00401328|.E8A5000000

CALLCrackHea.004013D2

;關鍵,要按F7鍵跟進去

0040132D|.3BC6

CMPEAX,ESI

;比較

0040132F|.7542

JNZSHORTCrackHea.00401373

;不等則完蛋

00401331|.EB2C

JMPSHORTCrackHea.0040135F

00401333|.4E6F77207>

ASCII"Nowwriteakeyg"

00401343|.656E20616>

ASCII"enandtutandy"

00401353|.6F7527726>

ASCII"ou'redone.",0

0040135F|>6A00

PUSH0

;Style=MB_OK|MB_APPLMODAL

00401361|.680F304000

PUSHCrackHea.0040300F

;Title="Crudd'sCrackHead"

00401366|.6833134000

PUSHCrackHea.00401333

;Text="Nowwriteakeygenandtutandyou'redone."

0040136B|.FF7508

PUSHDWORDPTRSS:[EBP+8]

;hOwner

0040136E|.E819010000

CALL<JMP.&USER32.MessageBoxA>

;MessageBoxA

從上面的代碼,我們很容易看出00401328地址處的CALLCrackHea.004013D2是關鍵,必須仔細跟蹤。而注冊成功則會顯示一個對話框,標題是“Crudd'sCrackHead”,對話框顯示的內(nèi)容是“Nowwriteakeygenandtutandyou'redone.”現(xiàn)在我按一下F8,準備步進到00401328地址處的那條CALLCrackHea.004013D2指令后再按F7鍵跟進去。等等,怎么回事?怎么按一下F8鍵跑到這來了:

00401474$-FF252C204000

JMPDWORDPTRDS:[<&USER32.GetWindowText>

;USER32.GetWindowTextA

0040147A$-FF2530204000

JMPDWORDPTRDS:[<&USER32.LoadCursorA>]

;USER32.LoadCursorA

00401480$-FF251C204000

JMPDWORDPTRDS:[<&USER32.LoadIconA>]

;USER32.LoadIconA

00401486$-FF2520204000

JMPDWORDPTRDS:[<&USER32.LoadMenuA>]

;USER32.LoadMenuA

0040148C$-FF2524204000

JMPDWORDPTRDS:[<&USER32.MessageBoxA>]

;USER32.MessageBoxA

原來是跳到另一個斷點了。這個斷點我們不需要,按一下F2鍵刪掉它吧。刪掉00401474地址處的斷點后,我再按F8鍵,呵,完了,跑到User32.dll的領空了。看一下OllyDBG的標題欄:“[CPU-主線程,模塊-USER32],跑到系統(tǒng)領空了,OllyDBG反匯編窗口中顯示代碼是這樣:

77D3213C6A0C

PUSH0C

77D3213E68A021D377

PUSHUSER32.77D321A0

77D32143E87864FEFF

CALLUSER32.77D185C0

怎么辦?別急,我們按一下ALT+F9組合鍵,呵,回來了:

00401328|.E8A5000000

CALLCrackHea.004013D2

;關鍵,要按F7鍵跟進去

0040132D|.3BC6

CMPEAX,ESI

;比較

0040132F|.7542

JNZSHORTCrackHea.00401373

;不等則完蛋

光標停在00401328地址處的那條指令上?,F(xiàn)在我們按F7鍵跟進:

004013D2/$56

PUSHESI

;ESI入棧004013D3|.33C0

XOREAX,EAX

;EAX清零

004013D5|.8D35C4334000

LEAESI,DWORDPTRDS:[4033C4]

;把注冊碼框中的數(shù)值送到ESI

004013DB|.33C9

XORECX,ECX

;ECX清零

004013DD|.33D2

XOREDX,EDX

;EDX清零

004013DF|.8A06

MOVAL,BYTEPTRDS:[ESI]

;把注冊碼中的每個字符送到AL

004013E1|.46

INCESI

;指針加1,指向下一個字符

004013E2|.3C2D

CMPAL,2D

;把取得的字符與16進制值為2D的字符(即“-”)比較,這里主要用于判斷輸入的是不是負數(shù)

004013E4|.7508

JNZSHORTCrackHea.004013EE

;不等則跳

004013E6|.BAFFFFFFFF

MOVEDX,-1

;如果輸入的是負數(shù),則把-1送到EDX,即16進制FFFFFFFF

004013EB|.8A06

MOVAL,BYTEPTRDS:[ESI]

;取“-”號后的第一個字符

004013ED|.46

INCESI

;指針加1,指向再下一個字符

004013EE|>EB0B

JMPSHORTCrackHea.004013FB

004013F0|>2C30

SUBAL,30

;每位字符減16進制的30,因為這里都是數(shù)字,如1的ASCII碼是“31H”,減30H后為1,即我們平時看到的數(shù)值

004013F2|.8D0C89

LEAECX,DWORDPTRDS:[ECX+ECX*4]

;把前面運算后保存在ECX中的結果乘5再送到ECX

004013F5|.8D0C48

LEAECX,DWORDPTRDS:[EAX+ECX*2]

;每位字符運算后的值與2倍上一位字符運算后值相加后送ECX

004013F8|.8A06

MOVAL,BYTEPTRDS:[ESI]

;取下一個字符

004013FA|.46

INCESI

;指針加1,指向再下一個字符

004013FB|>0AC0

ORAL,AL

004013FD|.^75F1

JNZSHORTCrackHea.004013F0

;上面一條和這一條指令主要是用來判斷是否已把用戶輸入的注冊碼計算完

004013FF|.8D040A

LEAEAX,DWORDPTRDS:[EDX+ECX]

;把EDX中的值與經(jīng)過上面運算后的ECX中值相加送到EAX

00401402|.33C2

XOREAX,EDX

;把EAX與EDX異或。如果我們輸入的是負數(shù),則此處功能就是把EAX中的值取反

00401404|.5E

POPESI

;ESI出棧??吹竭@條和下一條指令,我們要考慮一下這個ESI的值是哪里運算得出的呢?

00401405|.81F653757A79

XORESI,797A7553

;把ESI中的值與797A7553H異或

0040140B\.C3

RETN

這里留下了一個問題:那個ESI寄存器中的值是從哪運算出來的?先不管這里,我們接著按F8鍵往下走,來到0040140B地址處的那條RETN指令(這里可以通過在調試選項的“命令”標簽中勾選“使用RET代替RETN”來更改返回指令的顯示方式),再按一下F8,我們就走出00401328地址處的那個CALL了?,F(xiàn)在我們回到了這里:0040132D|.3BC6

CMPEAX,ESI

;比較

0040132F|.7542

JNZSHORTCrackHea.00401373

;不等則完蛋

光標停在了0040132D地址處的那條指令上。根據(jù)前面的分析,我們知道EAX中存放的是我們輸入的注冊碼經(jīng)過計算后的值。我們來看一下信息窗口:

ESI=E6B5F2F9

EAX=FF439EBE

左鍵選擇信息窗口中的ESI=E6B5F2F9,再按右鍵,在彈出菜單上選“修改寄存器”,我們會看到這樣一個窗口:

可能你的顯示跟我不一樣,因為這個crackme中已經(jīng)說了每個機器的序列號不一樣。關掉上面的窗口,再對信息窗口中的EAX=FF439EBE做同樣操作:

由上圖我們知道了原來前面分析的對我們輸入的注冊碼進行處理后的結果就是把字符格式轉為數(shù)字格式。我們原來輸入的是字串“12345666”,現(xiàn)在轉換為了數(shù)字12345666。這下就很清楚了,隨便在上面那個修改ESI圖中顯示的有符號或無符號編輯框中復制一個,粘貼到我們調試的程序中的編輯框中試一下:

呵呵,成功了。且慢高興,這個crackme是要求寫出注冊機的。我們先不要求寫注冊機,但注冊的算法我們要搞清楚。還記得我在前面說到的那個ESI寄存器值的問題嗎?現(xiàn)在看看我們上面的分析,其實對做注冊機來說是沒有多少幫助的。要搞清注冊算法,必須知道上面那個ESI寄存器值是如何產(chǎn)生的,這弄清楚后才能真正清楚這個crackme算法。今天就先說到這里,關于如何追出ESI寄存器的值我就留到下一篇OllyDBG入門系列(四)-內(nèi)存斷點中再講吧。OllyDBG入門系列(四)-內(nèi)存斷點

作者:CCDebuger

還記得上一篇《OllyDBG入門系列(三)-函數(shù)參考》中的內(nèi)容嗎?在那篇文章中我們分析后發(fā)現(xiàn)一個ESI寄存器值不知是從什么地方產(chǎn)生的,要弄清這個問題必須要找到生成這個ESI值的計算部分。今天我們的任務就是使用OllyDBG的內(nèi)存斷點功能找到這個地方,搞清楚這個值是如何算出來的。這次分析的目標程序還是上一篇的那個crackme,附件我就不再上傳了,用上篇中的附件就可以了。下面我們開始:

還記得我們上篇中所說的關鍵代碼的地方嗎?溫習一下:

00401323|.E84C010000

CALL<JMP.&USER32.GetWindowTextA>

;GetWindowTextA

00401328|.E8A5000000

CALLCrackHea.004013D2

;關鍵,要按F7鍵跟進去

0040132D|.3BC6

CMPEAX,ESI

;比較

0040132F|.7542

JNZSHORTCrackHea.00401373

;不等則完蛋

我們重新用OllyDBG載入目標程序,F(xiàn)9運行來到上面代碼所在的地方(你上次設的斷點應該沒刪吧?),我們向上看看能不能找到那個ESI寄存器中最近是在哪里賦的值。哈哈,原來就在附近啊:

我們現(xiàn)在知道ESI寄存器的值是從內(nèi)存地址40339C中送過來的,那內(nèi)存地址40339C中的數(shù)據(jù)是什么時候產(chǎn)生的呢?大家注意,我這里信息窗口中顯示的是DS:[0040339C]=9FCF87AA,你那可能是DS:[0040339C]=XXXXXXXX,這里的XXXXXXXX表示的是其它的值,就是說與我這里顯示的9FCF87AA不一樣。我們按上圖的操作在數(shù)據(jù)窗口中看一下:

從上圖我們可以看出內(nèi)存地址40339C處的值已經(jīng)有了,說明早就算過了。現(xiàn)在怎么辦呢?我們考慮一下,看情況程序是把這個值算出來以后寫在這個內(nèi)存地址,那我們要是能讓OllyDBG在程序開始往這個內(nèi)存地址寫東西的時候中斷下來,不就有可能知道目標程序是怎么算出這個值的嗎?說干就干,我們在OllyDBG的菜單上點調試->重新開始,或者按CTR+F2組合鍵(還可以點擊工具欄上的那個有兩個實心左箭頭的圖標)來重新載入程序。這時會跳出一個“進程仍處于激活狀態(tài)”的對話框(我們可以在在調試選項的安全標簽下把“終止活動進程時警告”這條前面的勾去掉,這樣下次就不會出現(xiàn)這個對話框了),問我們是否要終止進程。這里我們選“是”,程序被重新載入,我們停在下面這一句上:00401000>/$6A00

PUSH0

;pModule=NULL

現(xiàn)在我們就要來設內(nèi)存斷點了。在OllyDBG中一般我們用到的內(nèi)存斷點有內(nèi)存訪問和內(nèi)存寫入斷點。內(nèi)存訪問斷點就是指程序訪問內(nèi)存中我們指定的內(nèi)存地址時中斷,內(nèi)存寫入斷點就是指程序往我們指定的內(nèi)存地址中寫東西時中斷。更多關于斷點的知識大家可以參考論壇精華7->基礎知識->斷點技巧->斷點原理這篇Lenus兄弟寫的《如何對抗硬件斷點之一---調試寄存器》文章,也可以看這個帖:。根據(jù)當前我們調試的具體程序的情況,我們選用內(nèi)存寫入斷點。還記得前面我叫大家記住的那個40339C內(nèi)存地址嗎?現(xiàn)在我們要用上了。我們先在OllyDBG的數(shù)據(jù)窗口中左鍵點擊一下,再右擊,會彈出一個如下圖所示的菜單。我們選擇其中的轉到->表達式(也可以左鍵點擊數(shù)據(jù)窗口后按CTR+G組合鍵)。如下圖:

現(xiàn)在將會出現(xiàn)這樣一個對話框:

我們在上面那個編輯框中輸入我們想查看內(nèi)容的內(nèi)存地址40339C,然后點確定按鈕,數(shù)據(jù)窗口中顯示如下:

我們可以看到,40339C地址開始處的這段內(nèi)存里面還沒有內(nèi)容。我們現(xiàn)在在40339C地址處后面的HEX數(shù)據(jù)或ASCII欄中按住左鍵往后拖放,選擇一段。內(nèi)存斷點的特性就是不管你選幾個字節(jié),OllyDBG都會分配4096字節(jié)的內(nèi)存區(qū)。這里我就選從40339C地址處開始的四個字節(jié),主要是為了讓大家提前了解一下硬件斷點的設法,因為硬件斷點最多只能選4個字節(jié)。選中部分會顯示為灰色。選好以后松開鼠標左鍵,在我們選中的灰色部分上右擊:

經(jīng)過上面的操作,我們的內(nèi)存斷點就設好了(這里還有個要注意的地方:內(nèi)存斷點只在當前調試的進程中有效,就是說你如果重新載入程序的話內(nèi)存斷點就自動刪除了。且內(nèi)存斷點每一時刻只能有一個。就是說你不能像按F2鍵那樣同時設置多個斷點)。現(xiàn)在按F9鍵讓程序運行,呵,OllyDBG中斷了!

7C932F398808

MOVBYTEPTRDS:[EAX],CL

;這就是我們第一次斷下來的地方

7C932F3B40

INCEAX

7C932F3C4F

DECEDI

7C932F3D4E

DECESI

7C932F3E^75CB

JNZSHORTntdll.7C932F0B

7C932F408B4D10

MOVECX,DWORDPTRSS:[EBP+10]

上面就是我們中斷后反匯編窗口中的代碼。如果你是其它系統(tǒng),如Win98的話,可能會有所不同。沒關系,這里不是關鍵。我們看一下領空,原來是在ntdll.dll內(nèi)。系統(tǒng)領空,我們現(xiàn)在要考慮返回到程序領空。返回前我們看一下數(shù)據(jù)窗口:

現(xiàn)在我們轉到反匯編窗口,右擊鼠標,在彈出菜單上選擇斷點->刪除內(nèi)存斷點,這樣內(nèi)存斷點就被刪除了。

現(xiàn)在我們來按一下ALT+F9組合鍵,我們來到下面的代碼:

00401431|.8D359C334000

LEAESI,DWORDPTRDS:[40339C]

;ALT+F9返回后來到的位置

00401437|.0FB60DEC334000

MOVZXECX,BYTEPTRDS:[4033EC]

0040143E|.33FF

XOREDI,EDI

我們把反匯編窗口往上翻翻,呵,原來就在我們上一篇分析的代碼下面???

現(xiàn)在我們在0040140C地址處那條指令上按F2設置一個斷點,現(xiàn)在我們按

CTR+F2組合鍵重新載入程序,載入后按F9鍵運行,我們將會中斷在我們剛才在0040140C地址下的那個斷點處:0040140C/$60

PUSHAD

0040140D|.6A00

PUSH0

;/RootPathName=NULL

0040140F|.E8B4000000

CALL<JMP.&KERNEL32.GetDriveTypeA>

;\GetDriveTypeA

00401414|.A2EC334000

MOVBYTEPTRDS:[4033EC],AL

;磁盤類型參數(shù)送內(nèi)存地址4033EC

00401419|.6A00

PUSH0

;/pFileSystemNameSize=NULL

0040141B|.6A00

PUSH0

;|pFileSystemNameBuffer=NULL

0040141D|.6A00

PUSH0

;|pFileSystemFlags=NULL

0040141F|.6A00

PUSH0

;|pMaxFilenameLength=NULL

00401421|.6A00

PUSH0

;|pVolumeSerialNumber=NULL

00401423|.6A0B

PUSH0B

;|MaxVolumeNameSize=B(11.)

00401425|.689C334000

PUSHCrackHea.0040339C

;|VolumeNameBuffer=CrackHea.0040339C

0040142A|.6A00

PUSH0

;|RootPathName=NULL

0040142C|.E8A3000000

CALL<JMP.&KERNEL32.GetVolumeInformationA>

;\GetVolumeInformationA

00401431|.8D359C334000

LEAESI,DWORDPTRDS:[40339C]

;把crackme程序所在分區(qū)的卷標名稱送到ESI

00401437|.0FB60DEC334000

MOVZXECX,BYTEPTRDS:[4033EC]

;磁盤類型參數(shù)送ECX

0040143E|.33FF

XOREDI,EDI

;把EDI清零

00401440|>8BC1

MOVEAX,ECX

;磁盤類型參數(shù)送EAX

00401442|.8B1E

MOVEBX,DWORDPTRDS:[ESI]

;把卷標名作為數(shù)值送到EBX

00401444|.F7E3

MULEBX

;循環(huán)遞減取磁盤類型參數(shù)值與卷標名值相乘

00401446|.03F8

ADDEDI,EAX

;每次計算結果再加上上次計算結果保存在EDI中

00401448|.49

DECECX

;把磁盤類型參數(shù)作為循環(huán)次數(shù),依次遞減

00401449|.83F900

CMPECX,0

;判斷是否計算完

0040144C|.^75F2

JNZSHORTCrackHea.00401440

;沒完繼續(xù)

0040144E|.893D9C334000

MOVDWORDPTRDS:[40339C],EDI

;把計算后值送到內(nèi)存地址40339C,這就是我們后來在ESI中看到的值

00401454|.61

POPAD

00401455\.C3

RETN通過上面的分析,我們知道基本算法是這樣的:先用GetDriveTypeA函數(shù)獲取磁盤類型參數(shù),再用GetVolumeInformationA函數(shù)獲取這個crackme程序所在分區(qū)的卷標。如我把這個Crackme程序放在F:\OD教程\crackhead\目錄下,而我F盤設置的卷標是GAME,則這里獲取的就是GAME,ASCII碼為“47414D45”。但我們發(fā)現(xiàn)一個問題:假如原來我們在數(shù)據(jù)窗口中看到的地址40339C處的16進制代碼是“47414D45”,即“GAME”,但經(jīng)過地址00401442處的那條MOVEBX,DWORDPTRDS:[ESI]指令后,我們卻發(fā)現(xiàn)EBX中的值是“454D4147”,正好把我們上面那個“47414D45”反過來了。為什么會這樣呢?如果大家對x86系列CPU的存儲方式了解的話,這里就容易理解了。我們知道“GAME”有四個字節(jié),即ASCII碼為“47414D45”。我們看一下數(shù)據(jù)窗口中的情況:

0040339C

47414D45000000000000000000000000

GAME............

大家可以看出來內(nèi)存地址40339CH到40339FH分別按順序存放的是47414D45。

如下圖:

系統(tǒng)存儲的原則為“高高低低”,即低字節(jié)存放在地址較低的字節(jié)單元中,高字節(jié)存放在地址較高的字節(jié)單元中。比如一個字由兩個字節(jié)組成,像這樣:1234,這里的高字節(jié)就是12,低字節(jié)就是34。上面的那條指令MOVEBX,DWORDPTRDS:[ESI]等同于MOVEBX,DWORDPTRDS:[40339C]。注意這里是DWORD,即“雙字”,由4個連續(xù)的字節(jié)構成。而取地址為40339C的雙字單元中的內(nèi)容時,我們應該得到的是“454D4147”,即由高字節(jié)到低字節(jié)順序的值。因此經(jīng)過MOVEBX,DWORDPTRDS:[ESI]這條指令,就是把從地址40339C開始處的值送到EBX,所以我們得到了“454D4147”。好了,這里弄清楚了,我們再接著談這個程序的算法。前面我們已經(jīng)說了取磁盤類型參數(shù)做循環(huán)次數(shù),再取卷標值ASCII碼的逆序作為數(shù)值,有了這兩個值就開始計算了?,F(xiàn)在我們把磁盤類型值作為n,卷標值ASCII碼的逆序數(shù)值作為a,最后得出的結果作為b,有這樣的計算過程:

第一次:b=a*n

第二次:b=a*(n-1)+b

第三次:b=a*(n-2)+b

第n次:b=a*1+b

可得出公式為b=a*[n+(n-1)+(n-2)+…+1]=a*[n*(n+1)/2]

還記得上一篇我們的分析嗎?看這一句:

00401405|.81F653757A79

XORESI,797A7553

;把ESI中的值與797A7553H異或

這里算出來的b最后還要和797A7553H異或一下才是真正的注冊碼。只要你對編程有所了解,這個注冊機就很好寫了。如果用匯編來寫這個注冊機的話就更簡單了,很多內(nèi)容可以直接照抄。

到此已經(jīng)差不多了,最后還有幾個東西也說一下吧:

1、上面用到了兩個API函數(shù),一個是GetDriveTypeA,還有一個是GetVolumeInformationA,關于這兩個函數(shù)的具體用法我就不多說了,大家可以查一下MSDN。這里只要大家注意函數(shù)參數(shù)傳遞的次序,即調用約定。先看一下這里:

00401419|.6A00

PUSH0

;/pFileSystemNameSize=NULL

0040141B|.6A00

PUSH0

;|pFileSystemNameBuffer=NULL

0040141D|.6A00

PUSH0

;|pFileSystemFlags=NULL

0040141F|.6A00

PUSH0

;|pMaxFilenameLength=NULL

00401421|.6A00

PUSH0

;|pVolumeSerialNumber=NULL

00401423|.6A0B

PUSH0B

;|MaxVolumeNameSize=B(11.)

00401425|.689C334000

PUSHCrackHea.0040339C

;|VolumeNameBuffer=CrackHea.0040339C

0040142A|.6A00

PUSH0

;|RootPathName=NULL

0040142C|.E8A3000000

CALL<JMP.&KERNEL32.GetVolumeInformationA>

;\GetVolumeInformationA

把上面代碼后的OllyDBG自動添加的注釋與MSDN中的函數(shù)原型比較一下:

BOOLGetVolumeInformation(

LPCTSTRlpRootPathName,

//addressofrootdirectoryofthefilesystemLPTSTRlpVolumeNameBuffer,

//addressofnameofthevolume

DWORDnVolumeNameSize,

//lengthoflpVolumeNameBuffer

LPDWORDlpVolumeSerialNumber,

//addressofvolumeserialnumber

LPDWORDlpMaximumComponentLength,

//addressofsystem'smaximumfilenamelength

LPDWORDlpFileSystemFlags,

//addressoffilesystemflags

LPTSTRlpFileSystemNameBuffer,

//addressofnameoffilesystem

DWORDnFileSystemNameSize

//lengthoflpFileSystemNameBuffer

);

大家應該看出來點什么了吧?函數(shù)調用是先把最后一個參數(shù)壓棧,參數(shù)壓棧順序是從后往前。這就是一般比較常見的stdcall調用約定。

2、我在前面的00401414地址處的那條MOVBYTEPTRDS:[4033EC],AL指令后加的注釋是“磁盤類型參數(shù)送內(nèi)存地址4033EC”。為什么這樣寫?大家把前一句和這一句合起來看一下:

0040140F|.E8B4000000

CALL<JMP.&KERNEL32.GetDriveTypeA>

;\GetDriveTypeA

00401414|.A2EC334000

MOVBYTEPTRDS:[4033EC],AL

;磁盤類型參數(shù)送內(nèi)存地址4033EC

地址0040140F處的那條指令是調用GetDriveTypeA函數(shù),一般函數(shù)調用后的返回值都保存在EAX中,所以地址00401414處的那一句MOVBYTEPTRDS:[4033EC],AL就是傳遞返回值。查一下MSDN可以知道GetDriveTypeA函數(shù)的返回值有這幾個:

Value

Meaning

返回在EAX中的值

DRIVE_UNKNOWN

Thedrivetypecannotbedetermined.

0

DRIVE_NO_ROOT_DIR

Therootdirectorydoesnotexist.

1

DRIVE_REMOVABLE

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論