1. 程式人生 > >32位匯編第四講,幹貨分享,匯編註入的實現,以及快速定位調用API的數量(OD查看)

32位匯編第四講,幹貨分享,匯編註入的實現,以及快速定位調用API的數量(OD查看)

pre 賦值 應該 檢測 src 代碼位置 spa sid 在哪裏

32位匯編第四講,幹貨分享,匯編註入的實現,以及快速定位調用API的數量(OD查看)

昨天,大家可能都看了代碼了,不知道昨天有沒有在匯編代碼的基礎上,實現註入計算器.

如果沒有,今天則會講解,不過建議把昨天代碼熟悉一遍(課程是緊跟著來的,請不要拉下任何一天,因為今天的知識,

可能就和昨天的知識掛鉤,昨天的知識,和前天的掛鉤.....,當然你如你懂匯編,不是新手,那麽則可以直接往下看)

一丶遠程線程註入,和匯編遠程註入的區別

昨天的代碼,大家可能看了(沒看也沒有關系,就是遠程線程註入的代碼,開發角度,和匯編代碼註入,底層角度的兩份代碼)

這裏說下,他們的區別

首先我們知道,任何註入方式都有它們使用的特定場合

1.遠程線程註入

  這個只針對軟件的保護防範低使用的,病毒很少用這個,為什麽

我們知道,遠程線程註入,它會創建遠程線程,進而加載我們的DLL,而對有保護的程序來說,它可能不能防範你使用CreateRemoteThread函數,但是可以針對你的dll,比如遍歷dll個數

發現多了一個,程序就退出.等等.

2.匯編的遠程註入

  匯編的遠程註入,這個就有點狠了,為什麽,因為你寫的不是一個dll,而是一斷匯編代碼,寫到對面的內存中,讓他去執行,這樣除非對面軟件,試試的檢測內存狀態,否則不容易檢測出自己程序的異常

當然匯編的遠程註入,還是會開辟內存,但是我們知道,註入方法很多種,我們可以發揮想象,只重劍意不重劍招,我們可以這樣想,你不是要申請內存嗎,我們可以不申請內存,對面程序肯定會存再對齊的問題

,比如為了保證對齊,對面程序肯定會用NOP指令填充,那麽我們則可以利用這塊內存,這樣軟件除非也檢測NOP,和對齊,否則你註入進去對面也發現不了,再比如對面軟件很厲害,檢測很到位,厲害到

對齊也檢測了,那麽我們可以把對面棧的內存擡高,把我們的程序代碼寫進去,對面總不註意試試的檢測棧吧,執行完我們的代碼就出棧,對面不可能檢測棧的進出吧.所以重在想像(廢話有點多,可以省略不看直接看下邊 :) )

3.匯編遠程註入代碼分析(0D分析)

昨天我們因為時間關系,沒有具體分析昨天的代碼,今天我們用OD(OlleyDbg)一步一步分析,

這個分析也能讓我們快速的掌握調試技巧

①分析FindWindow,看下匯編代碼執行了什麽

首先貼出我們昨天的代碼

invoke FindWindow,NULL,offset g_szWindowName ,第二個參數是計算器的字符串
mov @hWnd, eax
.if eax == NULL
invoke ShowLastError
 ret
.endif
上面代碼的邏輯:
尋找計算器,返回計算器的窗口句柄,如果成功,(返回值默認放eax中)
如果成功,繼續往下執行,如果失敗,調用ShowLastError顯示錯誤信息

OD分析

技術分享

這個是我們大體的FindWindow界面

我們查找下窗口,看下是否找到,找到則窗口句柄放在eax當中

技術分享

我們可以看出,已經找到窗口了,並且窗口句柄已經在eax當中了,所以 eax == NULL 不成立,則跳轉到下一條指令位置執行,而下一條指令位置,則開始調用GetWindowThreadProcessID了

②GetWindowThreadProcessID,獲得進程的ID

首先還是對應著偽指令的匯編代碼查看

invoke GetWindowThreadProcessId, @hWnd, addr @dwPID

代碼很簡單,我們知道,調用函數傳參的時候,代碼都是從右往左壓棧的,所以第一個會 push dwPid,第二個會push hWnd

OD分析

技術分享

因為走一步截次圖太麻煩,而且影響大家觀看,索性直接標號,把每一步寫出來,這樣大家自己調試,不懂的時候來看我的每一步代表什麽意思

首先我把每一步執行的代碼都用標號圈起來了

1. lea eax,[local,3] 意思是我要拿第三個局部變量,也就是棧的第三個局部變量,而我們以前說過,局部變量都是 ebp -xxx來獲取,而現在是32位的匯編了,所以每個寄存器是4個字節,所以第三個局部變量則是-12 ,而對應局部變量

