WIN PE檔案插入MessageBox
阿新 • • 發佈:2020-06-22
1.準備工作
1.1獲取MessageBox地址
方法①:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include<stdio.h>
#include<windows.h>
typedef void (*FuncPointer)( LPTSTR ); //函式指標
int main()
{
HINSTANCE LibHandle;
FuncPointerGetAddr;
//載入成功後返回庫模組的控制代碼
LibHandle=LoadLibrary( "user32" );
printf ( "user32LibHandle=0x%X\n" ,LibHandle);
//返回動態連結庫(DLL)中的輸出庫函式地址
GetAddr=(FuncPointer)GetProcAddress(LibHandle, "MessageBoxA" );
printf ( "MessageBoxA=0x%X\n" ,GetAddr);
return 0;
}
|
方法②: 用OllyDBG載入notepad,左下角command
框中輸入bp MessageBoxA
下斷點。檢視斷點即可得MessageBox地址為:0x77D507EA
1.2CALL和JMP的計算
1 2 3 4 5 6 7 8 9 10 11 |
#include<stdio.h> #include<windows.h>
void func()
{
MessageBox(0,0);
}
int main()
{
func();
return 0;
}
|
main
函式反彙編:
1 2 3 4 |
11:func();
0040D478E88D3BFFFFcall@ILT+5(func)(0040100a)
12: return 0;
0040D47D33C0xoreax,eax
|
得到CALL
的硬編碼是E8
,call func
繼續跟進去:
1 2 |
0040100AE901000000jmpfunc(00401010)
0040100FCC int 3
|
得到JMP
的硬編碼是E9
,func
函式反彙編:
1 2 3 4 5 6 7 |
6:MessageBox(0,0);
004010288BF4movesi,esp
0040102A6A00push0
0040102C6A00push0
0040102E6A00push0
004010306A00push0
00401032FF15ACA24200calldwordptr[[email protected](0042a2ac)]
|
觀察到:
1 2 3 |
地址機器指令彙編指令指令所佔位元組數
0040D478E88D3BFFFFcall@ILT+5(func)(0040100a)5
0040D47D
|
E8後邊的值並不是真正我們要呼叫的函式地址, 他們有以下關係:
1 2 3 4 5 |
E8這條指令的下一行地址E8當前指令的地址E8當前指令所佔大小
0x0040D47D=0x0040D478+0x5
真正要跳轉的地址E8後邊的值E8當前指令的下一行地址
0x0040100A=0xFFFF3B8D+0x0040D47D
|
總結出:
1 2 |
E8後邊的值=真正要跳轉的地址-(E8當前指令的地址+E8當前指令所佔大小)
=真正要跳轉的地址-E8當前指令的下一行地址
|
1.3構造shellcode
通過之前的學習,可以構造出:
1 2 3 4 5 |
//18Bytes
shellcode[]={0x6A,0x00,0x6A, /*MessageBox4個引數入棧*/
0xE8, /*呼叫MessageBox*/
0xE9,0x00 /*跳到原程式的入口(AddressOfEntryPoint)*/
};
|
2.程式碼區新增shellcode
2.1分析PE結構
圖1
圖2
從圖2中可以得到以下資訊:
1 2 3 4 5 6 |
IMAGE_OPTIONAL_HEADER(可選頭)中部分成員資訊:
DWORD AddressOfEntryPoint; //程式執行入口RVA(0x0000739D)
DWORD ImageBase; //程式的優先裝載地址(基址)(0x01000000)
//程式執行時,PE裝載器先建立程序,再將檔案載入記憶體,然後把EIP暫存器的值設定為:ImageBase+AddressOfEntryPoint;
DWORD SectionAlignment; //記憶體中節的對齊粒度(0x00001000)
DWORD FileAlignment; //檔案中節的對齊粒度(0x00000200
|
1 2 3 4 5 |
IMAGE_SECTION_HEADER(節表第一項即程式碼區部分成員資訊):
DWORD VirtualSize; //節區在記憶體中沒有對齊前的實際大小(0x00007748)
DWORD VirtualAddress; //節區在記憶體中起始位置(RVA)(0x00001000)
DWORD SizeOfRawData; //節區在檔案中對齊後的大小(0x00007800)
DWORD PointerToRawData; //節區在在檔案中的偏移(0x00000400)
|
2.2判斷空閒區域是否能放下shellcode
計算檔案中程式碼區空閒空間
SizeOfRawData(0x7800) - VirtualSize(0x007748) > 0x12
空閒空間大於shellcode長度,可以放得下。
2.3將構造好的ShellCode寫入空閒區:
PointerToRawData(0x0400)+SizeOfRawData(0x7800)=0x7C00,
在0x7B48與0x7C00之間寫入shellcode:
圖3
2.4計算E8後邊的值
在計算相關值由檔案對映到記憶體時,需要考慮記憶體對齊和檔案對齊。
真正要跳轉的地址:MessageBox地址:0x77D507EA
1 2 3 4 5 |
檔案中,E8下一行地址相對PointerToRawData偏移量:0x7B5D-0x400=0x775D
對映到記憶體中,E8下一行地址:ImageBase+VirtualAddress+0x775D=0x0100875D
E8後邊的值:MessageBox-0x0100875D=0x76D4808D
|
2.5計算E9後邊的值
要保證MessageBox關閉後,程式能夠正常執行,需要jmp到原來的OEP
1 2 3 4 5 6 7 |
原來OEP(真正要跳轉的地址):ImageBase+AddressOfEntryPoint=0x0100739D
檔案中,E9下一行地址相對PointerToRawData偏移量:0x7B62-0x400=7762
對映到記憶體中,E9下一行地址:ImageBase+VirtualAddress+0x7762=0x01008762
E9後邊的值:`0x0100739D-0x01008762=0xFFFFEC3B`
|
2.6修改OEP(AddressOfEntryPoint)
1 2 3 4 5 |
檔案中shellcode起始地址相對PointerToRawData偏移量:0x7B50-0x400=0x7750
對映到記憶體中,相對ImageBase偏移:VirtualAddress+0x7750=0x8750
將原來的OEP修改為,對映到記憶體後的shellcode起始地址(0x8750)。
|
另存檔案,雙擊,成功彈出MessageBox,如圖4: