天天看點

動态分析工具OllyDbg學習筆記(一)-入門

沒有耐性學不好逆向!!!

OD常用快捷鍵:

f2:斷點
f3:打開檔案
f4:執行到光标
f5:窗體大小話
f6:窗體切換
f7:單步步入
f8:單步步過
f9:運作(到斷點停下)

組合快捷鍵:
ctrl+f2:重新開機調試程式
ctrl+f7:自動單步步入
ctrl+f8:自動單步步過(esc暫停)
ctrl+G:轉到位址
ctrl+N:找到調用的API位址
ctrl+f9:執行到傳回(一般是RETN 指令)

shift + f9 :忽略系統産生的異常, (也可以在設定的忽略異常中添加忽略範圍0 - 88888888 全程忽略)

alt+ f7 :轉到上一個參考
alt+ f8 :轉到下一個參考
alt+ f9: 傳回到程式的領空(使用者态)
空格:修改指令
分号(英文):寫注釋
{
	以前沒發現的調試技巧,當程式一閃而過的時候,很惱火是吧,特别是注冊時候,一旦輸入錯誤直接退出,沒時間下斷,如先彈框然後點選确定後直接退出,
	我們可以讓他先彈框,然後OD點選暫停,然後alt+f9傳回到使用者界面,單擊确定,這樣程式就(處理不了消息了,可以接收),然後就會回到暫停的位置,然後我們可以ctrl+f9 或者單步f8傳回,傳回到調用它的位置(一般是pop的位置)
}

(有一種取消息的死循環就是當程式調用GetMessageA() 或PeekMessage()時由于我們在調試,使程式得不到消息會一直在等系統給他的分派消息而死循環,這時隻需要點選一下程式就行了 )

PE結構簡介:

1.OD中可以檢視PE頭檔案,通過Memory map 看他分為幾個段

在其中的資料是從右向左讀的,為什麼呐?因為 windows是大尾的資料存法,高位址在右邊,
如
004013c0    47 45 32 12  -----> asi(12,32,45,47)   其中
比如
004000E8    00 10 00 00  -----> 00 00 10 00  ---> 1000(H)   ,記住,在每一個位元組内的顯示順序是對的,但是是16進制的(這個在od 也可以調)

低 <----------------------> 高

低
 |
高

的記憶體分布。是以嘛,棧是在高位址的,系統棧空間嘛,堆是在低位址的,系統管不着


004013c0    47  --- > 兩位16進制就是一個位元組(八位) 
004013c1    45
004013c2    32
004013c3    12

是以最高位是高位址,先讀
之是以是橫着排放,寬度隻要看上下兩個位址的差就行了,這個可以自己調在OD裡面
004013c4    78 43 34 55 

一般的exe檔案是在0x00400000 的基址開始載入記憶體的
一般代碼段是在0x004001000   開始的 ,前一千個位元組是PE頭

and 是彙編的與操作
add 是彙編的加操作
call 和 ret (ret n) 是成對存在的
sar: 算術右移,通常用在對帶符号的數字減半,最高位符号位(最右邊)不變,最低位右移到cf中(除2 , sar eax,5  就是除 32  在放入 eax)

VA:是PE檔案載入記憶體中的一片線性位址
RVA:就是在相對虛拟位址(relative Virtual Address ,相對于基址的偏移位址)
EP: entry point 程式入口點
OEP: orgin entry point 原始程式入口點(相對于加殼後)
 
記住程式的PE頭是在 00400000 開始的,而 dll 的位址一般是在73453535 以7開頭的位址也就是系統領空的
一般正确的PE結構就是在memory中一個段占1000(或者2000 ,3000 ,但是不會是5000這莫大) 位元組的如:
位址	        大小   區段
00400000		1000   PE頭    PE 檔案頭
00401000		1000   .text
00402000		1000   .rdata
00403000		1000   .data
00404000		1000   .rsrc


77D10000        1000   User32   PE 檔案頭
+1000           1000   .text
+1000           1000   .rdata
+1000           1000   .data
+1000           1000   .rsrc

77EF0000        1000   GDI32   PE 檔案頭
+1000           1000   .text
+1000           1000   .rdata
+1000           1000   .data
+1000           1000   .rsrc


7C800000        1000   Kernel32 PE 檔案頭

+1000           1000   .text
+1000           1000   .rdata
+1000           1000   .data
+1000           1000   .rsrc


