32位匯編第三講,RadAsm,IDE的配置和使用,以及匯編代碼注入方式
一丶RadAsm的配置和使用
簡介:
2020.4.29 添加更正內容
更正內容:
如果只想看RadAsm配置以及使用,可以查看新版配置 https://www.cnblogs.com/iBinary/p/12802276.html 當前博客講解的是舊版的設置.下面內容介紹了一個注入.注入可以學習一下.使用RadAsm編寫
正文:
用了怎么長時間的命令行方式,我們發現了幾個問題
1.沒有代碼提醒功能
2.編寫代碼很慢,記不住各種聲明
那么現在有大神,已經幫我們做了一個IDE環境,就是RadAsm,首先簡單介紹一下界面
(對於這個IDE(最新版是3.0)我已經打包好了,有中文版本,和英文版本)
我們需要配置一下環境
1.配置編譯環境,配置lib文件庫,配置Debug調試器
打開后會彈出
1.1 配置調試器路徑
首先這里我們注意下面的幾個選項
1.編譯選項 Assemble : 這個是默認的即可,如果你是編譯32位程序,那么我們就用 /c /coff 即可,后面幾個默認
2.連接選項 link: 一般是默認的,他是默認生成windows的程序,如果我們有控制台的程序,那么我們可以手工去編譯,
也可以通過它的項目,新建項目的時候選擇指定的
3.擴展的調試器 External debugger: 這個則配置我們od所在的路徑即可,3.0版本已經可以支持選擇調試器了,以前的版本則是你指定文件夾,默認的調試器是Ollydbg.exe,所以我們名字還要改為這個才可以
4.library: 庫的路徑,有時候你編寫匯編程序,會使用lib庫,也會使用inc文件,那么可以把我們昨天的MASM32的庫路徑放到這里,也可以用高版本的,比如vc++6.0的,或者vs系列的都是可以的
注意:
上面配置的編譯選項,以及連接選項,我們都是安裝的MASM32的,也就是昨天提供的工具,我們把它的路徑設置為環境變量,這樣就可以用編譯的指令了,例如 /c /coff ....,如果你沒有安裝,或者沒有配置環境變量,那么計算機就會找不到編譯器,進而你的/c /coff就是錯誤的,所以我們還需要設置環境變量
1.2 RadAsm 3.0版本設置環境變量
上面我們已經設置了調試器.下面我們設置環境變量
點擊 Option -> Environment
彈出界面
可以看到有三個路徑:
path include lib
分別加上我們的MASM 目錄即可.
例如Path
你的Masm路徑\bin
include 路徑
lib 路徑同上
建議使用Radasm 3.0 雖然是英文版.都是單詞都很簡單. 以前的我用了下有Bug. 例如程序運行之后不關閉.
2.RadAsm的字體設置,以及中文亂碼問題解決
關於RadAsm的字體,以及中文亂碼,網上有很多解決方法,這里我只對當前最新版本的RadAsm3.0做一個講解
選擇代碼編輯
從上往下看
1.第一個紅框,選擇的是Masm的背景顏色,以及主題,這個可以自由設置不多做講解
2.第二個紅框,Tab Size 4,這里是編寫匯編代碼的時候Tab制表符,的距離,我們可以設置為2個
3.第三個紅框,分別有個Code(代碼按鈕),和Line Number(行號的按鈕)
這里則是設置代碼字體的大小,還有行號顯示的大小
首先中文亂碼問題,打開code(代碼編輯)
在這里設置代碼字體的時候,一定注意要把下面的語言換成 ""中文 GB2312 " 而不是默西歐語言
選擇了即可解決中文亂碼的問題
三丶建立RadAsm工程,以及編譯編譯連接,和調試
1.打開 Project(項目) - > new Project(新建項目)
2.選擇項目編譯的語言,(這里我使用masm),選擇項目生成的路徑
默認即可
編寫我們的第一段代碼,並且調試輸出
1,編譯連接一起執行
,
快捷鍵是Ctrl + F5 編譯運行
1.編譯快捷鍵 是 F5 會生成obj中間文件
3.鏈接快捷鍵是 Ctrl + Alt + F5 將obj文件生成exe
4. 編譯並連接 shift + alt + f5 直接將編譯連接在一起了.
4.調試快捷鍵是 Ctrl + D 它會默認打開我們的OD調試器,並且附加我們的程序,我們試一下
打開了我們的OD調試器,並且開始調試了
打開了我們的OD調試器,並且開始調試了
其余功能,自己嘗試,如果不會配置,也可以在下方評論,那么我看到則會幫你解決,然后如果有好心人看到也會幫你一把
二丶匯編程序的注入代碼,注入32位計算機,並且彈出個信息框
一丶遠程線程注入的講解
在講解匯編程序注入代碼的時候,我們需要先明白,遠程線程注入的原理,我會寫一個遠程線程開發的例子,這樣有助於我們理解匯編注入
我們總共需要幾步
/*1.查找窗口,獲取窗口句柄*/ /*2.根據窗口句柄,獲得進程的PID*/ /*3.根據進程的PID,獲得進程的句柄*/ /*4.根據進程的句柄,給進程申請額外內存空間*/ /*5.調用WriteProcessMemory,給進程寫入DLL的路徑*/ /*6.創建遠程線程,執行我們的代碼*/ /*7.調用退出代碼,釋放遠程線程的dll*/
每一步單獨講解
我們新建一個MFC 對話框程序,添加一個按鈕,這個按鈕專門響應注入的實現
第一步: 查找窗口,獲得窗口句柄(采用WindowsAPI FindWindow,傳入窗口名稱,然后找到則返回對應的窗口句柄)
HWND hWnd = FindWindow("","計算器"); if(NULL == hWnd) { return; //失敗則返回 }
第二步: 根據窗口句柄,查找進程PID (調用 GetWindowThreadProcessId API,傳入窗口句柄,然后通過第二個參數把進程的PID給我們的參數)
/*2.根據窗口句柄,獲得進程的PID*/ DWORD DwPid = 0; GetWindowThreadProcessId(hWnd,&DwPid); //這個函數會返回線程的ID,但是我們不關心,所以沒有加返回值
第三步: 根據進程PID,返回進程的句柄(OpenProcess,參數一,權限 參數二,句柄是否繼承,參數三,進程的pid)
/*3.根據進程的PID,獲得進程的句柄*/ HANDLE hProHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,DwPid); //參數一,選擇所有權限,參數二,不繼承給false,參數三,給我們上面獲得的pid的值 if(NULL == hProHandle) { return; }
第四步: 給遠程進程申請空間,並且返回空間的首地址(調用的API 是VirtualAllocEx)
/*4.根據進程的句柄,給進程申請額外內存空間*/ LPVOID lpAddr = VirtualAllocEx(hProHandle,NULL,0x1000,MEM_COMMIT,PAGE_EXECUTE_READWRITE); if(NULL == lpAddr) { return ; }
VirtualAllocEx說明:
第一個參數: 進程的句柄
第二個參數: 指定位置分配內存,給NULL為默認幫我們找塊地方申請內存(不過這個地址會返回,所以不關心)
第三個參數: 內存分配多大,我們給了4096個字節大小,也就是一個分頁(1000H)
第四個參數: 是否立即申請,還是保留這塊內存,只能給我們用,但是還沒申請,我們選擇立即申請
第五個參數: 權限,你申請的這塊內存是什么內存,只能讀,還是只能寫,還是只能執行,我們選擇全部,可讀可寫可執行
第五步:調用WriteProcessMemory將我們的Dll路徑,寫入到遠程進程中
/*5.調用WriteProcessMemory,給進程寫入DLL的路徑*/ char szBuf[MAX_PATH] = {NULL}; GetCurrentDirectory(sizeof(szBuf),szBuf); //這三行代碼主要是拼接我們的DLL,DLL是我們自己寫的 strcat(szBuf,"StaticDll.dll"); //DLL這里就不寫了,用我的吧,我會發上去的
BOOL bRet = WriteProcessMemory(hProHandle,lpAddr,szBuf,strlen(szBuf)+1,NULL); if(!bRet) { return; }
WriteProcessMemory講解:
第一個參數: 進程的句柄,可以用我們上面的OpenProcess返回的
第二個參數: 你要寫入的地址,地址使我們VirtualAllocEx申請之后返回的(就是你要往哪個地址寫內容)
第三個參數: 你寫入的內容是什么,寫入的內容使我們的Dll路徑,上面已經拼接好了
第四個參數: 你寫入的內容的大小是多大,這里我們用strlen求出來了
第五個參數: 實際寫入的個數,我們不關心,如果你想知道,則定義一個變量,然后 取地址傳入即可,因為是個指針
第六步: 遠程進程開辟線程,調用LoadLibrary,加載我們的dll,而你們知道,當dll被加載的時候,會有信息
所以我們在我們的dll里面寫入我們自己的代碼,比如這個dll被加載的時候,我們執行我們的代碼,
這里我的代碼就是找到計算器,然后給它加個菜單,並且響應消息
HANDLE hThreadHandle = CreateRemoteThread(hProHandle, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, lpAddr, 0, NULL);
核心代碼就在這里,我們必須要知道,我們現在加載我們的DLL,而我們的DLL被加載的時候,就要執行自己的代碼
所以這個時候就可以執行代碼了
CreateRemoteThread講解:
第一個參數: 進程的句柄(你要往哪個進程開辟線程)
第二個參數: 安全屬性,句柄可否繼承,不需要給NULL
第三個參數: 棧的大小,給0則默認
第四個參數: 函數執行,我們要開辟線程,開辟的線程叫做loadLibrary
第五個參數: 開辟線程傳入的參數,我們知道,線程只有一個參數,而現在正好load也是一個參數,所以加載的參數就是我們的
寫入遠程進程內存的dll路徑,而dll路徑一旦啟動,則會執行自己的代碼(核心,一定掌握)
第六個參數: 創建的標志,默認給0
第七個參數: 線程的ID,不需要知道,給NULL
我們嘗試一下是否可以成功注入計算器,並且加入菜單
已經成功注入了.對於完整的代碼,我會放到課堂資料中,但是這幾步,一定要親自手動弄明白
(備注: 我是使用VC++6.0編寫代碼,是MFC程序,當然你也可以用高版本,參考我這個,是一樣的
對於DLL,我也會發,你們可以自己去寫自己的DLL,比如一個空DLL會有DLL main,也就是dll的入口點
當第一次加載的時候會來信息什么的,所以可以在里面寫代碼.dll靠自己,這里只提供思路)
二丶匯編代碼的注入
注意,別看博客每天內容很少,其實你想真正掌握,沒有2-3個小時就不算掌握,當你真正明白這個知識點了,那才叫掌握,而不是看一遍就走的,自以為已經會了,千萬不要眼高手低
比如上面的遠程線程注入,原理是什么,雖然我代碼給你了,當你知道原理了,那么代碼自己就會寫了
原理: 原理就是找到進程,利用VirtualAllocEX給他開辟個空間,利用他的返回值,會返回這個空間的地址
然后再利用WriteProcessMemory給這塊空間寫入DLL的路徑,最后利用遠程線程(CreateRemoteThread)
把loadlibrary當做線程回調執行,傳入的參數就是遠程進程空間我們寫入的DLL路徑,
這樣相當於當另一個進程 調用了LoadLibray,並且加載我們的DLL,而利用DLL被加載會執行的機制,
然后在我們的DLL中寫入被加載時候的代碼即可.
(對於為什么我們確定loadlibary在遠程進程中,其實這個是系統的特性,重要你軟件一啟動,就必然要有dll的支持
而我們調用的loadlibary就在這些dll中)
廢話補多少,會的人請看今天重要的知識,匯編代碼注入
首先,前邊介紹了RadAsm的IDE環境,那么我們利用他建立一個Dlg匯編程序
1.首先,新建工程,創建一個窗口程序
(因為對於RadAsm3.0不太熟悉,所以這里不用3.0了,還是用2.2.2.0)
選擇win32 app
選擇 dialog app
生成的時候注意ID和窗口名
,對於ID,和IDname,我們需要自己去定義宏
然后在窗口過程函數相應我們的消息
注意,對於資源,我們必須單獨編譯,快捷鍵是 shift + F5
Ctrl + F5 編譯運行
現在已經可以正常執行了
我們OD看一下,
Ctrl + D
下斷點之后,消息來了,我們知道了WM_COMMAND消息是0x111,所以就直接調用MessageBox函數了
開始遠程線程匯編代碼的編寫(因時間問題,今天只會講解一個思路,具體的實現代碼回去自己去寫一下)
明天公布答案,以及坑
看匯編代碼,我們會執行上面的那幾個步驟
先把變量定義出來
LOCAL @hWnd:HWND ;查找窗口返回窗口句柄 LOCAL @hProcessHandle:HANDLE: ;進程句柄 LOCAL @dwPid:DWORD ;進程的PID LOCAL @lpBuf:PVOID ;遠程進程開辟空間的首地址 LOCAL @hThread:HANDLE ;線程的句柄 LOCAL @dwExitCode:DWORD ;退出代碼,獲取的是遠程load的返回值 ;這些變量都是定義在窗口過程函數中
第一步,判斷按鈕ID,並且查找窗口句柄
;1.解析低位,獲得按鈕消息,並且查找窗口句柄 invoke FindWindow,NULL, offset g_szWindowName mov @hWnd,eax ;把返回值給@hWnd .if eax == NULL ret .endif
第二步:獲得進程的PID
invoke GetWindowThreadProcessId, @hWnd, addr @dwPID
第三步: 獲得進程句柄
invoke OpenProcess,PROCESS_ALL_ACCESS, FALSE, @dwPID
mov @hProcess, eax
.if eax == NULL
ret
.endif
第四步:申請內存空間
invoke VirtualAllocEx,@hProcess, NULL, 1000h, MEM_COMMIT, PAGE_EXECUTE_READWRITE mov @lpBuff, eax .if eax == NULL invoke ShowLastError ret .endif
第五步: 直接寫入代碼,寫入(按照以前,是往內存寫入DLL路徑,這里我們把代碼寫進去)
invoke WriteProcessMemory,@hProcess,
@lpBuff,
INJECT_CODE, ;代碼的標號
start - INJECT_CODE, ;起始位置-標號等於實際大小
NULL
標號是寫在代碼區的上面的
一會OD調試一下看看
至於為什么寫入的代碼要ret 4,也就是start上面的那句話,是因為
下面我們調用CreateRemoteThread的時候,其中一個參數是@lpbuf,我們是當做函數調用的,所以壓入一個返回地址
第六步: 創建遠程線程
invoke CreateRemoteThread,@hProcess, NULL, 0, @lpBuf, NULL, 0, NULL mov @hThread, eax .if eax == NULL ret .endif invoke WaitForSingleObject,@hThread, INFINITE 等待結束 invoke GetExitCodeThread, @hThread, addr @dwExitCode 獲取loadlibrary的返回值
他的返回值其實是dll在內存中的實例句柄,所以我們需要釋放掉.(釋放自己可以搜一下,暫時先把主要的講了)
OD分析 明天講解,今天先熟悉一下代碼,內容比較多,消化一下
課堂資料:
鏈接:http://pan.baidu.com/s/1o7B9f2i 密碼:5kcu