則是 -c

這句代碼真實的代碼則被翻譯成了 技術分享

lea eax,dword ptr ss:[ebp - oxc]位置,我們就可以去棧中看下ebp -c的位置是什麽了,註意這裏因為我走到下邊

所以已經獲取到了進程PID的值,所以是810,默認的時候是0,那麽是什麽意思那,就是取得 ebp -c 的地址

2.取得ebp的地址(假設地址是18ff44)那麽吧地址給eax,再把eax入棧,

3.把我們的第一個局部變量,也就是ebp - 4的值,(40D40,因為這裏是中括號,所以對棧取內容得出的,而上面的是沒有取內容,因為我們用的是lea指令)

4.調用GetWindowThreadProcessId,這個時候,因為我們把第二部的eax入棧(eax是ebx-c的棧地址),所以獲得的PID

值則會給對應棧地址的內容(什麽意思: 就是你提供局部變量的地址,也就是我們先前ebp -c的地址,操作系統獲得PID的值,則會根據你給的地址,把對應地址裏面的內容修改了,所以相當於是 mov dword ptr[18ff44],810)

至此,我們可以總結下,這個GetWindowThreadProcessId,執行的過程

1.先獲得局部變量的地址(ebp - c的地址,註意,不是值)

2.壓棧

3.獲得棧中第一個參數的值,(註意是棧地址裏面的值,而不是棧地址)也就是上次獲得的窗口的句柄

4.調用GetWindowThreadProcessId,把對應棧地址裏面的值修改為我們的PID值(810),所以我們已經得到PID的值了

匯編代碼:

invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, @dwPID
        mov @hProcess, eax
        .if eax == NULL
          invoke ShowLastError
          ret
        .endif
首先,把PID壓棧,然後把FALSE(匯編中是0)壓棧,然後把權限壓棧(權限就是常量)
最後打開進程,如果成功獲得進程句柄,則返回值放在eax中,把eax給局部變量
然後判斷局部變量是否==NULL,不想等繼續走,相等就是打開失敗,執行錯誤代碼提示(ShowlastError)

OD分析

技術分享

這個地方我也不用細講了

1.首先,我們把進程的PID,也就是局部變量第三個(ebp - c裏面的值)壓棧

2.其次從右往左壓入第二個參數,也就是FALSE

3.然後壓入權限

4.調用OpenProcess

5.成功則eax保存的是進程的實例句柄

6.判斷eax是否等於NULL,相等(獲取失敗)繼續往下執行,調用Call injectas.0040118b

④VirtualAllocEx,遠程申請內存

匯編代碼

invoke VirtualAllocEx,@hProcess, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE
        mov @lpBuff, eax
        .if eax == NULL
          invoke ShowLastError
          ret
        .endif

這個和上面一樣,都是從右向左入棧,如果成功,返回在遠程進程申請的內存的首地址,放在eax當中

失敗則下面判斷.

OD分析(註意,這種的上面都已經分析了很多遍了,API調用的傳參,出棧,以及寄存器給局部變量賦值)

所以下方的API我會提供圖片去看,但是不具體分析了,都是一樣的,如果又興趣的可以,自己練練手,手動分析,看下代碼流程怎麽執行.

技術分享

已經成功了,肯定會執行,現在介紹OD的第二種用法

當一個應用程序被打開的時候,我們可以選擇附加的方式,將這個程序掛起

現在我們把計算機附加,看下這個地方是否申請了內存

重新打開OD,現在是兩個OD

技術分享

,選擇我們的計算器程序

技術分享

搜索我們用Vir...申請成功的內存首地址,看看是否申請成功

技術分享

申請成功,然後我們繼續下一條指令執行,寫內存數據到這裏面

⑤,利用WriteProcessMemory寫內存數據到這裏面來

註意,我們寫的使我們的INJECT_CODE的代碼的二進制,所以程序在調用遠程線程的時候,

會把我們的而二進制當做代碼運行

看下匯編代碼:

        invoke WriteProcessMemory,@hProcess, 
                                   @lpBuff, 
                                   INJECT_CODE, 
                                   start - INJECT_CODE, 
                                   NULL
        

傳參,什麽的不說了,這裏需要註意一下 要寫入的內容是我們剛才申請的內存首地址

現在給的是lpBuff,也就是我們往哪裏寫,(往計算機器我們申請的哪塊內存寫,所以lpbuff就是計算器這塊內存的首地址了)

寫入的數據是 INJECT_CODE的代碼的二進制

寫入的大小是START - INJECT_CODE數據的大小

INJECT_CODE是在START標號的上面,我們看下

匯編代碼,和OD分析

技術分享

OD分析

技術分享

可以看到,我的標號

