如何查找特定指令的內存地址(用於利用寫入)?如何查找DLL中特定指令的內存地址
具體而言,我正在尋找在Windows XP上user32.dll
的call ebp
指令,沒有Service Pack,其地址我可以指向EIP
。我在目標上安裝了Immunity Debugger和OllyDBG。
如何查找特定指令的內存地址(用於利用寫入)?如何查找DLL中特定指令的內存地址
具體而言,我正在尋找在Windows XP上user32.dll
的call ebp
指令,沒有Service Pack,其地址我可以指向EIP
。我在目標上安裝了Immunity Debugger和OllyDBG。
要找到一條指令,您需要找出代碼,.text,節開始和結束的位置,然後加載DLL並執行班輪搜索,直到找到指令。
這裏我們有兩個call ebp
指令的測試DLL:
// test.c
// gcc -Wall -shared test.c -o test.dll
#include <stdio.h>
__declspec(dllexport) void test(void) {
asm("call *%ebp");
puts("test");
asm("call *%ebp");
}
編譯並在OllyDbg中加載DLL,然後單擊CTRL + ˚F和搜索CALL EBP
:
6BEC125A |. FFD5 CALL EBP
6BEC125C |. C70424 6430EC6> MOV DWORD PTR SS:[ESP],test.6BEC3064 ; |ASCII "test"
6BEC1263 |. E8 74060000 CALL <JMP.&msvcrt.puts> ; \puts
6BEC1268 |. FFD5 CALL EBP
你看到第一條指令的地址是0x6bec125a
第二條在0x6bec1268
。請記住這個,call ebp
的操作碼是0xff 0xd5
。
現在我們需要找到的代碼的邊界,你可以使用objdump的使用-h
:
> objdump --headers test.dll
test.dll: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000984 6bec1000 6bec1000 00000600 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE, DATA
1 .data 00000008 6bec2000 6bec2000 00001000 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .rdata 0000011c 6bec3000 6bec3000 00001200 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
....
>
的代碼開始在VMA,虛擬內存地址,0x6bec1000
其尺寸爲0x984
,所以它在結束0x6bec1000
+ 0x984
= 0x6bec1984
爲:
0x6bec1000
....
what is between are the DLL instructions
....
0x6bec1984
我希望是清楚爲止。
如果我們要編寫我們call ebp
掃描儀,我們需要做的流動:
.text
,找到它的相對地址和它的虛擬大小。0xff 0xd5
,call ebp
的操作碼,簡單的班輪搜索。下面是一個簡單的實現:
// findopcode.c
// gcc -Wall findopcode.c -o findopcode
#include <windows.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
const char opcode[] = {0xff, 0xd5}; // The opcode of `call ebp'
FILE *dllFile;
HMODULE dllHandle;
IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS NtHeaders;
IMAGE_SECTION_HEADER sectionHeader;
unsigned int i;
unsigned char *starAddr;
unsigned char *endAddr;
if(argc < 2) {
printf("usage: %s [DLL]\n", argv[0]);
return -1;
}
if((dllFile = fopen(argv[1], "rb")) == NULL) {
perror("[!] Error");
return -1;
}
// Read the basic PE headers
fread(&dosHeader, sizeof(dosHeader), 1, dllFile);
fseek(dllFile, dosHeader.e_lfanew, SEEK_SET);
fread(&NtHeaders, sizeof(NtHeaders), 1, dllFile);
// Search for the executable section, .text section.
for(i = 0 ; i < NtHeaders.FileHeader.NumberOfSections ; i++) {
fread(§ionHeader, sizeof(sectionHeader), 1, dllFile);
// If we found a section that contains executable code,
// we found our code setion.
if((sectionHeader.Characteristics & IMAGE_SCN_CNT_CODE) != 0) {
printf("[*] Code section: `%s'\n", sectionHeader.Name);
break;
}
}
fclose(dllFile);
// Load the DLL to get it's base address
if((dllHandle = LoadLibraryA(argv[1])) == NULL) {
printf("[!] Error: loading the DLL, 0x%.8x\n", (unsigned int) GetLastError());
return -1;
}
// The code start at : base address + code virtual address
starAddr = (unsigned char *) dllHandle + sectionHeader.VirtualAddress;
// It ends at : base address + code virtual address + virtual size
endAddr = (unsigned char *) starAddr + sectionHeader.Misc.VirtualSize;
printf("[*] Base address : 0x%.8x\n", (unsigned int) dllHandle);
printf("[*] Start address: 0x%.8x\n", (unsigned int) starAddr);
printf("[*] End address : 0x%.8x\n", (unsigned int) endAddr);
// Simple liner search, when ever we find `0xff 0xd5' we print that address
for(endAddr -= sizeof(opcode) ; starAddr < endAddr ; starAddr++) {
if(memcmp(&opcode, (void *) starAddr, sizeof(opcode)) == 0) {
printf("[*] Found `call ebp` at: 0x%.8x\n", (unsigned int) starAddr);
}
}
FreeLibrary(dllHandle);
return 0;
}
編譯並與該DLL測試:
> gcc -Wall findopcode.c -o findopcode
> findopcode.exe test.dll
[*] Code section: `.text'
[*] Base address : 0x6bec0000
[*] Start address: 0x6bec1000
[*] End address : 0x6bec1984
[*] Found `call ebp` at: 0x6bec125a
[*] Found `call ebp` at: 0x6bec1268
>
它工作得很好,讓我們嘗試user32.dll
:
> findopcode.exe \Windows\System32\user32.dll
[*] Code section: `.text'
[*] Base address : 0x75680000
[*] Start address: 0x75681000
[*] End address : 0x756e86ef
[*] Found `call ebp` at: 0x756b49b5
>
我只找到一個call ebp
a t 0x756b49b5
。請注意,你的方式要檢查,如果你有一個讀訪問您使用IsBadReadPtrmemcmp
閱讀前:
if(IsBadReadPtr(starAddr, sizeof(opcode)) == 0 &&
memcmp(&opcode, (void *) starAddr, sizeof(opcode)) == 0) {
所以如果你擊出了一些面積一些奇怪的訪問計劃不會失敗。
哇!優秀的答案。非常有用和信息豐富。非常感謝! –
的另一種方法是從使用msfpescan
的metasploit framework:
msfpescan -j ebp user32.dll
好,DLL是重定位(和_are being_搬遷),這樣你就永遠只能找到一個地址相對於模塊的基本地址在這樣的可靠的方式。 – Damon
@Damon對於所有現代操作系統都是如此,但對於Windows XP,沒有ASLR,因此DLL的內存中位置在操作系統版本和SP版本之間非常一致。 –
我甚至沒有想到ASLR,但這當然是另一個問題。我正在考慮常規發生的正常DLL重新綁定。所有的DLL通常都有相同的基地址(雖然可以告訴鏈接器選擇一個隨機的地址,但是這會使地址空間更加分裂),並且在加載時重新分頁。第一個DLL通常是kernel32,加載到它的原始地址。然後它通常是NLS,然後是所有其他DLL(包括user32),所有這些都被重新定位。他們可能每次都會以相同的地址結束,但誰知道。 – Damon