![Windows PE權威指南(第二部分 進階)_第1頁](http://file4.renrendoc.com/view/21d53d4d8814a74b8759c64831d83c2d/21d53d4d8814a74b8759c64831d83c2d1.gif)
![Windows PE權威指南(第二部分 進階)_第2頁](http://file4.renrendoc.com/view/21d53d4d8814a74b8759c64831d83c2d/21d53d4d8814a74b8759c64831d83c2d2.gif)
![Windows PE權威指南(第二部分 進階)_第3頁](http://file4.renrendoc.com/view/21d53d4d8814a74b8759c64831d83c2d/21d53d4d8814a74b8759c64831d83c2d3.gif)
![Windows PE權威指南(第二部分 進階)_第4頁](http://file4.renrendoc.com/view/21d53d4d8814a74b8759c64831d83c2d/21d53d4d8814a74b8759c64831d83c2d4.gif)
![Windows PE權威指南(第二部分 進階)_第5頁](http://file4.renrendoc.com/view/21d53d4d8814a74b8759c64831d83c2d/21d53d4d8814a74b8759c64831d83c2d5.gif)
版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
WindowsPE權威指南第二部分進階(剖析WindowsPE文件格式的原理及其編程技術)目錄\h第二部分PE進階\h第12章PE變形技術\h12.1變形技術的分類\h12.2變形技術可用的空間\h12.3PE文件變形原則\h12.4將PE變小的實例HelloWorldPE\h12.5打造目標PE的步驟\h12.6小結\h第13章PE補丁技術\h13.1動態(tài)補丁\h13.2靜態(tài)補丁\h13.3嵌入補丁程序\h13.4萬能補丁碼\h13.5小結\h第14章在PE空閑空間中插入程序\h14.1什么是PE空閑空間\h14.2添加注冊表啟動項的補丁程序?qū)嵗齖h14.3手工打造目標PE的步驟\h14.4開發(fā)補丁工具\h14.5小結\h第15章在PE間隙中插入程序\h15.1什么是PE間隙\h15.2插入HelloWorld的補丁程序?qū)嵗齖h15.3開發(fā)補丁工具\h15.4存在綁定導入數(shù)據(jù)的PE補丁程序?qū)嵗齖h15.5小結\h第16章在PE新增節(jié)中插入程序\h16.1新增PE節(jié)的方法\h16.2在本地建立子目錄的補丁程序?qū)嵗齖h16.3開發(fā)補丁工具\h16.4小結\h第17章在PE最后一節(jié)中插入程序\h17.1網(wǎng)絡文件下載器補丁程序?qū)嵗齖h17.2開發(fā)補丁工具\h17.3小結\h第三部分PE的應用案例\h第18章EXE捆綁器\h18.1基本思路\h18.2EXE執(zhí)行調(diào)度機制\h18.3字節(jié)碼轉(zhuǎn)換工具hex2db\h18.4執(zhí)行調(diào)度程序_host.exe\h18.5宿主程序host.exe\h18.6EXE捆綁器bind.exe\h18.7小結\h第19章軟件安裝自動化\h19.1基本思路\h19.2補丁程序patch.exe\h19.3消息發(fā)送器_Message.exe\h19.4消息發(fā)送器生成工廠MessageFactory.exe\h19.5軟件安裝自動化主程序AutoSetup.exe\h19.6小結\h第20章EXE加鎖器\h20.1基本思路\h20.2免資源文件的窗口程序nores.asm\h20.3免重定位的窗口程序login.asm\h20.4補丁程序patch.asm\h20.5附加補丁運行\(zhòng)h20.6小結\h第21章EXE加密\h21.1基本思路\h21.2加密算法\h21.3開發(fā)補丁工具\h21.4處理補丁程序\h21.5小結\h第22章PE病毒提示器\h22.1基本思路\h22.2手工打造PE病毒提示器\h22.3補丁版的PE病毒提示器\h22.4小結\h第23章破解PE病毒\h23.1病毒保護技術\h23.2PE病毒補丁程序解析\h23.3解毒代碼的編寫\h23.4小結第二部分PE進階第12章PE變形技術第13章PE補丁技術第14章在PE空閑空間中插入程序第15章在PE間隙中插入程序第16章在PE新增節(jié)中插入程序第17章在PE最后一節(jié)中插入程序第12章PE變形技術本章將研究PE文件的可塑性,通過對PE文件進行變形,看是否能通過操作系統(tǒng)的PE加載器。本章的目標是通過手工打造一些小的PE程序,以便探究PE文件結構與操作系統(tǒng)PE加載器之間的關系。研究PE變形技術不局限于了解PE加載器加載PE的機制,還在于通過變形可以實現(xiàn)反調(diào)試、運行劫持等。12.1變形技術的分類所謂變形是指通過改變鏈接器生成的PE文件內(nèi)容,擴大或縮小文件尺寸,用以測試PE加載器的機制及健壯性。本節(jié)主要講述靜態(tài)PE文件中的四種變形技術,它們依次是:結構重疊技術空間調(diào)整技術數(shù)據(jù)轉(zhuǎn)移技術數(shù)據(jù)壓縮技術下面分別介紹這四種變形技術。12.1.1結構重疊技術結構重疊技術是指在不影響正常性能的前提下,將某些數(shù)據(jù)結構進行重疊的技術。在縮小PE的變形中將大量使用這種技術?,F(xiàn)在舉例說明,以下是一個使用了結構重疊的PE文件頭部字節(jié)碼:該文件頭部就是典型的IMAGE_DOS_HEADER和IMAGE_NT_HEADERS兩個結構的重疊。首先分開來看,如果把這部分數(shù)據(jù)看成是IMAGE_DOS_HEADER,則各部分的值為:可以看到,指向PE文件頭部的字段依然是在偏移3Ch處。IMAGE_DOS_HEADER的40個字節(jié)一個不缺,所以,它是一個完整的DOSMZ頭結構。由于兩個結構并不是從一開始就重疊,所以在IMAGE_DOS_HEADER結構的0ch偏移處兩個結構開始重疊。從該位置處開始的IMAGE_NT_HEADERS結構各字段的值分別是:從上面的分析可以看出,兩個結構從以下字段開始發(fā)生重疊:IMAGE_NT_HEADERS.SignatureIMAGE_DOS_HEADER.e_maxalloc+IMAGE_DOS_HEADER.e_ss兩個結構中的字段發(fā)生了重疊,結構自然也就重疊了。那么為什么結構重疊了卻沒有發(fā)生加載錯誤呢?得益于以下三點:1)被覆蓋的數(shù)據(jù)可能是另一個結構中無用的數(shù)據(jù)。2)有用的數(shù)據(jù)可能只對一個結構起作用,但有時被覆蓋的數(shù)據(jù)在兩個結構中都有用。此種情況下發(fā)生的重疊必須保證重疊的字段在兩個結構中擁有相同值。3)PE加載器并不檢測所有的字段。重疊以后的兩個數(shù)據(jù)結構關系見圖12-1。圖12-1結構重疊示意圖從圖中可以看出,重疊以后的數(shù)據(jù)明顯變少了。12.1.2空間調(diào)整技術本小節(jié)以不固定大小的數(shù)據(jù)塊DOSSTUB為例介紹空間調(diào)整技術。具體思路是,通過調(diào)整字段IMAGE_DOS_HEADER.e_lfanew的值,實現(xiàn)動態(tài)地擴充或縮小DOSSTUB塊空間,從而達到PE變形的目的。這里以第6章的免導入、免重定位的HelloWorld1_1.exe作為藍本,目標是將該PE文件擴充一個內(nèi)存頁大小。以下是詳細的測試步驟:使用FlexHex建立一個大小為5120字節(jié)的HelloWorld1_10.exe程序,并執(zhí)行以下操作:步驟1修改IMAGE_DOS_HEADER.e_lfanew的值,增加一個頁面大小1000h,由原來的000000A8更改為000010A8。步驟2將HelloWorld1_1.exe的PE標識符起始位置開始的所有非零數(shù)據(jù)全部復制到000010A8位置(采用覆蓋方式)。步驟3修改字段IMAGE_OPTIONAL_HEADER32AddressOfEntryPoint的值,由原來的00001124更改為00002124。步驟4修改字段IMAGE_OPTIONAL_HEADER32SizeOfImage的值,由原來的00002000更改為00003000。步驟5修改字段IMAGE_OPTIONAL_HEADER32SizeOfHeaders的值,由原來的00000200更改為00001200。步驟6修改字段IMAGE_SECTION_HEADER.VirtualAddress的值,由原來的00001000更改為00002000。步驟7修改字段IMAGE_SECTION_HEADER.PointerToRawData的值,由原來的00000200更改為00001200。因為該文件沒有重定位信息、沒有導入表、沒有數(shù)據(jù)段、沒有數(shù)據(jù)目錄項,且只有一個節(jié),所以本測試中所有需要修改的參數(shù)都已列出。運行chapter12\HelloWorld1_10.exe,發(fā)現(xiàn)可以正常顯示對話框。在OD中查看內(nèi)存分配,可以看到文件頭部被擴充了一個頁面大小,如圖12-2所示。在操作系統(tǒng)查看兩個文件大小之差為4096,十六進制剛好是1000h,打造HelloWorld1_10的實驗證明調(diào)整DOS_STUB塊空間是可行的。圖12-2擴大后的HelloWorld1_10.exe文件頭部占用的內(nèi)存空間該實例演示了PE變形中的擴大技術??梢钥吹剑琀elloWorld.exe在被加載到虛擬內(nèi)存空間的PE文件頭占用空間的大小,由原來的00001000h變成了00002000h。12.1.3數(shù)據(jù)轉(zhuǎn)移技術在編程過程中,出于某種考慮,經(jīng)常會將PE中的一部分數(shù)據(jù)轉(zhuǎn)移到另一個位置。比如,將程序中的變量存儲到文件頭部結構的某個字段中,將代碼轉(zhuǎn)移到頭部結構的某個連續(xù)空間中等,這就是數(shù)據(jù)轉(zhuǎn)移技術。該技術包括對變量的存儲和代碼的存儲。1.變量存儲變量存儲的例子節(jié)選自隨書文件chapter12\HelloWorld7.exe的PE頭部,如下所示:000000004D5A48656C6C6F576F726C6450450000MZHelloWorldPE..該示例將程序要顯示的字符串變量移動到了文件頭部的IMAGE_DOS_HEADER中,而且與數(shù)據(jù)結構IMAGE_NT_HEADERS的PE標識字段自動重合,重合的部分為:50450000該部分既可以認為是IMAGE_NT_HEADERS.Signature,也可以認為是字符串"HelloWorldPE\0\0"的一部分。該部分變量原來的位置是在一個獨立的節(jié)".data"中,占據(jù)文件中的200h個字節(jié);通過這樣的轉(zhuǎn)移,使得PE產(chǎn)生變形,不僅節(jié)的內(nèi)容沒有了,節(jié)表中也少了一個描述該節(jié)信息的表項。2.代碼存儲對代碼的轉(zhuǎn)儲比較普遍,常見的有:PE壓縮、病毒、加密與解密等。文件頭部的連續(xù)空間被認為是存儲代碼的好地方,如果連續(xù)空間的長度無法容納所有的代碼,則可以將代碼分解。例如,看OD對第1章中HelloWorld.exe的反匯編代碼:指令字節(jié)碼總長度為36字節(jié)。變形空間中能夠容納這些代碼的有兩處:一個是IMAGE_DOS_HEADER,另一個是數(shù)據(jù)目錄表。假設以上兩處沒有空間能存放這些代碼,我們也可以將這些代碼分開來存儲,但分開存儲時必須要保證調(diào)用指令之間的先后順序。下面是對HelloWorld.exe反匯編代碼的連續(xù)指令長度的一個統(tǒng)計:長度為6字節(jié)的指令有3個長度為5字節(jié)的指令有2個長度為2字節(jié)的指令有4個長度為1字節(jié)的指令有1個有了以上的統(tǒng)計數(shù)據(jù),就可以對比變形空間中描述的可用連續(xù)字段,將這些指令分別存儲在不同的空間位置。以前三條指令為例:用字段擴展PE頭中的BaseOfCode開始的8字節(jié)存儲6A006A00指令,另加一條近跳轉(zhuǎn)指令EB00。用擴展PE頭中的MajorOperatingSystemVersion字段開始的8個字節(jié)存儲6800304000指令,另外加一條近跳轉(zhuǎn)指令EB00。然后根據(jù)兩部分的距離修正跳轉(zhuǎn)指令中的操作數(shù)如下:如上所示,從字段BaseOfCode到字段MajorOperatingSystemVersion,中間隔了三個雙字的字段,所以第一條近跳轉(zhuǎn)指令中的操作數(shù)為4*3+2=0Eh;另一個操作數(shù)則要根據(jù)下一條指令所在字段的位置進行計算。這樣,原來的指令:PUSH0PUSH0PUSHHelloWo.00403000就變成了現(xiàn)在的指令:PUSH0PUSH0JmpLoc1:……Loc1:PUSHHelloWo.00403000以上方法在構造指令時非常復雜,其實還有一種更好的方法,即通過程序編碼,使鏈接器輔助我們構造指令長度。下面為大家演示,步驟如下。步驟1未修正前的指令字節(jié)碼。以下內(nèi)容節(jié)選自HelloWorld.exe的代碼段:以上所列為沒有修正前的原始指令字節(jié)碼。步驟2修正以后的程序。通過程序?qū)⒃贾噶钭止?jié)碼分解為多個小塊代碼,詳情見代碼清單12-1。代碼清單12-1分解原始指令代碼(chapter12\exp.asm)1;2;手工修改用的HelloWorld源代碼3;戚利4;2011.2.185;6.3867.modelflat,stdcall8optioncasemap:none910includewindows.inc11includeuser32.inc12includelibuser32.lib13includekernel32.inc14includelibkernel32.lib1516;數(shù)據(jù)段17.data18szTextdb'HelloWorldPE',019;代碼段20.code21start:22pushMB_OK23pushNULL24pushoffsetszText25jmpshort@next126db8dup(0aah);在代碼中加入了8個字節(jié)27@next1:28pushNULL29callMessageBoxA3031pushNULL32callExitProcess33endstart行26使用偽指令語句db定義了第一塊代碼(行22~25)到第二塊代碼(行28~32)之間的間隔(以字節(jié)計)。步驟3修正后的代碼。下面是加入了補足數(shù)據(jù)的字節(jié)碼:與該字節(jié)碼對應的匯編代碼如下:代碼清單12-1的行25使用了跳轉(zhuǎn)語句(翻譯為指令字節(jié)碼是EB)。在程序源代碼中,開發(fā)者只需要簡單地使用標號來表明跳轉(zhuǎn)指令要跳轉(zhuǎn)到的位置,以及跳轉(zhuǎn)指令后的操作數(shù),即可由編譯程序自動生成。從以上反匯編代碼中可以看到,EB指令后的操作數(shù)為08,這8個字節(jié)是代碼清單12-1的行26定義的8個0AAh。通過這種簡單的方法就可以讓編譯器幫助我們計算跳轉(zhuǎn)指令的操作數(shù)了。12.1.4數(shù)據(jù)壓縮技術在編程的過程中,如果指令代碼比較長,還可以先對代碼實施壓縮,然后在PE頭部找一塊比較大的連續(xù)區(qū)域存放解壓縮用的代碼。程序被PE加載器加載后,文件頭就基本不再使用了。這時,可以將存儲的壓縮代碼通過PE頭部的解壓縮程序進行解壓,解壓后即可通過跳轉(zhuǎn)指令實施程序指令的轉(zhuǎn)移。由于壓縮以后的代碼不便于通過十六進制直觀地看到,所以這種方法在一些病毒程序代碼中比較常見;另外,在一些加殼程序中會經(jīng)??吹綌?shù)據(jù)壓縮技術。先來看一個這種技術的應用,以下是某病毒代碼的頭部信息:該病毒對兩個標識字段并沒有進行大的改動。注意觀察節(jié)表部分內(nèi)容,按照基礎知識中所介紹的,每個節(jié)表最少應該有40個字節(jié),在這里明顯大小并不符合。經(jīng)過仔細分析之后才知道,病毒程序?qū)@部分數(shù)據(jù)進行了加密處理。下面詳細分析病毒是如何加密該部分數(shù)據(jù)的。>>504500024C01根據(jù)前面所學的知識,PE頭部應該有兩個“\0”,在這里只是用了00-02來表示這兩個“\0”,看起來好像使用了簡單的行程壓縮算法。凡是有連續(xù)“\0”的地方都將0的個數(shù)作為緊跟在“\0”后面的一項。來看節(jié)SCODE的內(nèi)容,如下所示:根據(jù)以上的猜測來還原該節(jié)的實際內(nèi)容如下:根據(jù)恢復以后的節(jié)來看,對該算法的猜測應該是沒有問題的。再仔細分析一下,可以看到,該病毒的作者只對文件頭部進行了加密,其他部分還是沒有更改的。也就是說,要想恢復這個PE文件的內(nèi)容,只需要對頭部進行處理即可。知道原理之后,接下來的解密工作就容易多了,代碼清單12-2是解密的源代碼。代碼清單12-2解壓病毒文件頭部數(shù)據(jù)(chapter12\UnEncrpt.asm)1;2;forxxVirusunzipFileHeader3;戚利4;2011.2.195;6.3867.modelflat,stdcall8optioncasemap:none910includewindows.inc11includeuser32.inc12includelibuser32.lib13includekernel32.inc14includelibkernel32.lib15includecomdlg32.inc16includelibcomdlg32.lib171819TOTAL_SIZEequ162h2021;數(shù)據(jù)段22.data23szFileSourcedb'c:\worm2.exe',024szFileDestdb'c:\worm2_bak.exe',025dwTotalSizedd026hFileSrcdd027hFileDstdd028dwTempdd029dwTemp1dd030dwTemp2dd031szCaptiondb'Gotyou',032szTextdb'OK!?^_^',033szBufferdbTOTAL_SIZEdup(0)34szBuffer1db0ffffhdup(0)3536;代碼段37.code3839start:4041;打開文件worm2.exe42invokeCreateFile,addrszFileSource,GENERIC_READ,\43FILE_SHARE_READ,\440,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,045movhFileSrc,eax46;創(chuàng)建另外一個文件worm2_bak.exe47invokeCreateFile,addrszFileDest,GENERIC_WRITE,\48FILE_SHARE_READ,\490,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,050movhFileDst,eax5152;解壓縮頭部53invokeReadFile,hFileSrc,addrszBuffer,\54TOTAL_SIZE,addrdwTemp,05556movesi,offsetszBuffer57movedi,offsetszBuffer158movecx,TOTAL_SIZE59movdwTemp2,060@@0:61lodsb62movbl,al63subbl,064jz@@165stosb66incdwTemp267dececx68jecxz@F69jmp@@070@@1:71dececx72jecxz@F73lodsb74pushecx75xorecx,ecx76movcl,al77adddwTemp2,ecx78moval,079repstosb80popecx8182dececx83jecxz@F84jmp@@085@@:86invokeWriteFile,hFileDst,addrszBuffer1,\87dwTemp2,addrdwTemp1,NULL8889;關閉文件90invokeCloseHandle,hFileDst91invokeCloseHandle,hFileSrc9293invokeMessageBox,NULL,offsetszText,\94offsetszCaption,MB_OK95invokeExitProcess,NULL96endstart程序首先打開兩個文件,一個是待解壓的文件,用來讀;另一個是解壓后的文件,用來寫。解壓縮的代碼在行56~84。行61取出一個字節(jié),然后判斷是否為0,如果是則跳轉(zhuǎn)到標號@@1處執(zhí)行;否則將字節(jié)原樣寫入目標緩沖區(qū)szBuffer1。如果取到的是0,則再取一個字節(jié),該字節(jié)記錄了0的個數(shù)。將該值賦給cl寄存器,使用語句repstosb將指定個數(shù)的0存入目標緩沖區(qū);然后,調(diào)整循環(huán)次數(shù),并跳轉(zhuǎn)到標號@@0處繼續(xù)執(zhí)行下一個循環(huán)。最后,將目標緩沖區(qū)中已經(jīng)解壓的字節(jié)寫入目標文件(行86)。以上描述了四種基本的PE變形技術,下面探討PE變形時需要遵循的一些原則,以確保最終變形后的PE能被Windows加載器順利加載而不發(fā)生錯誤。12.2變形技術可用的空間要想對PE進行變形,需要掌握PE文件中每個位置的數(shù)據(jù)的可替換特性,即該位置數(shù)據(jù)是否可以被替換為別的值,某段數(shù)據(jù)是否可以被其他用途利用等??傮w上講,PE中可以用作變形的空間有以下四類。12.2.1文件頭部未用的字段通過基礎知識部分的學習我們知道,在PE文件頭部有許多字段的值可以被修改和利用。也就是說,出于兼容上的考慮,PE頭部的數(shù)據(jù)結構中為將來預留了很多的字段,這些字段現(xiàn)在有的被強制設置為0,有的則未加任何限制。這些可以被替換的數(shù)據(jù)見表12-1(以下結論是筆者測試得出的,并不保證能適應所有的場合)。由于文件頭中大部分字段不是連續(xù)的,受不同PE內(nèi)容的影響很大。比如,上表并沒有列出數(shù)據(jù)目錄表中加載配置和延遲導入表項的空間。如果一個PE中不存在以上特性,則這些空間中的[x].size域就是可用的。不連續(xù)的空間通常的用途是存放數(shù)據(jù),對于連續(xù)的但字節(jié)數(shù)不多的空間,則可以存放代碼。比如,以下常用的指令其字節(jié)碼本身就不大:相對較大的連續(xù)空間則可以存放一段較長的指令字節(jié)碼。這些空間主要包括:IMAGE_DOS_HEADER中的54個字節(jié)、標準頭12個字節(jié)、擴展頭14個字節(jié)、數(shù)據(jù)目錄52個字節(jié)、每個節(jié)表項中的20個字節(jié)。12.2.2大小不固定的數(shù)據(jù)塊通過對一些大小不固定的數(shù)據(jù)塊進行擴展,也可以獲取足夠的空間。這些大小不固定的數(shù)據(jù)塊包括:(1)DOSSTUB由于DOSSTUB是為16位系統(tǒng)保留的,其中的任何一個字節(jié)都可以填充為任意值。(2)PE擴展頭IMAGE_OPTIONAL_HEADER32在IMAGE_FILE_HEADER中有一個字段記錄了PE擴展頭的長度。該字段為:SizeOfOptionalHeader。注意,這個字段為DW類型,最多能擴展一個字的空間。(3)數(shù)據(jù)目錄項數(shù)據(jù)目錄表的項數(shù)由字段IMAGE_OPTIONAL_HEADER32.NumberOfRvaAndSizes來定義,通過修改該值也可以擴充或縮小文件頭的尺寸。(4)節(jié)表在節(jié)表數(shù)據(jù)結構中有一個值SizeOfRawData,表示節(jié)在文件對齊后的尺寸。修改這個值也可以起到擴充節(jié)大小的作用。注意若修改了一個節(jié)的大小,其他節(jié)在文件的起始地址都要跟著修改。理論上講,只要是大小不固定的塊,都有被擴展的可能。關鍵的問題是,PE加載器的機制是否允許修改,大家可以通過實驗自行測試(2)(3)(4)部分的可行性。后面會有一個專門的實驗來驗證(1)的可行性。如果可執(zhí)行文件不存在輸出表,那么當PE加載器將其加載到內(nèi)存以后,PE的文件頭部分數(shù)據(jù)就已經(jīng)是無用的了。這也就意味著,PE文件頭相關數(shù)據(jù)結構中的所有的字段,在運行期均是可隨意填充任何值的。唯一遺憾的是PE加載器在加載完PE以后,把該段內(nèi)存設置成了只讀的R屬性。12.2.3因?qū)R產(chǎn)生的補足空間操作系統(tǒng)對PE文件的強制對齊特性,使得PE文件的節(jié)中存有大量為對齊而補足的0。這種機制同樣影響到文件頭部。由于默認對齊尺寸為200h大小的限制,大部分的系統(tǒng)文件(如記事本、kernel32.dll等)的文件頭部只剩下很少的空間。12.3PE文件變形原則前面對PE變形時與字段有關的空間進行了簡單的分析,本節(jié)重點研究變形時要遵循的一些原則。在對PE文件進行變形時,改動PE數(shù)據(jù)結構中某些字段的值需要遵循一些原則,如果沒有原則地隨意變形,將會導致生成的目標PE文件無法被操作系統(tǒng)識別并加載。在對PE進行變形時需要特別注意這些原則。12.3.1關于數(shù)據(jù)目錄表數(shù)據(jù)目錄表的個數(shù)必須大于等于2。如果PE文件的最后一個字節(jié)位于目錄表之間,如介于第3項資源表定義之間,即[DD[2].VirtualAddress]<文件總長度<[DD[2].isize],則文件中無法定義資源表的大小,PE加載器默認資源表的大小為0。一個完整的數(shù)據(jù)目錄表在普通的PE文件中可讀寫的字段如下,以下截取了測試用的PE文件的數(shù)據(jù)目錄表。從測試看,連續(xù)AA的部分可以是任意值。12.3.2關于節(jié)表PE文件頭中可以沒有節(jié)的定義,但必須將文件頭部的字段IMAGE_FILE_HEADER.NumberOfSections設置為1。12.3.3關于導入表導入表是PE的核心。要想在已有的PE中靜態(tài)引入動態(tài)鏈接庫的函數(shù),必須通過變形技術構造一個合理的導入表(這里的“合理”指的是結構上的合理),或者重構已有導入表。在12.5.7小節(jié)將看到一個只有133字節(jié)的PE文件。在該文件中,PE頭部的數(shù)據(jù)結構中的字段能減的都減了,能重疊的也都重疊了,但即使是在這么短的PE中,導入表的雙橋結構還是存在的。導入表的IMAGE_IMPORT_DESCRIPTOR結構是順序排列的。前面我們講過,“指向的數(shù)組最后以一個內(nèi)容全0的結構作為結束”。其實,這個條件可以寬限到只判斷IMAGE_IMPORT_DESCRIPTOR.Name1是否為0即可。如果一個PE文件的結尾剛好沒有空間存儲該字段對應的值,則系統(tǒng)會默認該字段是存在的,并且其值為0。12.3.4關于程序數(shù)據(jù)數(shù)據(jù)可以存儲在內(nèi)存中的任何位置,可以位于文件頭,也可以位于其他節(jié)中。代碼和數(shù)據(jù)一樣,可以在內(nèi)存的任何位置,但所在的節(jié)(無論是指定的節(jié)還是文件頭部),其節(jié)的屬性必須可讀、可寫、可執(zhí)行。將代碼段設置為可寫屬性主要是考慮到某些程序會將一些變量存儲在代碼段,且在程序中有為該變量賦值的代碼。如果所有的數(shù)據(jù)(程序變量、導入表、IAT等)都在文件頭部,也就是說加載進內(nèi)存的PE文件只有文件頭存在,假設其大小為一個頁面1000h,那么操作系統(tǒng)會因為IAT的緣故自動將該頁面設置為ERW,即可讀、可寫、可執(zhí)行(這和以前我們看到的文件頭部只讀是不一樣的)。12.3.5關于對齊節(jié)的對齊尺寸必須大于或等于文件的對齊尺寸。由于文件的對齊尺寸被定義為2的N次冪,所以通常會將文件對齊尺寸設置得更小,并使兩個的值相等,以達到縮小PE文件的目的。如本章后面講的兩個例子中,文件對齊粒度用了10h,內(nèi)存對齊粒度用了4h,即:SectionAlignment=FileAlignment=10hSectionAlignment=FileAlignment=4h12.3.6幾個關注的字段每當修改了程序的尺寸后,程序中相關的字節(jié)碼的位置、字節(jié)碼的長度會發(fā)生或多或少的變化,這種變化勢必會影響一些記錄這些位置和大小的字段,表12-2所列字段是在變形時必須要關注、指定或修改的。表12-3所列是在變形時可當做固定標志的(即對大多數(shù)EXE文件來說經(jīng)常不變的)字段。表12-3中帶有中括號[]的表達式表示由該地址處取出的值作為定位對應字段的偏移。下面通過講解兩個實際例子的操作過程,學習將PE尺寸變小的方法。12.4將PE變小的實例HelloWorldPE本節(jié)要分析的源程序與第1章的HelloWorld.asm有一點區(qū)別,即將字符串定義為"HelloWorldPE"。為了能與最終手工打造修改后的PE程序有所區(qū)別,這里將源代碼及最終生成的PE的字節(jié)碼分別列出來。12.4.1源程序HelloWorld的字節(jié)碼(2560字節(jié))要手工打造的源代碼見代碼清單12-3。代碼清單12-3手工修改用的HelloWorld源代碼(chapter12\helloworld.asm)1;2;手工修改用的HelloWorld源代碼3;戚利4;2010.6.105;6.3867.modelflat,stdcall8optioncasemap:none910includewindows.inc11includeuser32.inc12includelibuser32.lib13includekernel32.inc14includelibkernel32.lib1516;數(shù)據(jù)段17.data18szTextdb'HelloWorldPE',019;代碼段20.code21start:22invokeMessageBox,NULL,offsetszText,NULL,MB_OK23invokeExitProcess,NULL24endstart源代碼比較簡單,程序?qū)崿F(xiàn)了彈出窗口的功能,彈出的窗口中顯示字符串"HelloWorldPE"。將HelloWorld.asm編譯鏈接生成最終的EXE文件,使用FlexHex打開HelloWorld.exe,復制字節(jié)碼并按照類別分為以下四部分:文件頭部、代碼段、導入表和數(shù)據(jù)段。各部分字節(jié)碼如下。(1)文件頭部=文件頭+節(jié)表+補齊(大小400h)(2)代碼段=代碼+補齊(大小200h)(3)導入表=導入表及相關結構+補齊(大小200h)(4)數(shù)據(jù)段=數(shù)據(jù)+補齊(大小200h)以上列出了完整的HelloWorld.exe的字節(jié)碼,主要是為了和最終打造生成的較小的PE文件進行比對。下面先跳過手工打造過程,看最終生成的目標PE文件。12.4.2目標PE文件的字節(jié)碼(432字節(jié))最終打造的目標PE見隨書文件chapter12\HelloWorld_7.exe,其所有的字節(jié)碼長度為432字節(jié),可以在WindowsXPSP3環(huán)境運行,在OD中調(diào)試時其內(nèi)存空間分配如圖12-3所示。圖12-3OD中HelloWorld_7.exe的內(nèi)存分配從圖中可以看出,HelloWorld_7文件中所有的數(shù)據(jù)被加載進內(nèi)存后,均被安排到了文件頭的位置,且文件頭部數(shù)據(jù)的訪問屬性被設置為RWE,即可讀、可寫、可執(zhí)行。以下是該PE文件的完整的字節(jié)碼:字節(jié)碼中存在連續(xù)AA字節(jié)的部分都是可以再次利用的空間。12.5節(jié)就從該文件頭部開始,詳細介紹打造目標PE的全過程。12.5打造目標PE的步驟12.4節(jié)為我們展示了打造前后PE文件的字節(jié)碼對比,通過對比可以發(fā)現(xiàn),打造后的目標PE文件盡管變得更小,卻依然具備打造前的PE的所有功能。本節(jié)將詳細介紹此次打造的全過程。希望讀者能夠全面理解和把握PE文件頭部數(shù)據(jù)結構中各字段的作用,同時,也讓讀者了解改變某些字段的值對整個PE文件所產(chǎn)生的影響。12.5.1對文件頭的處理根據(jù)前面介紹的結構覆蓋技術和數(shù)據(jù)轉(zhuǎn)移技術壓縮文件頭,主要操作包括:把NT頭提前,覆蓋DOS頭部分,只保留最重要的e_lfanew字段。因為數(shù)據(jù)段的起始地址BaseOfData是一個可以修改的字段,所以讓BaseOfData剛好落在e_lfanew這里,然后將這一部分更改為指向PE頭的0ch,如下所示:將IMAGE_NT_HEADERS提到前面來并不影響程序的運行。除了BaseOfData字段需要改成指向PE頭的指針外,其他都無需改動。從偏移02h開始一直到0Ch的數(shù)據(jù)沒有什么用處。于是把數(shù)據(jù)段中的數(shù)據(jù)放到了這里。不幸的是,原來要顯示的字符串"HelloWorldPE"長度好像超出了這個范圍;幸運的是,字符串里的"PE"剛好和PE文件的標志重疊了。如下所示:刪除節(jié).data的內(nèi)容,即從800h處開始的內(nèi)容全部刪除,然后將.rdata節(jié)表后的文件頭數(shù)據(jù)的所有內(nèi)容清零,將節(jié)數(shù)量從原來的3更改為2。12.5.2對代碼段的處理首先來看HelloWorld.exe代碼段字節(jié)碼反匯編的結果。1.程序代碼段反匯編代碼使用OD打開HelloWorld.exe,復制反匯編代碼段內(nèi)容如下:將以上代碼的字節(jié)碼整理出來,然后將這些字節(jié)碼移動到數(shù)據(jù)目錄表中。2.將代碼嵌入數(shù)據(jù)目錄表將代碼移動到PE文件頭部的數(shù)據(jù)目錄表中,見加黑部分。在覆蓋時需要注意不要將有用的部分覆蓋。以上顯示的字節(jié)碼中,短跳轉(zhuǎn)代碼指令E8中涉及的偏移部分已經(jīng)做了修改。由于獨立代碼部分長度剛好填完數(shù)據(jù)目錄表項03、04和05,免去了按照較短的空閑長度重新構造代碼的麻煩。下面來看對導入表部分數(shù)據(jù)的處理。12.5.3對導入表的處理按照第4章介紹的導入表重組的方法,將導入表更改為如下字節(jié)碼:可以看到,從0130h開始的16個字節(jié)為IAT的內(nèi)容,與之相關的由字段originalFirstThunk指向的數(shù)據(jù)結構則放到了導入表的最后一個全0的IMAGE_IMPORT_DESCRIPTOR結構中。因為前面說過,只要保證該結構的name1(框起來的部分)為0,即可滿足導入表結構數(shù)組以全0結束的條件。從0140h開始部分即為導入表結構數(shù)組。12.5.4對部分字段值的修正相關數(shù)據(jù)基本安排就緒,接下來的工作就是修正文件頭部因數(shù)據(jù)遷移而導致的字段的值的變更。主要包括以下幾個部分。1.定義節(jié).HelloPE由于.HelloPE段中存放了常量、數(shù)據(jù)和代碼,所以該段必須可讀、可寫、可執(zhí)行。下面是節(jié)表中對.HelloPE節(jié)表項中的各字段的賦值:標志位:0E00000E0h節(jié)的名字:自定義字符串為:.HelloPE節(jié)區(qū)的實際尺寸:01b0h節(jié)區(qū)起始RVA:從頭開始,即0000h文件對齊后的長度:01b0h節(jié)位于文件的偏移:從頭開始,即0000h注意節(jié)區(qū)的實際尺寸可以在0800h范圍內(nèi)隨意更改,不受任何影響,這里選擇文件長度01b0h。.HelloPE節(jié)表項結構的相關數(shù)據(jù)如下:2.基地址、執(zhí)行入口和代碼段大小裝入的基地址不變,依然是00400000h;而執(zhí)行入口則更改為009Ch,即文件偏移009Ch處。由于可執(zhí)行文件很?。ㄐ∮?00h),所以這里的文件偏移地址即為RVA,無需轉(zhuǎn)換。代碼段大小即整個文件的大小000001b0h,相關數(shù)據(jù)如下:3.對齊尺寸為了讓文件變得更小,文件的對齊尺寸和內(nèi)存的對齊尺寸均設置為00000010h,即16個字節(jié)。相關數(shù)據(jù)如下:4.文件頭大小與PE內(nèi)存映像大小所有頭+節(jié)表的大小為00000130h,而PE在內(nèi)存中的映像大小為00001000h,相關數(shù)據(jù)如下:5.數(shù)據(jù)目錄表中導入表字段導入表的起始RVA=00000140h,長度為3Ch。相關數(shù)據(jù)如下:12.5.5修改后的文件結構手動修改以后的PE文件結構如圖12-4所示。圖12-4手動修改后的PE結構如圖所示,源PE中數(shù)據(jù)段的數(shù)據(jù)存儲在目標PE的DOSMZ頭和PE標識之間,源PE的程序代碼存儲在目標PE的數(shù)據(jù)目錄表中;文件頭部定義了一個節(jié)表項,導入表和IAT表安排在目標PE的尾部。12.5.6修改后的文件分析接下來將使用工具PEInfo和PEComp分別對比兩個文件,得到的結果如下。1.PEInfo運行結果對比下面來看PEInfo對目標PE的輸出:與源PE相比,目標PE中的節(jié)少了,但導入表還是很完整的。模塊的基地址沒有發(fā)生變化,程序代碼由于搬遷到數(shù)據(jù)目錄表中,所以入口地址發(fā)生了變化。2.使用PEComp工具對比結果使用PEComp工具打開兩個PE文件,運行結果如圖12-5所示。圖12-5手工打造的PE程序與源程序?qū)Ρ葟膱D中可以看出,源PE與目標PE文件頭部不相同的地方很多。造成這種結果的最主要的原因是在手工打造時使用了數(shù)據(jù)轉(zhuǎn)移技術。12.5.7目標文件更小的實例分析下面看一個能顯示指定信息對話框的更小的PE文件miniPE程序,其大小總共為133字節(jié)。該文件的字節(jié)碼如下。1.字節(jié)碼2.源程序生成以上字節(jié)碼的源代碼見代碼清單12-4。為了去除微軟編譯器的提示錯誤,避免在鏈接時追加任何其他內(nèi)容,以及匯編指令調(diào)用時對invoke指令的分解,這次使用了Borland公司的Tasm和Tlink作為這個源文件的編譯器和鏈接器。具體方法可以參照源文件頭部的注釋。代碼清單12-4miniPE程序(chapter12\minipe.asm)從代碼的注釋可以看出,該PE使用的數(shù)據(jù)結構包括:IMAGE_DOS_HEADERIMAGE_FILE_HEADERIMAGE_OPTIONAL_HEADER32IMAGE_IMPORT_DESCRIPTOR[0]IMAGE_IMPORT_DESCRIPTOR[1].VirtualAddress其中數(shù)據(jù)目錄只用了兩個,且最后一個還沒有用全,因為節(jié)表在程序里沒有定義。對該代碼的詳細分析見圖12-6和圖12-7。圖12-6133字節(jié)的PE程序分析一圖12-7133字節(jié)的PE程序分析二如圖所示,為了便于分析,源程序中每行被按照功能劃分為6列,它們依次是:第1列標號,用于標識源程序中的一些特殊位置。第2列指令,即匯編源代碼。第3列結構字段名,定義此處的結構和字段。第4列字段的值,為每個字段賦值。第5列用分號做的注釋,標注該行的含義。第6列對應的字節(jié)碼。12.6小結本章介紹了PE的變形技術。所謂變形就是通過技術手段使PE文件的大小發(fā)生變化,或縮小或擴大;無論怎么變,都能保證PE文件能被WindowsPE加載器加載,且能正常運行。本章首先介紹了四種變形技術、PE數(shù)據(jù)結構和PE文件中可以被二次利用的空間,以及變形時需要遵循的原則;最后,通過對HelloWorldPE的變形過程進行分析,幫助讀者全面理解和把握PE數(shù)據(jù)結構中相關字段的作用。本章在全書中具有承前啟后的作用,既是對前面所學知識的一個簡單回顧和復習,又能為下一步利用這些技術實施靜態(tài)文件補丁和應用做好知識上的儲備。第13章PE補丁技術第12章介紹了PE變形技術,該技術研究的是程序的字節(jié)碼;本章來研究PE補丁技術,該技術側(cè)重于研究使用Masm32編寫的補丁程序,而非程序字節(jié)碼。PE補丁技術被廣泛應用于PE病毒、PE加密解密等領域,通過對目標程序嵌入不同的補丁程序,可以實現(xiàn)不同的目的。PE補丁分為動態(tài)補丁和靜態(tài)補丁,其中靜態(tài)補丁框架由兩部分組成:補丁程序和將補丁程序附加到目標PE的補丁工具。13.1動態(tài)補丁動態(tài)補丁是指目標PE處于活動狀態(tài)時(即進程)為其實施的補丁。PE文件被映像加載器裝載到內(nèi)存后,就變成了進程,由Windows子系統(tǒng)調(diào)度PE映像里預先存放的指令代碼完成指定的功能。前面講過,每個進程其存取空間為4GB,各進程的地址空間獨立,相互之間并不影響,動態(tài)補丁技術即要求我們打破這種傳統(tǒng)的認識,實現(xiàn)一個進程可以操作另外一個進程的地址空間。動態(tài)補丁常用于游戲修改器、動態(tài)調(diào)試、病毒生存等領域。一個完整的動態(tài)補丁一般需要具備以下四個要素:與其他進程通信的能力。良好的讀寫其他進程地址空間的能力。能正確識別要補丁的目標進程。在其他進程地址空間執(zhí)行代碼的能力。下面就針對以上四點展開討論。13.1.1進程間的通信機制在實施補丁過程中,兩個進程之間會相互交換數(shù)據(jù),如補丁程序必須動態(tài)獲取目標進程運行的狀態(tài),以確定在什么時候,什么地點實施補丁。這些信息的傳遞需要用到Windows系統(tǒng)中進程間的數(shù)據(jù)通信機制。在Windows中,實現(xiàn)進程間通信的機制有很多方法,歸納一下分為兩大類:一種是通過兩個進程實施的耦合性強的進程間通信。這種通信機制要求參與通信的兩個進程必須密切配合,兩個進程工作在服務器/客戶端模式。這類通信機制主要包括匿名管道、命名管道、郵件槽、遠程方法調(diào)用等。另外一種是由第三方參與的耦合性相對較弱的進程間通信,比如通過剪貼板、共享內(nèi)存、動態(tài)鏈接庫、映射文件、注冊表、一般文件、Socket、Windows消息隊列、信號量等。以下是常見的進程通信機制。1.管道技術管道(pipe)是一種具有兩個端點的通信通道,兩個端點分別連接兩個進程。管道可以是一個方向的,也可以是兩個方向的;連接管道的兩個端點既可以從管道中讀取數(shù)據(jù),也可以將數(shù)據(jù)寫進管道。匿名管道(AnonymousPipe)存在于父進程與子進程之間,由于它連接了兩個具有繼承關系的進程,所以該管道不需要名字,管道的創(chuàng)建由父進程完成。匿名管道是單機上實現(xiàn)子進程標準I/O重定向的有效方法,它無法在網(wǎng)絡上使用,也不能用于兩個不相關的進程。創(chuàng)建匿名管道的API函數(shù)是CreatePipe。命名管道(NamedPipe)是服務器進程和一個或多個客戶進程之間通信的單向或雙向管道。創(chuàng)建管道的服務器端在建立管道時會給管道指定一個名字,其他任何進程都可以通過這個名字打開管道的另一端,并根據(jù)給定的權限與創(chuàng)建管道的進程實施通信。創(chuàng)建命名管道的API函數(shù)是CreateNamedPipe。2.郵件槽單一的郵件槽(MailSlots)提供了兩個進程間的單向通信能力。由一個進程建立郵件槽從而成為郵件槽服務器,而其他進程,則通過郵件槽的名字向服務器發(fā)送消息。該消息一直處在郵件槽中直到服務器讀取它。這種機制與命名管道的機制類似,都是基于SOCKET技術通過端口實現(xiàn)的,但兩者傳遞數(shù)據(jù)的協(xié)議不同。如果要建立雙向的通信,則客戶端也可以建立相同的郵件槽,從而使得客戶端同時具備服務器和客戶端兩種角色。兩個這樣的進程連在一起就形成了一種雙向的可讀寫的通信通道。由于郵件槽使用了不可靠的數(shù)據(jù)報協(xié)議,所以其通常用于廣播消息。創(chuàng)建郵件槽的API函數(shù)是CreateMailslot。3.剪貼板剪貼板(ClippedBoard)是為應用程序之間進行數(shù)據(jù)共享而提供的一個第三方的數(shù)據(jù)存儲區(qū)。兩個需要傳遞數(shù)據(jù)的進程無需進行協(xié)商,由一方通過剪切(或復制)操作實施數(shù)據(jù)的轉(zhuǎn)移,另一方則可以在任何時刻(保證剪貼板中數(shù)據(jù)沒有被重新覆蓋)從剪貼板中取回數(shù)據(jù)。進程間存取數(shù)據(jù)唯一的限制是兩者必須使用同樣格式的數(shù)據(jù)。4.共享內(nèi)存共享內(nèi)存是文件映射機制的一個特例。內(nèi)存映射文件(MemoryMappedFiles)將磁盤不連續(xù)存儲的文件復制到連續(xù)內(nèi)存空間中,在前面有所介紹。Win32API允許多個進程訪問同一文件映射對象,各個進程在它自己的地址空間里接收指向內(nèi)存線性文件的指針。通過使用這些指針,不同進程就可以讀寫文件的內(nèi)容,從而實現(xiàn)對文件中數(shù)據(jù)的共享。Win32API中共享內(nèi)存(SharedMemory)實際是文件映射的一種特殊情況。進程在創(chuàng)建文件映射對象時用0xFFFFFFFF來代替正常的文件句柄,表示對應的文件映射對象是從操作系統(tǒng)頁面文件來訪問內(nèi)存,其他進程只要打開該文件映射對象就可以訪問該內(nèi)存塊,進而實現(xiàn)多進程共享同一段內(nèi)存數(shù)據(jù)的目的。建立內(nèi)存映射對象的API函數(shù)是CreateFileMapping,其他進程訪問內(nèi)存映射文件的API函數(shù)是OpenFileMapping。5.消息機制消息機制是Windows應用程序的核心,在Windows中發(fā)生的大部分事件都可以用消息來表示。消息可以告訴操作系統(tǒng)發(fā)生了什么,所有的Windows應用程序都是消息驅(qū)動的??梢哉f,消息機制是Windows系統(tǒng)間、進程間傳遞數(shù)據(jù)的最好的方法。窗口移動、鼠標點擊、鍵盤按鍵等事件的發(fā)生,以及程序的啟動或退出都會產(chǎn)生標準的Windows消息。這些消息告訴Windows操作系統(tǒng)(或接管了消息處理的程序)當前系統(tǒng)(或進程)的運行狀態(tài)。當然,Windows也提供了一些其他的非標準的消息,如異常消息,這些消息與系統(tǒng)的某些機制相關。動態(tài)補丁程序使用消息機制,配合內(nèi)存讀寫來實現(xiàn)進程間數(shù)據(jù)傳遞會相對容易些。一個進程在運行時會根據(jù)運行狀態(tài)產(chǎn)生各種消息(比如,因異常事件觸發(fā)的異常消息、程序中由中斷指令引發(fā)的調(diào)試消息等),這些消息會通過進程的外露端口(如異常調(diào)試消息經(jīng)由異常端口)傳輸出去。由于引發(fā)異常調(diào)試消息事件EXCEPTION_DEBUG_EVENT的指令最為精簡(即int3指令,一個字節(jié),十六進制字節(jié)碼為0CCh),所以這種進程間傳遞數(shù)據(jù)的方式被普遍用在動態(tài)補丁技術中。通過該消息傳遞數(shù)據(jù)的流程如圖13-1所示。圖13-1動態(tài)補丁中的信息傳遞如圖所示,補丁工具通過進程內(nèi)存讀寫技術將補丁代碼寫入目標進程指定位置(補丁代碼為int3,即0CCh);同時,補丁工具記錄該位置的字節(jié)值。當目標進程執(zhí)行到該位置時將產(chǎn)生異常,操作系統(tǒng)將該異常產(chǎn)生時的相關信息(各寄存器的值等)包裝到數(shù)據(jù)結構EXCEPTION_RECORD中,并查找此時調(diào)試目標進程的進程(即動態(tài)補丁工具),操作系統(tǒng)將該結構通過消息傳遞給動態(tài)補丁工具;然后,由動態(tài)補丁工具完成對目標進程信息的解讀,將解讀以后的相關信息再有選擇地重新寫回到目標進程地址空間,以改變目標進程的運行行為或當前的進程狀態(tài)。13.1.2讀寫進程內(nèi)存讀寫其他進程內(nèi)存地址空間是動態(tài)補丁必須具備的功能,WindowsAPI中提供了讀寫其他進程地址空間的函數(shù)。首先介紹這些相關的函數(shù)。1.相關函數(shù)Windows的安全機制不允許一個進程直接讀寫其他進程空間的數(shù)據(jù),除非使用了特定的WindowsAPI函數(shù)。這些函數(shù)包括:OpenProcess(通過設置訪問權限打開要讀寫的進程)ReadProcessMemory(實現(xiàn)打開進程空間數(shù)據(jù)的讀?。¦riteProcessMemory(完成向打開的進程空間寫入數(shù)據(jù))以下是這三個函數(shù)的詳細介紹。(1)OpenProcess函數(shù)OpenProcess函數(shù)用來打開一個已存在的進程對象,并返回進程的句柄。函數(shù)原型定義如下:HANDLEOpenProcess(DWORDdwDesiredAccess,//訪問權限BOOLbInheritHandle,//繼承標志,若句柄能由子進程繼承,則設置為TRUEDWORDdwProcessId//進程號);各參數(shù)解釋如下:1)dwDesiredAccess:訪問權限。它可以是表13-1所列的值。2)bInheritHandle:繼承標志;如果設置為TRUE,表示繼承打開的進程,否則表示不繼承。3)dwProcessId:進程的ID號。4)返回值:如成功,返回值為指定進程的句柄;如失敗,返回值為空??烧{(diào)用GetLastError獲得錯誤代碼。(2)ReadProcessMemory函數(shù)讀進程內(nèi)存函數(shù)。以下是函數(shù)原型:BOOLReadProcessMemory(HANDLEhProcess,//遠程進程句柄PVOIDpvAddressRemote,//遠程進程地址VA值PVOIDpvBufferLocal,//存放數(shù)據(jù)的緩沖區(qū)DWORDdwSize,//緩沖區(qū)大小PDWORDpdwNumBytesRead//讀出的實際字節(jié)數(shù),是輸出參數(shù));各參數(shù)解釋如下:1)hProcess:遠程進程的句柄,遠程進程即為要操作的進程。2)pvAddressRemote:要操作的進程的地址空間,該地址為VA。3)pvBufferLocal:存放要操作的數(shù)據(jù)的本地緩沖區(qū)。4)dwSize:本地緩沖區(qū)大小。5)pdwNumBytesRead:輸出參數(shù),表示本次讀取的實際字節(jié)數(shù)。6)返回值:如成功,返回TRUE,否則返回NULL。(3)WriteProcessMemory函數(shù)寫進程內(nèi)存函數(shù)。完整定義如下:BOOLWriteProcessMemory(HANDLEhProcess,//遠程進程句柄PVOIDpvAddressRemote,//遠程進程地址VA值PVOIDpvBufferLocal,//存放數(shù)據(jù)的緩沖區(qū)DWORDdwSize,//緩沖區(qū)大小PDWORDpdwNumBytesRead//讀寫的字節(jié)數(shù),是返回值);讀進程內(nèi)存和寫進程內(nèi)存的函數(shù)的參數(shù)定義是一樣的,各參數(shù)的解釋如下:1)hProcess:指定將要被讀寫的目標進程句柄。2)pvAddressRemote:目標進程中被讀寫的起始線性地址。3)pvBufferLocal:用來接收讀取數(shù)據(jù)的緩沖區(qū)(對于ReadProcessMemory函數(shù))或者要寫到目標進程的數(shù)據(jù)緩沖區(qū)(對于WriteProcessMemory函數(shù))。4)dwSize:要讀寫的字節(jié)數(shù)。5)pdwNumBytesRead:指向一個雙字變量,供函數(shù)返回實際讀寫的字節(jié)數(shù);如果不關心這個結果,可以將其設置為NULL。6)返回值:如果函數(shù)執(zhí)行成功,那么返回值是非0值,執(zhí)行失敗的話返回0。2.讀寫進程內(nèi)存實例分析如果大家經(jīng)常使用PEInfo小工具,就會發(fā)現(xiàn)該工具在查看一些有大量數(shù)據(jù)的PE時,經(jīng)常會出現(xiàn)界面“死住”的情況,關于原因和解決方法已經(jīng)在第2章里講過了,現(xiàn)在來看看通過動態(tài)補丁技術如何解決這一問題。(1)修改代碼為了簡化問題的描述,配合大家理解進程內(nèi)存讀寫,本實例采用了一些技巧,比如,在需要打補丁的PEInfo.asm中,做了如下修改:步驟1在數(shù)據(jù)段中添加一個標志dwFlag:szFileNamedbMAX_PATHdup(?),0FFhdwFlagdd0FFFFFFFFh;新增加的標志szDllEditdb'RichEd20.dll',0szClassEditdb'RichEdit20A',0步驟2在代碼中增加檢測標志位的代碼:.while[esi].VirtualAddresscld……invoke_appendInfo,addr@szBufferpopecx;重定位項數(shù)量xoredi,edi.repeatpushecxinvoke_appendInfo,addr@szBufferpopecx.break.ifdwFlag==1;加入一個看似永遠也不可能成立的條件;該標志會由其他進程修改!.untilcxz.break.ifdwFlag==1;加入一個看似永遠也不可能成立的條件;該標志會由其他進程修改!.ifediinvoke_appendInfo,addrszCrLf.endif.endw在兩層循環(huán)中均增加了對標志位的判斷,如果該標志位值為1,則退出循環(huán)。因為dwFlag在定義時被設置為值0xffffffff,并且chapter13\PEInfo.asm中再也沒有與dwFlag有關的賦值代碼,所以,該程序中這個退出條件似乎永遠也不會發(fā)生。(2)補丁工具源代碼動態(tài)補丁技術是通過修改進程內(nèi)存空間數(shù)據(jù),使被修改進程發(fā)生程序流向轉(zhuǎn)移的技術。起到這種作用的程序一般稱為補丁工具,代碼清單13-1是補丁工具的部分源代碼(完整代碼請參照隨書文件chapter13\DPatchPEInfo.asm)。代碼清單13-1讀寫進程內(nèi)存的函數(shù)_writeToPEInfo(chapter13\DPatchPEInfo.asm)1;2;讀寫內(nèi)存示例3;測試方法:首先運行PEInfo.exe4;顯示Kernel32.dll的信息5;啟動該程序,在kernel32.dll顯示重定位時單擊菜單第一項6;會發(fā)現(xiàn)PEInfo.exe的遍歷重定位信息被終止7;8_writeToPEInfoproc9pushad1011;通過標題獲得進程的handle12invokeGetDesktopWindow13invokeGetWindow,eax,GW_CHILD14invokeGetWindow,eax,GW_HWNDFIRST15movphwnd,eax16invokeGetParent,eax17.if!eax18movparent,119.endif2021moveax,phwnd22.whileeax23.ifparent24movparent,0;復位標志25;得到窗口標題文字26invokeGetWindowText,phwnd,addrstrTitle,\27sizeofstrTitle28nop29invokelstrcmp,addrstrTitle,addrszTitle30.if!eax31moveax,phwnd32.break33.endif34.endif3536;尋找這個窗口的下一個兄弟窗口37invokeGetWindow,phwnd,GW_HWNDNEXT38movphwnd,eax39invokeGetParent,eax40.if!eax41invokeIsWindowVisible,phwnd42.ifeax43movparent,144.endif45.endif46moveax,phwnd47.endw4849;moveax,phwnd50;invokewsprintf,addrszBuffer,addrszOut1,eax51;invokeMessageBox,NULL,addrszBuffer,NULL,MB_OK5253;根據(jù)窗口句柄獲取進程ID54invokeGetWindowThreadProcessId,phwnd,addrhProcessID5556;moveax,hProcessID57;invokewsprintf,addrszBuffer,addrszOut2,eax58;invokeMessageBox,NULL,addrszBuffer,NULL,MB_OK5960invokeOpenProcess,PROCESS_ALL_ACCESS,\61FALSE,hProcessID62.if!eax63invokeMessageBox,NULL,addrszErr1,NULL,MB_OK64jmp@ret65.endif66movhProcess,eax;找到的進程句柄在hProcess中676869;invokewsprintf,addrszBuffer,addrszOut3,eax70;invokeMessageBox,NULL,addrszBuffer,NULL,MB_OK717273;將進程掛起74;invoke_suspendProcess,hProcess7576;讀內(nèi)存77invokeReadProcessMemory,hProcess,STOP_FLAG_POSITION,\78addrdwFlag,4,NULL79.ifeax80;moveax,dwFlag81;invokewsprintf,addrszBuffer,addrszOut,eax82;invokeMessageBox,NULL,addrszBuffer,NULL,MB_OK8384;寫內(nèi)存,將標志位賦值85invokeWriteProcessMemory,hProcess,\86STOP_FLAG_POSITION,\87addrdwPatchDD,4,NULL88.else89invokeMessageBox,NULL,addrszErr2,NULL,MB_OK90jmp@ret91.endif9293;繼續(xù)進程的運行94;invoke_resumeProcess,hProcess95invokeCloseHandle,hProcess9697@ret:98popad99ret100_writeToPEInfoendpSTOP_FLAG_POSITION是PEInfo.asm中定義的標志dwFlag所在進程地址空間中的VA值。該位置可以通過以下方法計算出來:首先,找到dwFlag變量在文件中的位置(加黑部分):如上所示,標志字節(jié)dwFlag在文件地址0x00002510處。通過PEInfo小工具查看該文件的所有的節(jié)的相關信息,結果如下:根據(jù)RVA和FOA的換算關系可以得出,停止標志位的內(nèi)存地址(VA)為:0x00404115。在DPatchPEInfo.asm的開始部分聲明一個常量即可:STOP_FLAG_POSITION=00404115h(3)運行測試接下來,就是見證奇跡的時刻。整體思路是:當PEInfo小工具運行時,如果用戶發(fā)現(xiàn)要獲取的信息已經(jīng)輸出,則可以通過另外一個程序終止PEInfo代碼的運行(注意,不是終止進程的運行),看起來好像這個補丁程序也參與了PEInfo指令代碼的流程控制過程。現(xiàn)在來看該動態(tài)補丁的三要素:1)該補丁可以讀寫PEInfo.exe的進程內(nèi)存。2)該補丁的調(diào)用時機由用戶判斷。當發(fā)現(xiàn)需要的信息已經(jīng)輸出,即選擇菜單選項“文件”|“停止遍歷重定位表”,補丁會立刻生效,補丁的位置已經(jīng)事先計算出來了。3)盡管補丁沒有實現(xiàn)代碼部分,為了簡化任務,代碼事先已經(jīng)安排到原始程序中等待補丁對它的激活。以后我們看到的大部分的動態(tài)補丁則是將要追加的代碼通過補丁工具直接寫入補丁程序。擴展閱讀關于熱補通常情況下,補丁程序總是在被補丁的程序后期開發(fā)的,有時候,為了后期補丁方便,有些程序會事先給自己留一個“后門”。比如,微軟的大部分內(nèi)核函數(shù),檢查Windows的ntdll.dll中大部分函數(shù)的源代碼,其起始位置的代碼看起來總是這樣的:MOVEDI,EDIPUSHEBPMOVEBP,ESP"movedi,edi"指令為兩個字節(jié),可以存放短跳轉(zhuǎn)指令,短跳轉(zhuǎn)指令指向的位置可以存放一個指向長跳轉(zhuǎn)指令的地址,從而在運行期實現(xiàn)熱補(hotfix)。在VisualC++的編譯器選項中也有類似的參數(shù)/hotpatch可以創(chuàng)建可熱修補的PE映像。以上只是演示了一個思路,大家可以在此基礎上自行添加功能,比如提升補丁工具的權限、修改進程空間代碼、增強內(nèi)存讀寫能力等。13.1.3目標進程枚舉在進行動態(tài)補丁時,有一步是必需的,即獲取目標進程的句柄或者ID號。通過枚舉系統(tǒng)進程即可獲取這些信息。1.枚舉系統(tǒng)進程的方法枚舉Win32子系統(tǒng)進程的方法很多,常見的有以下四種。(1)調(diào)用PSAPI.DLL提供的函數(shù)該動態(tài)鏈接庫是微軟WindowsNT開發(fā)小組開發(fā)的與進程有關的函數(shù)集。核心函數(shù)包括:(2)調(diào)用ToolHelpAPI提供的函數(shù)ToolHelp32函數(shù)是一組存儲在Kernel32.dll中的WindowsAPI函數(shù),它能夠通過Snapshot獲得駐
溫馨提示
- 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. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025至2031年中國法式鉗工錘行業(yè)投資前景及策略咨詢研究報告
- 2025至2030年中國脂肪醇酰胺數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年中國羊皮罩數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年工程王輪胎項目投資價值分析報告
- 2025至2030年中國毛線帽數(shù)據(jù)監(jiān)測研究報告
- 2025至2030年中國智能控制吹灰器數(shù)據(jù)監(jiān)測研究報告
- 2025年中國隔聲器市場調(diào)查研究報告
- 2025年中國精密高速沖床市場調(diào)查研究報告
- 中草藥在國際市場的競爭力分析考核試卷
- 二零二五年度期貨交易投資顧問服務合同規(guī)范文本4篇
- 快速入門穿越機-讓你迅速懂穿越機
- 水利安全生產(chǎn)風險防控“六項機制”右江模式經(jīng)驗分享
- 2024年四川省成都市高新區(qū)中考數(shù)學二診試卷
- 幼兒園衛(wèi)生保健開學培訓
- 食材配送服務售后服務方案
- 礦井主要災害事故防治應急避災知識培訓課件
- 不老莓行業(yè)分析
- STARCCM基礎培訓教程
- 2016-2023年婁底職業(yè)技術學院高職單招(英語/數(shù)學/語文)筆試歷年參考題庫含答案解析
- 貴陽市2024年高三年級適應性考試(一)一模英語試卷(含答案)
- 地理標志專題通用課件
評論
0/150
提交評論