1. 程式人生 > >32位彙編第四講,乾貨分享,彙編注入的實現,以及快速定位呼叫API的數量(OD檢視)

32位彙編第四講,乾貨分享,彙編注入的實現,以及快速定位呼叫API的數量(OD檢視)

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

原文地址:https://www.cnblogs.com/iBinary/p/7524289.html

 

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

原文地址:https://www.cnblogs.com/iBinary/p/7524289.html