1.表示我們要寫入對面內存的起始地址(也就是我們用vir申請的)

2.我們要寫入的緩沖區,也就是我要寫入inject為開始,開始把這塊內存寫入

3.寫入的大小就是我們計算出來的START- INJECT_CODE

4.實際寫入的字節我們不關心,關於START - INJECT_CODE我們看下代碼開始出就明白了

技術分享

相當於00401017 - 00401000 = 17個字節,所以要寫入17個字節

看下計算機程序中,有沒有寫入我們的二進制代碼

技術分享

正好17個字節,而且代碼也寫進去了

最後我們調用CreateReomteThread開始把INJECT_CODE當做代碼去執行了,這裏傳參和上面一樣

不在分析了

至此,分析到這裏就完了,下面寫代碼就不分析了,開始真正的寫匯編代碼註入的程序了,因為匯編代碼和上面大同小異都是調用API,而後API傳參.保存返回值給局部變量,出棧等等都是一樣的,所以下方開始真正寫.如果感性區,想提升自己的調試能力,以及對OD的熟練程度,可以自己去分析一下

二丶匯編註入代碼的編寫,以及應該註意的各種問題

首先,如果做過昨天作業的同學應該知道,會遇到對面代碼和我方代碼的的位置不一樣

比如

我們INJECT_CODE的位置,和對面INJECT_CODE代碼的位置

還有就是DLL加在的位置不同,也會影響API的調用

比如我們代碼在INJECT_CODE裏面調用一個MessageBox,他可以彈窗

但是要註意,在對面的那邊調用這個就會出錯,為什麽

所以我們要註意幾個問題

1.Call的時候問題

2.地址重定位問題

首先是1問題

①Call的時候的問題

我們在匯編代碼中隨便看一個Call 然後按下空格鍵,看下匯編是什麽

我們分別在自己程序的INJECT_CODE 和對面程序的INJECT_CODE看下執行MessageBox會出現什麽問題

技術分享

這個是匯編代碼

看下OD

技術分享

我們可以看到都是調用0x401204,但是結果是正確的嗎

技術分享

我們用在反匯編窗口 CTRL + G 跳轉到00401204 我們發現

第一個程序,也就是我們的註入程序,它調用MessageBox,是有的

而計算器的程序調用的時候,是沒有的,找不到這塊內存,所以就出錯了

為什麽會出現這個問題,這個就是著名的重定位問題,以前我們DLL註入的時候,是系統幫我們重定位了

而現在我們要自己去重定位這個問題

首先我們知道,任何程序運行的時候,都會加在ntdll, 而kernel32.dll也會加載,user32.dll也會加載

而kernel32.dll並不是必須加載的,但是%99.999的程序都會加載這個dll ((*^▽^*))

user32.dll是和用戶相關的,也會有%99的加載

那麽就產生一個問題,看下圖

技術分享

我們註入程序調用MessageBox會從user32.dll中找到MessAgebox的地址,並且調用

而B程序,顯然DLL的首地址是2000的位置,首先不說我們能不能調用它

就我們剛才看Call的時候,他是直接call了一個常量 00401204,而顯然,這塊內存是不屬於B進程的所以出錯了

他是屬於A進程的,

所以我們要重定位API地址

怎麽定位

1.獲得當前註入程序的User32.dll的加載的實例句柄

2.並且創建進程快照遍歷計算機器進程模塊User32.dll的實例句柄

然後看下圖

技術分享

首先註入程序得出1000h ,遠程的程序得到的user32.dll的模塊地址是2000h

3.獲得MessageBox距離模塊的偏移,註意,這個偏移獲取出來,是兩方都一樣的,因為函數的位置都是一樣的,只有

模塊地址加載的位置不一樣

看圖

技術分享

算出來的都是100, 所以我們就有了一個公式

函數首地址 - 模塊首地址 = 得出了函數距離模塊的實際偏移

然後遠程模塊 + 函數距離模塊的實際偏移,得出遠程進程的Messagebox的實際偏移

假設我們本地進程是1000h Messagebox的距離是1100

那麽 1100 (函數首地址) - 模塊首地址(1000) = 實際偏移(100)

然後 遠程模塊地址(2000) + 實際偏移(100) = 實際函數地址

這個公式請熟練記住

看字不明白,看圖:

技術分享

先看公式,再看箭頭指向

那麽基於這個公式我們就開始寫我們的匯編代碼了

現在的函數地址重定義問題已經解決了,但是註意,只是函數地址的重定位

下面寫完匯編代碼,就明白,另一個函數調用地址無關性的重定位問題了,

也就是我們要解決的第二個問題,說的有點多,看代碼,其實代碼很簡單

