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