Как найти в памяти адрес конкретной инструкции в DLL
Как я могу найти в памяти адрес (для написания эксплойта) конкретной инструкции?
В частности, я ищу call ebp
инструкция в user32.dll
в Windows XP без пакета обновления, адрес которого я могу указать EIP
к. У меня есть и Immunity Debugger, и OllyDBG, установленные на цели.
2 ответа
Чтобы найти инструкцию, вам нужно выяснить, где код, .text, раздел начинается и заканчивается, затем загружать DLL и просто выполнять поиск лайнера, пока не найдете инструкцию.
Здесь у нас есть тестовая DLL, которая имеет два call ebp
инструкции:
// 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");
}
Скомпилируйте его и загрузите DLL в ollydbg, нажмите 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
сканер, нам нужно сделать текущий:
- Прочитайте информацию PE и получите информацию об исполняемом разделе, обычно
.text
, чтобы найти его относительный адрес и его виртуальный размер. - Загрузите DLL, используя LoadLibrary, она вернет базовый адрес DLL.
- Виртуальный адрес начала секции кода: Базовый адрес DLL + секция кода virtualAddress, и он заканчивается в Базовом адресе DLL + секция кода virtualAddress + VirtualSize.
- Теперь мы готовы пройтись по коду и искать
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
в 0x756b49b5
, Обратите внимание, вы хотите проверить, есть ли у вас доступ для чтения, прежде чем читать с memcmp
используя IsBadReadPtr:
if( IsBadReadPtr(starAddr, sizeof(opcode)) == 0 &&
memcmp(&opcode, (void *) starAddr, sizeof(opcode)) == 0 ) {
так что программа не выйдет из строя, если вы попадете в какую-то область со странным доступом.
Альтернативным способом является использование msfpescan
из структуры Metasploit:
msfpescan -j ebp user32.dll