LOCAL @hLocalUser32Module:MODULE        ;存放本地User32.dll的模塊地址
LOCAL @hRemoteUser32Module:MODULE      ;存放遠程user32.dll的模塊地址
;1.調用GetModule加載user32.dll獲得user32.dll的模塊地址
invoke GetModule,offset g_szUser32         ;g_szUser32看做字符串user32.dll,就是dll需要這個字符串,去尋找user32.dll,如果想看完整工程
請下載每天資料查看
mov @hLocalUser32Module,eax                ;返回值存放user32.dll模塊地址,給局部變量 @hRemoteUser32Module,eax; 按理說這裏應該遍歷被註入進程的模塊

;獲得user32.dll的地址,但是這裏我的都是同系統,所以dll位置是一樣的,如果你把這個註入程序給另外一個系統就要自己遍歷了,遍歷代碼就不寫了,和調用API ;一樣,如果不會寫,可以下方評論. mov ;2.獲得MessageBox函數的地址 invoke GetProcess,@hLocalUser32Module,offset g_szMsgbox sup eax,@hLocalUser32Module         ;函數地址 - 模塊地址 = 實際偏移 mov ebx,hRemoteUser32Module         ; 把另外進程的句柄給ebx add ebx,eax                   ; 遠程模塊地址 + 實際偏移 = 遠程函數實際偏移位置 這裏給ebx是為了中轉一下計算 lea eax,MSG_BOX             ;求出inject標號所在的位置 mov[eax],ebx                   ;寫入另外實際函數地址並且調用

對於最後兩個,求出標號所在的位置,和寫入實際函數地址並且調用

這個則是在我們的INJECT_CODE裏面,新申請了一個標號位置,

然後裏面的內存寫入的使我們的實際地址

技術分享

但是現在我們發現出現了新的問題

雖然我們已經寫給了MSG_BOX,但是還是不能正常運行

為什麽我們寫進去的代碼確實是MSG函數的地址

但是我們要知道,我們現在並不知道代碼執行的位置在哪裏

比如INJECT_CODE 中我們要CALL這個MSG_BOX的地址

你會發現,CALL的時候MSG_BOX還是一個全局常量,也就是說,你API地址的重定位問題已經解決了,現在的

代碼重定位還沒有解決

看下圖理解:

技術分享

現在我們也計算出來了API的地址了,但是地址還沒有計算出來,這個時候大家會問,我們不是遍歷了dll模塊的地址了嗎,把它拿過來用不行嗎,可以,但是問題不在這,你拿過來也是也內存,但是只要你在INJECT_CODE裏面call的時候

都不是call的它,而是你在本地進程call的,給遠程內存寫過去了,遠程也call,call的也是一個地址,而這個地址壓根不存在,那麽就會出錯.

比如:

技術分享

我們要計算的是代碼和我們的代碼call的位置的偏移

也就是 inject_code 和我們下方寫的代碼的偏移

技術分享

算這一段距離,但是這個你在遠程進程中也不好算,所以就有了新的方法

看匯編代碼

push ebp
    push ebx
    call $+5 ;CALL 下行指令
TEXT:
    pop  ebp  ;地址重定位
    sub  ebp, offset TEXT

首先保存棧環境,ebp,我們下方會用到ebx,也保存,

call $+5是什麽意思, 一起就是call指令占五個字節,在call的時候會把下一條指令入棧,也就是TEXT指令位置入棧

而下方緊接著pop ebp,這個比較重要了,主要為了什麽,我們主要為了拿到IP的位置

試想一下,CALL 一次會把下面的地址入棧,然後出棧就得到了當前IP執行代碼的地址了

對不對

緊接著我們又寫了

sub ebp,offset TEXT,這個是為了什麽,我們想一下,在我們本進程,offset TEXT會被翻譯為一個常量

當我們ebp減去TEXT位置,就得到了ebp和代碼位置處的偏移了,看圖

技術分享

想想一下,另外一個進程減掉我們的本地的偏移,得到一個偏移,是不是相當於另外一個進程也得到自己代碼的位置了,然後我方用ebp + 函數的偏移位置 得出函數地址 相當於對面的程序 減去我們的地址 加上 函數位置的偏移

也是一樣的調用函數地址

不明白看圖,這裏比較繞,但是很重要:

技術分享

2000 - 1000 = 1000 這個1000是地址重定位 也就是代碼的位置在這裏,而後加上函數的偏移= 實際執行代碼的位置

也就是遠程線程也是這樣的

已經深夜了,基於時間關系,大家先把概念弄清楚,明天白天重新編輯更新

課堂代碼連接: 鏈接:http://pan.baidu.com/s/1bprSUcf 密碼:rsag

32位匯編第四講,幹貨分享,匯編註入的實現,以及快速定位調用API的數量(OD查看)