其中位址和大小都是16進制的,幾個比較主要的PE檔案位址映射關系,之是以都是1000的整數倍,是為例記憶體對齊
一般對于過大的PE頭就是有問題的,并且做過修改後,用OD打開還會爆出缺少段的錯誤,沒關系

這是常用的PE頭資料
SizeOfCode = 400 (不會太大)    ---> 40000400
SizeOfInitializedData = A00    + 4000000 (基址的大小)
SizeOfUninitializedData = 0   + ---> 40000A00
AddressOfEntryPoint = 1000   
BaseOfCode = 1000            --->  40001000 
BaseOfData = 1000            --->  40002000 
ImageBase = 400000  --->虛拟基址(一般不變)
NumberOfRvaAndSizes = 10 (比較固定) 太大了會占用太多的記憶體
Export Table address  = 0 (不是dll ,沒有導出表) (可以随便改)
Export Table size = 0 (不是dll ,沒有導出表)   (可以随便改)

Import Table address  = 0 (有導入表) 
Import Table size = 0  

函數的入口點一般都是 55 H 就是push ebp 

記住修改頭檔案後,儲存的時候要注意,首先記住開始改的地方,然後轉到資料視窗,往下選擇一段(不要太長,否則有問題,太大放不下),選擇儲存到檔案,後面的資料說明PE檔案是可以動态的修改的,隻要 隻要把代碼段的基位址,和資料段的基位址向後平移,當然code size 也要增大,就會得到新的反調試的程式,OD載入會報錯,但是 windows 加載器就會智能的識别并正确運作

VC是禁止用寄存器傳遞參數的,一般delphi 是經常用寄存器傳遞參數的
遇到call 函數 不進入預覽,可以按Enter 實作,看看 call 的内容
 
SEH (Structured exception handling )結構化異常處理,windows 作業系統提供的功能,跟開發工具無關,windows程式設計最重要的理念就是消息傳遞。也就是事件驅動,當程式觸發一個消息時,系統将把該消息放在消息隊列,
然後去查找調用窗體對應的消息處理函數(callback),傳遞的參數就是這個消息(前提是那個對應的應用程式在系統注冊過消息處理函數的callback)(do not call me,i will call you ,等到有事的時候才發生函數執行)
實際上我們可以把異常也當做一種消息,當應用程式發生異常時就觸發該消息并通知 系統(打小報告),系統在找這個異常的處理回調函數,也就是常說的異常處理例程,如果我們沒有在程式中做異常處理,系統不會置之不理,會彈出“應用程式錯誤”的錯誤框,然後結束掉該程式,是以當我們用callback的思維來看待SEH就沒啥神秘的了


一般程式的修改都是修改一個功能,然後dump一下,然後測試通過再進行下一個修改 (就像版本更新一樣)  

查找字元串一般是先打開記憶體視窗,然後 ctrl+b 查找 asc 或 Unicode 都要試試(這裡搜尋要把光标拉倒最上面,的大小寫不要選中,因為系統有時候會故意把小寫變大寫,也可以兩次都進行嘗試,有的區分了大小寫就搜不出來,要在指令處輕按兩下進去,不是在 ASC碼的地方點進去),然後修改,注意修改的時候 要保證 位元組對齊,就是沒修改的也填充,這樣不會有問題,然後在資料視窗儲存(edit->save to exectutable )
再記憶體中檢視參考都是,就是找到那個字元串的時候,可以檢視參考(find reference ) 看到是哪裡引用他的,然後就在參考的反彙編裡看到指令了

去除Nag的幾種辦法(nag 就是提醒注冊的一個框)
1.直接把那裡jmp
2.直接把那裡填為nop
3.如果是有MessageBoxA() ,直接把他的第一個參數變成1(這個參數說明的是他的父句柄是哪個,1直接就不存在,是以函數直接執行失敗,父句柄無意義 )
4.改變他的入口位址 GetMoufduleHandleA()就是傳回程式的ImageBase(基址), 在Memory map 中有個 addressofEntryPoint 的位址,我們隻需要在資料視窗找到他的位址,然後将它往後修改一點,跳過nag就行了
5.還有一種比較保守的調試方法,就是在一個限制次數的程式中,在程式執行流程發生改變的前後測試,做标記,然後當程式流程改變的時候,我們看到是哪個jnz,jz,等與先前的注釋不同,就可以找到程式分支判斷點了,這是在程式執行流程改變的程式的一類程式中通用的對比思想

6.修改程式中的資料來判斷是否該修改