Как найти адрес возврата

The one above it is the previous EBP (0012FF80). The value above the prev-EBP is always the return address.

(This obviously assumes a non-FPO binary and 32bit Windows)1.

If you recall, the prologue looks like:

push ebp      ; back up the previous ebp on the stack
mov ebp, esp  ; set up the new frame pointer

and when a function is called, e.g.

call 0x00401000

The current EIP is pushed on the stack (used as the return address), so the stack after the prologue looks like:

[ebp+0xc]  ; contains parameter 1, etc
[ebp+0x8]  ; contains parameter 0
[ebp+0x4]  ; contains return address
[ebp]      ; contains prev-EBP

So for each %p, printf uses the next 4 bytes starting from [ebp+0xc] (the first %p parameter). Eventually you hit the previous EBP value stored on the stack, which is (0012FF80) and then the next one is the Return Address.

Note these addresses must ‘make sense’, which they clearly do here (though it may not be ‘clear’ for all).

Re Q2) The stack grows down. So when you push eax, 4 is subtracted from esp, then the value of eax is moved to [esp], equivalently in code:

push eax
;  <=>
sub esp, 4
mov [esp], eax

  1. The book is Writing Secure Code, yes?

Эти функции могут быть использованы для получения информации об абонентах функции.

Встроенная функция: void * __builtin_return_address (беззнаковый level int )

Эта функция возвращает адрес возврата текущей функции или одного из ее вызывающих. level аргумент число кадров для сканирования стеки вызовов. Значение 0 дает адрес возврата текущей функции, значение 1 дает адрес возврата вызывающей стороны текущей функции и так далее. Ожидаемое поведение при встраивании состоит в том, что функция возвращает адрес возвращаемой функции. Чтобы обойти это поведение, используйте noinline функции noinline .

level аргумент должен быть постоянным целым числом.

На некоторых машинах может быть невозможно определить адрес возврата какой-либо функции, кроме текущей; в таких случаях или при достижении вершины стека эта функция возвращает неопределенное значение. Кроме того, __builtin_frame_address может использоваться, чтобы определить, достигнута ли вершина стека.

Может потребоваться дополнительная постобработка возвращаемого значения, см. __builtin_extract_return_addr .

Сохраненное представление адреса возврата в памяти может отличаться от адреса, возвращаемого __builtin_return_address . Например, в AArch64 сохраненный адрес может быть изменен с помощью подписи обратного адреса, тогда как адрес, возвращаемый __builtin_return_address , — нет.

Вызов этой функции с ненулевым аргументом может привести к непредсказуемым последствиям,в том числе к аварийному завершению работы вызывающей программы.В результате,вызовы,считающиеся небезопасными,диагностируются при помощи функции-Wframe-addressопция в действии.Такие вызовы должны выполняться только в отладочных ситуациях.

На целевых объектах, где кодовые адреса представлены как void * ,

void *addr = __builtin_extract_return_addr (__builtin_return_address (0));

дает адрес кода, по которому будет возвращаться текущая функция. Например, такой адрес может использоваться с dladdr или другими интерфейсами, которые работают с кодовыми адресами.

Встроенная функция: void * (void * addr )

Адрес, возвращаемый __builtin_return_address , может потребоваться передать через эту функцию, чтобы получить фактический закодированный адрес. Например, на 31-битной платформе S / 390 старший бит должен быть замаскирован, или на платформах SPARC необходимо добавить смещение для выполнения истинной следующей инструкции.

Если исправление не требуется, эта функция просто проходит через addr .

Встроенная функция: void * __builtin_frob_return_addr (void * addr )

Эта функция делает обратное __builtin_extract_return_addr .

Встроенная функция: void * __builtin_frame_address (беззнаковый level int )

Эта функция похожа на __builtin_return_address , но возвращает адрес фрейма функции, а не адрес возврата функции. Вызов __builtin_frame_address со значением 0 дает адрес кадра текущей функции, значение 1 дает адрес кадра вызывающего текущую функцию и так далее.

Фрейм — это область в стеке, в которой хранятся локальные переменные и сохраненные регистры. Адрес кадра обычно является адресом первого слова, помещенного функцией в стек. Однако точное определение зависит от процессора и соглашения о вызовах. Если процессор имеет специальный регистр указателя кадра, а функция имеет кадр, то __builtin_frame_address возвращает значение регистра указателя кадра.

На некоторых машинах может быть невозможно определить адрес кадра какой-либо функции, кроме текущей; в таких случаях или при достижении вершины стека эта функция возвращает 0 , если указатель первого кадра правильно инициализирован кодом запуска.

Вызов этой функции с ненулевым аргументом может привести к непредсказуемым последствиям,в том числе к аварийному завершению работы вызывающей программы.В результате,вызовы,считающиеся небезопасными,диагностируются при помощи функции-Wframe-addressопция в действии.Такие вызовы должны выполняться только в отладочных ситуациях.

Далее: Векторные расширения , Предыдущий: Имена функций , Вверх: Расширения C [ Содержание ][ Индекс ]


GCC

12.2

  • 6.60.13.4 Необработанные функции чтения/записи

    В этом разделе описаны встроенные функции,связанные с доступом к памяти инструкций чтения и записи.

  • 7.2 Ограничение псевдослучайности указателя

    Как и в случае с C,G++понимает особенность C99-ограниченные указатели,указанные __restrict__,или квалификатор типа.

  • 6.60.28 Встроенные функции RISC-V

    Эти встроенные функции доступны для процессоров семейства RISC-V.

  • 6.33.27 Атрибуты функций RISC-V

    Эти атрибуты функций поддерживаются бэкэндом RISC-V:Этот атрибут позволяет компилятору построить требуемое объявление функции,при этом позволяя

Введение в реверсинг с нуля, используя IDA PRO. Часть 67. Часть 1.

Дата публикации 24 апр 2019

| Редактировалось 4 июл 2019

Метод, который мы не рассмотрели, чтобы эксплуатировать одну из уязвимостей драйвера HACKSYS.

Мой друг попросил у меня несколько разъяснений о методе, используемом для обхода COOKIE на 32-битных машинах, когда у нас есть переполнение стека в ядре, и мы можем перезаписать адрес возврата, но есть COOKIE, который мешает нам завершить выполнение кода.

Очевидно, что если у нас есть другая уязвимость, которая допускает утечку данных, мы могли бы прочитать значение COOKIE и затем использовать его для отправки наших данных, для перезаписи, но есть метод, который я никогда не использовал на практике, который немного стар и в системах, отличных от WINDOWS 7 32 бит не будет работать, но было бы хорошо взглянуть на него, чтобы уточнить для моего друга и того, кто читает меня (и для меня самого).

Мы уже знаем, что уязвимый драйвер можно скачать отсюда.

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/releases/download/v1.20/HEVD.1.20.zip

А сам инструмент для загрузки драйвера, можно загрузить отсюда.

http://www.osronline.com/OsrDown.cfm/osrloaderv30.zip?name=osrloaderv30.zip&id=157

Прежде чем скопировать драйвер в целевую машину, мы откроем его в загрузчике IDA для его анализа.

Внутри ZIP архива находятся такие файлы.

HEVD.1.20DRVVULNERABLEI386

1.png

Это драйвер и его символы.

2.png

При открытии драйвера в IDA, мы видим функцию DRIVERENTRY, первым аргументом которой всегда является указатель на объект _DRIVER_OBJECT.

3.png

Регистр ESI это указатель на объект _DRIVER_OBJECT.

Если мы перейдём во вкладку LOCAL TYPES.

4.png

Мы видим, что эта структура. Если мы поищем структуру _IRP, мы находим наиболее часто используемым для обращения структуры.

5.png

Мы будем отмечать структуры _DRIVER_OBJECT, _IRP, _DEVICE_OBJECT, _IO_STACK_LOCATION и PIRP и синхронизировать их, потому что именно их мы будем использовать чаще всего.

То что нам нужно сделать — это добавить структуру MAJORFUNCTION, которой нигде нет.

Напомним, что мы можем добавить структуру через вкладку LOCAL TYPES, используя правую кнопку мыши, пункт INSERT и вставить этот код.

struct __MajorFunction
{
SIZE_T _MJ_CREATE;
SIZE_T _MJ_CREATE_NAMED_PIPE;
SIZE_T _MJ_CLOSE;
SIZE_T _MJ_READ;
SIZE_T _MJ_WRITE;
SIZE_T _MJ_QUERY_INFORMATION;
SIZE_T _MJ_SET_INFORMATION;
SIZE_T _MJ_QUERY_EA;
SIZE_T _MJ_SET_EA;
SIZE_T _MJ_FLUSH_BUFFERS;
SIZE_T _MJ_QUERY_VOLUME_INFORMATION;
SIZE_T _MJ_SET_VOLUME_INFORMATION;
SIZE_T _MJ_DIRECTORY_CONTROL;
SIZE_T _MJ_FILE_SYSTEM_CONTROL;
SIZE_T _MJ_DEVICE_CONTROL;
SIZE_T _MJ_INTERNAL_DEVICE_CONTROL;
SIZE_T _MJ_SCSI;
SIZE_T _MJ_SHUTDOWN;
SIZE_T _MJ_LOCK_CONTROL;
SIZE_T _MJ_CLEANUP;
SIZE_T _MJ_CREATE_MAILSLOT;
SIZE_T _MJ_QUERY_SECURITY;
SIZE_T _MJ_SET_SECURITY;
SIZE_T _MJ_POWER;
SIZE_T _MJ_SYSTEM_CONTROL;
SIZE_T _MJ_DEVICE_CHANGE;
SIZE_T _MJ_QUERY_QUOTA;
SIZE_T _MJ_SET_QUOTA;
SIZE_T _MJ_PNP;
SIZE_T _MJ_PNP_POWER;
SIZE_T _MJ_MAXIMUM_FUNCTION;
};

6.png

Мы добавляем и синхронизируем структуру, и последнее что мы делаем – открываем во вкладке LOCAL TYPES структуру _DRIVER_OBJECT и изменяем ее так, чтобы последнее поле представляло собой структуру типа _MAJORFUNCTION.

7.png

Мы видим, что изначально это массив указателей на функции, но если мы изменим его на структуру с указателями на известные функции с их именами, это будет проще и будет работать так же, и это даст нам информацию, которая нам необходима в более четкой форме.

8.png

Таким образом, вместо того, чтобы был массивом указателей на функции, про которые мы ничего не знаем, что делает каждая из них, это будет структура той же длины, что и массив, но с указателями на функции, уже известные в соответствии со спецификацией.

Мы видим, что регистр ESI сохраняет значение указателя на объект _DRIVER_OBJECT в этой области, а по адресу 0x000160СF и это значение указателя затирается.

9.png

Таким образом, мы помечаем эту зону (это можно сделать с помощью ALT + L, спустится стрелкой курсора и затем снова нажать ALT + L для завершения) или если это небольшая область сделать это той же мышью.

10.png

Как только зона отмечена, мы нажимаем T.

11.png

Конечно, мы выбираем регистр ESI в качестве базового регистра структуры и смещение, которое мы устанавливаем в нуль, потому что оно указывает на начало структуры, и мы выбираем структуру _DRIVER_OBJECT, и IDA обнаруживает 4 поля которые используются.

12.png

Для нас важно то поле, которое обрабатывает IOCTL, т.е. _MJ_DEVICE_CONTROL.

Если бы у нас не было символов, такая же работа выполнялась бы путем импорта файла .H с 32-битными структурами для реверсинга драйверов.

Файл .H с 32-битными структурами находится здесь.

https://drive.google.com/file/d/1VXwR45uvw1FtvzW2b9eNO1DLid9CIdx8/view?usp=sharing

И он импортируется в IDA отсюда.

13.png

При этом мы увидим необходимые структуры DRIVER_OBJECT, _IRP, _DEVICE_OBJECT, _IO_STACK_LOCATION и PIRP и _MAJORFUNCTION в LOCAL TYPES. Мы синхронизируем их и получим таким же образом возможность распознавать функцию, которая обрабатывает IOCTL.

В этом случае, наличие символов для этой функции уже имело имя, что дало нам представление о том, что это искомая функция, но по мере того, как мы учимся, полезно знать это всё, чтобы найти ее для всех случаев реверсинга, либо с символами, либо без символов.

14.png

Внутри этой функции, которая обрабатывает IOCTL, находятся различные уязвимые функции. В этом случае мы будем пытаться эксплуатировать функцию STACKOVERFLOWG.

15.png

Вызов идет сюда.

16.png

И затем сюда.

Мы видим, что есть функция MEMCPY, которая копирует буфер в стек. MAXCOUNT — количество байтов. Полностью отреверсив функцию мы увидим, что у неё есть COOKIE. Хотя мы это уже видели до адреса возврата, который в отличие от другого переполнения стека, который мы уже эксплуатировали.

17.png

Перед инструкцией RETN есть такой вызов.

18.png

Эта проверка и в начале функции она то же есть.

19.png

Возвращаясь к началу функции с именем IRPDEVICEIOCTLHANDLER, которая обрабатывает IOCTL, в регистре EDI передается указатель на структуру IRP. Мы уже видели в предыдущих туториалах, что в 32х битных системах по смещению 0x60 это был указатель на структуру IO_STACK_LOCATION, которая останется в регистре ESI.

И я нажимаю T на ESI + 0xC.

20.png

Как мы уже видели, что структура IO_STACK_LOCATION варьируется в зависимости от функции, в которой она используется , поскольку здесь мы используем её в случае функции, которая обрабатывает IOCTL. Мы должны выбрать DEVICEIOCONTROL.

21.png

Я оставлю всё это так. В этой функции, регистр ESI имеет указатель на IO_STACK_LOCATION, регистр EDX – это код IOCTL (IOCONTROLCODE) и EDI указатель на _IRP.

22.png

Регистр ESI и регистр EDI – это два аргумента вызова функции STACKOVERFLOWGSIOCTLHANDLER.

23.png

Конечно, поскольку у нас есть символы, мы можем видеть эти два аргумента в определении функции. Первый — это указатель на _IRP, а второй — указатель на IO_STACK_LOCATION.

24.png

Опять же, пытаясь определить поле _IO_STACK_LOCATION, мы должны выбрать случай, когда используется DEVICEIOCONTROL.

25.png

Мы видим, что то, что мы определили при реверсинге, совпадает с тем, что нам показывают символы.

26.png

Поле INPUTBUFFERLENGHT — это размер входного буфера из режима пользователя, а TYPE3INPUTBUFFER — указатель на этот пользовательский входной буфер, который мы также передаем.

Переименуем эти два аргумента. Помните, что та переменная, которую мы называем SIZE_BUFFER_USER, является произвольным переданным нами числом, которое должно быть размером буфера, но это может быть любое значение, так как не видно никакой проверки.

27.png

Мы видим, что обе переменные используются без проверки или изменения в функции MEMCPY.

28.png

Назначением функции MEMCPY является буфер в стеке, который мы можем перезаписать. Проблема в том, что здесь функция не помогает нам перезаписать весь стек, пока он не закончится, потому что в этом случае SEH не вызывается так, как в пользовательском режиме, и создается BSOD, поэтому необходимо использовать другую технику.

29.png

30.png

Инициализируется только 0x1FF байт буфера. Также нет проблем, что некоторые байты ещё остались нетронутыми.

31.png

В начале функции мы видим, что выше адреса возврата есть структура CPPEH_RECORD.

32.png

Она находится чуть ниже буфера и поверх адреса возврата.

33.png

Мы видим, что в стек помещаются два аргумента. Константа 0x210 и указатель на структуру. Константа 0x210, так как это первый PUSH, будет чуть выше адреса возврата R, который я сохранил при входе в эту же функцию пролога.

34.png

Однако мы видим, что IDA показывает нам, что чуть выше «R» находится сохраненный регистр EBP т.е. «S«.

35.png

Также, если мы войдем в функцию __SEH_PROLOG4_GS.

36.png

Мы видим, что после помещения в регистр EAX значения переменной CONS_0x210, программа сохраняет здесь регистр EBP, так что на самом деле выше «R» , наконец, остается «S» или сохраненный регистр EBP.

Затем, над сохраненным регистром EBP, помещается указатель на эту структуру которая передается сразу после инструкции PUSH 0x210.

37.png

По этому адресу находится структура STRU_0x12218. Программа сохраняет её чуть выше «S«.

38.png

И чуть выше “S” находится переменная MS_EXC, которая является структурой типа CPPEH_RECORD, так что этот адрес будет последним полем этой структуры, и мы увидим это.

39.png

Здесь после сохранения сохраненного регистра EBP в переименую CONST_0X210 программа помещает адрес указанной переменной в регистр EBP. Это будет более или менее похоже на начало функции PUSH EBP, MOV EBP, ESP.

Оба должны сохранить значение регистра EBP родительской функции TRIGGERSTACKOVERFLOWGS и установить новый регистр EBP для неё через инструкцию LEA.

40.png

Затем программа освобождает место для переменных, выполняя инструкцию SUB ESP, EAX.

И также мы видим, что по адресу EBP-4 XORится значение, которое было там с COOKIE, которое читается из секции данных.

Напомним, что по адресу EBP-4 находится значение 0x12218. С этим значением XORится COOKIE и сохраняется по этому адресу.

41.png

Кроме того, XORится COOKIE с регистром EBP и полученное значение сохраняется по адресу EBP-1C.

42.png

Это то, что программа будет проверять в эпилоге.

43.png

И внутри функции __SECURITY_CHECK_COOKIE.

44.png

Программа сравнивает значения. Если они одинаковы, то всё хорошо, а если нет мне выкидывает BSOD.

Мы создадим стек с самого начала в соответствии с порядком, с которым программа размещает значения перед входом в пролог:

PUSH 0x210
PUSH 0x12218

Затем программа входит в пролог, который заставляет её сохранять адрес возврата в стеке, куда она будет возвращаться. Это будет адрес 0x000148E9, поскольку при выходе из пролога программа вернется туда.

45.png

Таким образом, при входе в пролог мы имеем в стеке два аргумента и адрес возврата, куда вернется программа.

46.png

Давайте продолжим смотреть на то, как аргументы помещаются в стек.

Затем есть еще две инструкции PUSH — адрес функции EXCEPTION_HANDLER4 и значение, которое содержит регистр FS:0.

47.png

Над обратным адресом тогда будут эти два значения.

48.png

Затем 0x210 перезаписывается STORED_EBP.

49.png

Мы знаем, что под сохраненным регистром EBP был адрес возврата в функцию TRIGGERSTACKOVERFLOWGS. Мы добавили его в наше представление стека.

50.png

Текущий регистр EBP остается с адресом STORE_EBP (смотри на адрес, а не значение)

Из регистра ESP вычитается значение 0x210 для пространства переменных, другими словами над адресом FS:00x210 останется регистр ESP.

51.png

Затем выше есть еще три PUSHEBX, ESI и EDI.

52.png

53.png

Затем содержимое EBP-4 XORится с COOKIE.

Поскольку ТЕКУЩИЙ EBP продолжает указывать на адрес STORE_EBP, EBP-4 указывает на значение 0x12218, это значение XORится с COOKIE.

ЗНАЧЕНИЕ EBX
ЗНАЧЕНИЕ ESI
ЗНАЧЕНИЕ EDI




FS:0
__EXCEPT_HANDLER4
0x148E9 <—- АДРЕС ВОЗВРАТА В ПРОЛОГ
0x12218 <———XORится с COOKIE
STORED_EBP <—— ТЕКУЩИЙ EBP — АДРЕС STORED_EBP
АДРЕС ВОЗВРАТА В ФУНКЦИЮ TriggerStackOverflowGS

Если мы сделаем так, чтобы уточнить первый столбец, с адресами ссылающимися на значение ТЕКУЩЕГО EBP.

ЗНАЧЕНИЕ EBX
ЗНАЧЕНИЕ ESI
ЗНАЧЕНИЕ EDI




EBP-10 FS:0
EBP-C __EXCEPT_HANDLER4
EBP-8 0x148E9 <—- АДРЕС ВОЗВРАТА В ПРОЛОГ
EBP-4 0x12218 <———XORИТСЯ С COOKIE
EBP STORED_EBP <—— ТЕКУЩИЙ EBP — АДРЕС STORED_EBP
АДРЕС ВОЗВРАТА В
ФУНКЦИЮ TRIGGERSTACKOVERFLOWGS

Одна из проблем здесь заключается в том, что это не нормальная функция, которая при входе и выходе из ESP остается такой же, как и до помещения аргументов. Ваши аргументы хорошо сбалансированы. Это функция, которая является прологом функции TRIGGERSTACKOVERFLOWGS. Этот код должен быть частью той же функции и не должет быть идти отдельным CALL.

Затем программа вычитает значение из регистра ESP, чтобы освободить место для переменных для этой функции, и идет, чтобы создать стек, но затем не возвращается, как в обычной функции, ищет адрес возврата и возвращает значение регистра ESP, где он был. Это не работает здесь, потому что регистр ESP должен сохранить значение, которое он уже отнял, и освободил место для переменных.

В обычной функции регистр ESP при возврате равен тому же значения, что и перед передачей аргументов.

54.png

Но в данном конкретном случае эта специальная функция похожа на часть функции TRIGGERSTACKOVERFLOWGS, выполняемой в отдельном CALL.

Если принимать регистр ESP в качестве нуля в начале функции, я вижу, что при возврате из CALL регистр увеличивается на 0x234 байт, потому что внутри функции пролога было сделано несколько инструкций PUSH, была выполнена инструкция SUB ESP, 0x210, и был осуществлен возврат из функции без восстановления регистра ESP.

55.png

Многие скажут, но если регистр ESP не восстановлен, как найти адрес возврата в стеке, который намного ниже значения регистра ESP.

Мы говорили, что адрес EBP-8 указывает на адрес возврата, чтобы вернуться из функции пролога в TRIGGERSTACKOVERFLOWGS и ТЕКУЩИЙ ESP после того, как три PUSH из EBX, ESI и EDI остались выше.

56.png

Если мы посмотрим в функции пролога, увидим, что она возвращает адрес возврата с помощью инструкции PUSH RET.

57.png

Помещенное в стек значение, указанное через адрес EBP-8, является адресом возврата. Программа помещает значение обратно в стек и затем выполняет инструкцию RET, и программа возвращается к функции TRIGGERSTACKOVERFLOWGS, не восстанавливая ESP и оставляя весь стек целым, как это было в прологе.

Между инструкцией PUSH и RET есть только инструкции MOV и LEA, поэтому стек не затрагивается, и это аналогично PUSHRET.

Мы уже знаем, как функция начинается, как все устроено в стеке и как функция возвращается, у нас есть некоторые вещи которые находятся в середине после трех PUSH перед возвратом.

Мы создали стек здесь.

58.png

До этого момента он был создан так.

ЗНАЧЕНИЕ EBX
ЗНАЧЕНИЕ ESI
ЗНАЧЕНИЕ EDI




EBP-10 FS:0
EBP-C __EXCEPT_HANDLER4
EBP-8 0x148E9 <—- АДРЕС ВОЗВРАТА В ФУНКЦИЮ ПРОЛОГ
EBP-4 0x12218 <———XORED COOKIE
EBP STORED_EBP <—— ТЕКУЩИЙ EBP — АДРЕС STORED_EBP
АДРЕС ВОЗВРАТА В ФУНКЦИЮ TRIGGERSTACKOVERFLOWGS

Мы уже знаем, что ничего из этого не будет потеряно, все, что я добавлю или изменю в прологе в стеке, не будет удалено, поскольку PUSH-RET покинет стек, как это было для функции TRIGGERSTACKOVERFLOWGS.

Еще одна вещь, которая уже настроена для функции TRIGGERSTACKOVERFLOWGS, это регистр EBP.

59.png

С помощью LEA вычисляется база для переменных и аргументов не только пролога, но и функции TRIGGERSTACKOVERFLOWGS, поскольку начиная с этого момента её значение остается постоянным, даже после возвращения.

Я смотрю функцию TRIGGERSTACKOVERFLOWGS, чтобы попытаться увидеть, где это соответствует адресу EBP-1C, где программа хранит COOKIE.

60.png

Мы видим, что переменная MS_EXC находится по адрес EBP-0x18. Другими словами место, где программа хранит COOKIE, которое вы собираетесь проверить, находится чуть выше структуры MS_EXC.

Напомним, что буфер DST был инициализирован только с 0x1FF байтами, и мы сказали, что осталось несколько байтов чуть ниже него, поэтому, если мы поправим размер DST на 0x1FF, у нас будет переменная, в которой сохраняется COOKIE в стеке.

61.png

Здесь я назначаю новый размер и у меня остается четыре пустых байта между ними. Я нажимаю D, пока я не изменю на DWORD (DD), и переименую переменную в COOKIE.

62.png

Я вижу, что это по адресу EBP-1C (слева от названия есть позиция относительно EBP т.е. 0x0000001C).

Затем идет инструкция PUSH EAX и сохраняется текущее значение регистра ESP по адресу EBP-18, которое было внутри структуры MS_EXC, которая начинается здесь. Это первое поле той же структуры.

63.png

Если мы заглянем внутрь структуры, первым полем будет OLD ESP

64.png

Так что стек стал таким

ЗНАЧЕНИЕ EAX
ЗНАЧЕНИЕ EBX
ЗНАЧЕНИЕ ESI
ЗНАЧЕНИЕ EDI


EBP-10 FS:0
EBP-C __EXCEPT_HANDLER4
EBP-8 0x148E9 <—- АДРЕС ВОЗВРАТА В ПРОЛОГ
EBP-4 0x12218 <———XORится с COOKIE
EBP STORED_EBP <—— ТЕКУЩИЙ EBP — АДРЕС STORED_EBP
АДРЕС ВОЗВРАТА В ФУНКЦИЮ TRIGGERSTACKOVERFLOWGS

Поскольку теперь обе функции совместно используют стек, если мы сравним их, мы увидим, что над STORED_EBP находится значение MS_EXC, поэтому внутри пролога чуть выше переменной «S» байты также являются полями указанной структуры.

65.png

Эти 4 DWORDS являются 4 нижними полями структуры MS_EXC.

66.png

Помните, что последние 4 поля структуры — это другая структура размером 0x10 байтов, т.е. 16 в десятичной системе (4 DWORDS), поэтому на изображении отмечены только те 4 DWORDS.

67.png

Двумя важными переменными являются NEXT и EXCEPTION HANDLER. Мы уже знаем их положение в стеке. Мы видим, что переменная NEXT в структуре имеет значение FS:0, а EXCEPTION HANDLER на данный момент имеет значение __EXCEPT_HANDLER4, хотя они еще не добавлены в цепочку SEH.

ЗНАЧЕНИЕ EAX
ЗНАЧЕНИЕ EBX
ЗНАЧЕНИЕ ESI
ЗНАЧЕНИЕ EDI




EBP-10 FS:0 — (NEXT)
EBP-C __EXCEPT_HANDLER4 — (EXCEPTION_HANDLER)
EBP-8 0x148E9 <—- АДРЕС ВОЗВРАТА В ПРОЛОГ -(SCOPETABLE)
EBP-4 0x12218 <———XORится с COOKIE (TRYLEVEL)
EBP STORED_EBP <—— ТЕКУЩИЙ EBP — АДРЕС STORED_EBP
АДРЕС ВОЗВРАТА В ФУНКЦИЮ TRIGGERSTACKOVERFLOWGS

Хорошо. У нас создан стек, и мы видим справа синие поля структуры.

Поскольку адрес возврата уже помещен в стек, изменение значения сохраненной переменной не имеет значения.

68.png

Мы видим, что в по адресу EBP-8 (SCOPETABLE) программа сохраняет значение COOKIE, XORит его со значением 0x12218, которое было в EBP-4, а затем в том же EBP-4, что является TRYLEVEL, сохраняет значение 0xFFFFFFFE.

В конце программа сохраняет адрес EBP-10 — NEXT в регистр FS:0 с настроенным обработчиком исключений.

Мы знаем, что регистр FS:0 указывает на последний элемент в списке цепочки исключений, т.е на верхнюю часть всей цепочки.

Помните, что добавление нового элемента в список осуществляется с помощью этого кода

PUSH OFFSET HANDLER
PUSH FS:[0]
MOV FS:[0], ESP

Т.е. поскольку здесь выполняется следующая инструкция.

MOV LARGE FS:0, EAX

Этот регистр EAX является адресом стека, где находится новый NEXT и ниже SEH.

Так как регистр EAX является адресом EBP-10, здесь будет переменная NEXT и чуть ниже SEH, как мы уже говорили.

Если я отлаживаю код и отправляю данные эксплойту, который обходит правильный IOCTL для достижения уязвимой функции (Позже мы увидим, как это сделать. Сейчас же это просто для проверки).

69.png

Я вижу, что регистр FS:0 указывает на верхний элемент цепочки SEH. В моем случае он равен 9CCEFCC0. Если я посмотрю, здесь должны быть переменные NEXT и SEH. Переменная NEXT равна 0xFFFFFFFF, потому что это последний NEXT в цепочке исключений.

70.png

Функция является типичным универсальным обработчиком. Если я собираюсь увидеть, что это за байты, то нажимаю C чтобы создать функцию.

71.png

Если я продолжу трассировать пролог, я попадаю туда, где регистр EAX сохранится в регистр FS:0.

72.png

Здесь мы видим новый драйвер, добавленный в цепочку.

73.png

Как мы уже рассматривали, адрес EBP-10 будет новым NEXT, а ниже находится SEH, который будет являться _EXCEPT_HANDLER4. Это то значение, которое мы должны будем переписать для эксплуатации

74.png

Хорошо. У нас уже все хорошо расположено. Пора начинать писать эксплойт.

Метод заключается в том, что, когда мы копируем из пользовательского буфера, который является источником, и мы предоставляем, вместо того, чтобы сломать стек, заполняя его полностью, мы должны вычислить, какой источник копирует SEH в стек. Его размер должен быть просто легко быть рассчитан, чтобы закончиться сразу после копирования SEH.

Идея состоит в том, что, поскольку сбой происходит при доступе на чтение к буферу пользовательского режима, это приводит к тому, что он обрабатывается как сбой в режиме пользователя и происходит переходит к SEH, а не обрабатывается как сбой ядра, что вызывает BSOD.

Метод работает, но эксплойт делаем сбой и вызывает BSOD. Так что нам нужно будет увидеть, где произошел сбой. Наверняка есть что-то что мы не видим.

Основное объяснение этого метода находится здесь:

http://poppopret.blogspot.com/2011/07/windows-kernel-exploitation-basics-part_16.html

И исходный код публичного эксплойта находится здесь:

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/tree/master/Exploit

Я не собираюсь делать все это также на PYTHON, потому что это того не стоит, но давайте посмотрим, как автор это объясняет и исправим, то что не работает.

Прежде всего, если бы мы сделали это в PYTHON, у нас была бы проблема, которую можно решить, но, компилируя его в C++, у нас уже есть модуль, который помимо запуска и экслуатации повышения привилегий, мы можем скомпилировать его по своему вкусу, например, без SAFESEH, или DEP или ASLR. В опциях VISUAL STUDIO позволят нам выбрать то что нужно, поэтому, если кто-то загружает решение, т.е. файл SLN в VISUAL STUDIO, вам придется изменить параметры по умолчанию.

75.png

Чуть выше находится это.

76.png

Хорошо. Я приложу скомпилированный файл с его символами HACKSYSEVDEXPLOIT.EXE и HACKSYSEVDEXPLOIT.PDB, чтобы его было легко увидеть в IDA.

Исполняемый файл скомпилирован для всех уязвимостей, которые есть у драйвера, и его выполнение в консоли в WINDOWS 7 32 с аргументами -G -C XXX.EXE достаточно, так как я уже добавляю в конце этого метода выполнение калькулятора c правами SYSTEM после поднятия прав. В остальных вместо XXX.EXE придется подставить CALC.EXE или CMD.EXE

Хорошо. Мы переходим к функции, которая использует эту уязвимость. В данном случае — STACKOVERFLOWGSTHREAD.

77.png

После исправления некоторых проблем консоли, которые не имеют отношения, давайте проанализируем эксплойт, который мы открываем в IDA, и видим, что эксплойт начинается здесь:

78.png

Внутри мы видим вызов функции CREATEFILE для получения дескриптора драйвера.

79.png

Все это так же, как и случаи, которые мы видели в предыдущих ядрах.

Регистр EBX остается с дескриптором драйвера, он используется только при вызове DEVICEIOCONTROL ниже.

80.png

Хорошо. Затем идет вызов функции CREATEFILEMAPPING, что является пространством виртуальной памяти, которое будет связано с содержимым файла. (Функция не резервирует память, только создает объект и возвращает дескриптор)

https://docs.microsoft.com/en-us/windows/desktop/memory/file-mapping

81.png

Но если мы посмотрим в описание функции CREATEFILEMAPPING, мы увидим, что первым аргументом является дескриптором файла, но нам также говорится, что может быть передан аргумент INVALID_HANDLE_VALUE. В этом случае, программа создаст отображение файла, не связывая его с файлом, и это будет общая анонимная память.

82.png

83.png

Хорошо. Это тот случай, поэтому мы видим, что когда вызывается эта API, вы передаете значение 0xFFFFFFFF, которое является INVALID_HANDLE_VALUE.

84.png

В исходном коде, под названием SHARED MEMORY, созданном здесь, мы видим, что функции передаются разрешение на выполнение, чтение и запись.

85.png

Хорошо. Она возвращает нам дескриптор файлового отображения.

86.png

Затем программа вызывает функцию MAPVIEWOFFILE, которая отображает объект в памяти, зарезервировав необходимое для него пространство.

87.png

88.png

Хорошо. Функция возвращает адрес начала секции, созданного для отображения файлов.

Чтобы отладить эксплойт в режиме пользователе, несмотря на драйвер, я копирую сервер IDAWIN32_REMOTE.EXE в целевую машину и запускаю его.

89.png

Я запускаю его на сервере с правами администратора в целевой системе, а на машине, где я реверсил эксплойт, меняю отладчик на удаленный отладчик WINDOWS. В PROCESS OPTIONS я указываю IP-адрес и порт.

90.png

91.png

Напомним, что мы можем отлично отладить этот эксплойт в пользовательском режиме, но на шелл-код, который вызывается из ядра, мы не сможем поставить BP или что-либо еще, потому что это вызовет исключение INT3 в ядре, которое не обрабатывается как в пользовательском режиме, и будет создан BSOD.

Если мы запустим файл без аргументов, он покажет нам опции.

92.png

Я запускаю эксплойт с аргументами -G, чтобы задействовать уязвимость STACK OVERFLOW GS,

93.png

Теперь я ожидаю.

94.png

Так что я могу теперь прикрепить IDA, ту где я реверсил эксплойт (не ту, которой был проанализирован драйвер)

95.png

Нажав клавишу в целевой машине, чтобы пропустить паузу, мы останавливаемся на BP, который я ставлю после паузы.

96.png

Я дохожу до функции CREATEFILEMAPPING.

97.png

Пройдя вызов с помощью F8, мне возвращается дескриптор файла.

98.png

Как мы уже говорили, этот дескриптор передается в регистре ESI.

99.png

Здесь нам вернется адрес отображаемого файла.

100.png

Это секция будет из 0x1000 байт.


yashechka

yashechka
Ростовский фанат Нарвахи

Регистрация:
2 янв 2012
Публикаций:
90


WASM

Есть такой код на MASM32:

.386
.model flat, stdcall
option casemap:none
includelib masm32libkernel32.lib
ExitProcess proto: DWORD

.code
    start:
        push DWORD PTR 2
        push DWORD PTR 3
        call AddDigs
        mov ecx, [esp - 4];

        invoke ExitProcess, 0

        AddDigs proc
            mov eax, [esp + 8]
            add eax, [esp + 4]
            ret 8
        AddDigs endp
    end start

Я хочу разобраться, что происходит со стеком при вызове этой процедуры и хочу получить адрес возврата, который хранился в стеке.

Сначала мы кладем на стек 2, регистр ESP явно указывает на это число. Затем кладем туда число 3, оно ложится на вершину, и теперь двойка находится по адресу [esp — 4]. После выполнения инструкции call на стек помещается адрес возврата 0040100F, а числа 2 и 3 находятся теперь по адресам [esp — 8] и [esp — 4] соответственно. Будем считать, что в процедуре вместо ret 8 написано ret, т.е. мы просто снимаем со стека адрес возврата и переходим в отладчике на следующую строчку после call, а мусор остается в стеке. Как в этом месте получить значение адреса возврата?

Если сразу после вызова call выполнить инструкцию mov ecx, [esp — 4], то в регистре ecx окажется число 2, а инструкция mov ecx, [esp — 8] помещает туда число 3. Почему так? Ведь двойка находится дальше от вершины стека, чем тройка, и поэтому чтобы добраться до нее, надо отнять от вершины стека не 4, а 8.

Когда мы снимаем со стека адрес возврата, регистр esp увеличивается на 4, потому что стек растет в сторону уменьшения адресов. Значит, чтобы добраться до адреса возврата, который только что лежал на вершине, от адреса вершины надо отнять 4. Почему же вместо адреса возврата оттуда вытаскиваются числа?

Олли дебагер

На второй картинке программа, где в процедуре вместо ret 8 написано просто ret. В окне стека видно, как в сторону увеличения адресов лежат последовательно числа 3 и 2. То есть адрес возврата находится выше и не виден в этом окне? Кажется, получить его обратно все-таки удалось, но все равно расскажите побольше теории обо всем этом.

Олли2

0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

1

Переполнение буфера, адрес возврата

23.03.2015, 18:39. Показов 8978. Ответов 26


Студворк — интернет-сервис помощи студентам

Подскажите пожалуйста, где можно найти адрес возврата при переполнении буфера. Visual Studio 2012 windows 8.1



0



Programming

Эксперт

94731 / 64177 / 26122

Регистрация: 12.04.2006

Сообщений: 116,782

23.03.2015, 18:39

Ответы с готовыми решениями:

Переполнение буфера
Объясните почему вылетает ошибка и помогите исправить.
Зарание спасибо

возникло переполнение…

Переполнение буфера
В программе имеется форма и записывается текстовые файлы. Когда она запускается всё работает…

Переполнение буфера
Не могу найти, где у меня ошибка, выдает переполнение буфера, подскажите пожалуйста.
Здесь…

Переполнение буфера
Подскажите, пожалуйста кто-нибудь, в чем беда… Во время выполения программы пишет, что буфер…

26

Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

23.03.2015, 18:51

2

Нужно, чтобы буфер был переполнен настолько, что перезаписал то
место в стеке, где хранится адрес возврата из функции.
Эту ситуацию очень непросто воссоздать, учитывая, что между
буфером и стеком обычно масса других страниц памяти, они могут
быть недоступны даже на чтение. А еще есть всякие ASLR, затрудняющие
«предсказания» того, в каком месте располагается стек потока.
В общем, в 99% случаев результатом переполнения буфера
будет access violation и аварийное завершение программы.



1



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

23.03.2015, 19:21

 [ТС]

3

у меня и просходит аварийное завершение, я просто думал может в исходниках какого нибуть kernel32.dll или самого exe найти его



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

23.03.2015, 21:36

4

Можно и самому найти. Регистр RSP/ESP указывает на верхушку стека,
там лежит адрес возврата. «RSP/ESP — (buffer + size)» — это на сколько байт
нужно перезаписать буфер, чтобы добраться до позиции RSP/ESP.
На современных Windows это практически всегда дохлый номер, если
только речь не идет о каком-то заранее известном приложении, когда
вы знаете, что буфер и стек всегда на известном удалении друг от друга.



1



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

24.03.2015, 15:07

 [ТС]

5

А как такое можно объяснить?)

Переполнение буфера, адрес возврата

Если добавить еще один А то будет аварийное завершение…



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

24.03.2015, 15:28

6

А что удивляет ?
Буфер лежит в стеке. Где-то рядом, тоже в стеке, лежит
адрес возврата и другие данные. Они затираются.
См. выше:

Цитата
Сообщение от Убежденный
Посмотреть сообщение

если
только речь не идет о каком-то заранее известном приложении, когда
вы знаете, что буфер и стек всегда на известном удалении друг от друга.

Такое да, можно эксплуатировать. В других случаях под большим вопросом.



1



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

24.03.2015, 15:55

 [ТС]

7

так, то есть если я правильно понял это то самое количество А, когда можно добавлять адрес возврата,

Переполнение буфера, адрес возврата

это адрес по которому находится sub esp 44h? то есть strcpy(str,»x33x18x41x00″);?
просто у меня задача эксплуатировать это прериполнение по средствам эксплоита



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

24.03.2015, 16:36

8

Лучший ответ Сообщение было отмечено rjrf как решение

Решение

Вы не туда смотрите. Адрес, по которому лежит инструкция «sub esp, 44h»,
лежит в read-only памяти (read+execute, если быть точным), ее вы никак
не перезапишете переполнением буфера, потому что будет AV.
Вам нужно поставить точку останова на входе в show_array и посмотреть,
по какому адресу в стеке лежит буфер и где в стеке лежит адрес возврата.
И уже исходя из этого писать эксплойт.



1



Модератор

Эксперт по электронике

8806 / 6589 / 894

Регистрация: 14.02.2011

Сообщений: 23,165

24.03.2015, 16:48

9

Цитата
Сообщение от Убежденный
Посмотреть сообщение

Вы не туда смотрите. Адрес, по которому лежит инструкция «sub esp, 44h»,
лежит в read-only памяти

я так понял что ему нужно так переполнить стек, чтобы отправить выполнение по своей ветке
дорогой rjrf, это уже взлом, а взлом здесь запрещен



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

24.03.2015, 17:12

10

И какой же это взлом ?
Ну у меня так один драйвер работает: находит адрес возврата в стеке и
подменяет его на свою функцию. Не вижу здесь никакого криминала.
И вообще, опасны не тех. средства, а мотивация…

А знать, как устроено переполнение буфера и его эксплуатация, полезно.
В первую очередь затем, чтобы писать устойчивый к таким атакам софт.



0



Модератор

Эксперт по электронике

8806 / 6589 / 894

Регистрация: 14.02.2011

Сообщений: 23,165

24.03.2015, 17:20

11

Цитата
Сообщение от Убежденный
Посмотреть сообщение

И какой же это взлом ?

классический, «атака через переполнения буфера»

Цитата
Сообщение от Убежденный
Посмотреть сообщение

А знать, как устроено переполнение буфера и его эксплуатация, полезно.

так это не ко мне а к правилам

Цитата
Сообщение от Убежденный
Посмотреть сообщение

находит адрес возврата в стеке и
подменяет его на свою функцию. Не вижу здесь никакого криминала.

во первых находит а не втупую забивает буфер своим адресом
во вторых как посмотреть, если программа делает что то без ведома пользователя это вирусная технология
на западе против защит которые используют вирусную технологию. выигрывали суды



0



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

29.03.2015, 03:58

 [ТС]

12

Нашел, оказалось вот в чем была проблема:почему то открыв kernel32.dll через ida я там не смог найти call esp, а открыв его через ollydbg(посмотрев связанные с этим exe длл) смог, типо потому что диначисекая библиотека?

Добавлено через 1 минуту
Еще при написании ехплоита пытался в kernel32 найти адрес winexec с помощью ida, нашел, но он почему то не подошел, пришлоь програмно искать

Добавлено через 1 минуту

Цитата
Сообщение от ValeryS
Посмотреть сообщение

классический, «атака через переполнения буфера»

так это не ко мне а к правилам
во первых находит а не втупую забивает буфер своим адресом
во вторых как посмотреть, если программа делает что то без ведома пользователя это вирусная технология
на западе против защит которые используют вирусную технологию. выигрывали суды

Думаю вы приувиличиваете, это у нас лаба такая на 3 курсе=)



0



Неэпический

17813 / 10585 / 2043

Регистрация: 27.09.2012

Сообщений: 26,625

Записей в блоге: 1

29.03.2015, 11:56

13

Почитайте книгу Эриксона «Хакинг — искусство эксплойта.». В ней всё подробно расписано. Правда там всё заточено под Ubuntu, но я когда читал сидел на винде. Будет разница в адресах, в стратегиях распределения памяти операционной системой, в устройстве стека и кучи и т.д. На первых порах это мешает, но зато думаешь самостоятельно и быстро привыкаешь.



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

29.03.2015, 11:57

14

Цитата
Сообщение от rjrf
Посмотреть сообщение

открыв kernel32.dll через ida я там не смог найти call esp

call esp — бессмысленная инструкция.
Стек лежит в неисполняемой области памяти, прыжок туда
моментально приводит к access violation.



0



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

29.03.2015, 12:00

 [ТС]

15

Ну не знаю) я в общеи в ntldll нашел jmp esp, вот с ним все норм, затираем main а дльше jmp esp и шел код, все работает



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

29.03.2015, 12:04

16

Цитата
Сообщение от rjrf
Посмотреть сообщение

в общеи в ntldll нашел jmp esp

Где ? В какой функции (и версию ntdll, пожалуйста) ?



0



0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

29.03.2015, 12:21

 [ТС]

17

Цитата
Сообщение от Убежденный
Посмотреть сообщение

Где ? В какой функции (и версию ntdll, пожалуйста) ?

Еще, после исполнения шел кода происходит аварийное завершение программы, еще приходтся переодически менять адрес jmp esp и адреса в shellcode и кстати у друга не получается так сделать типо нет доступа при вызове.
версия 6.3.9600. 17031
нашел поиском команды jmp esp
С ollydbg недавно познакомился и не знаю как функцию посмотреть



0



Убежденный

Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

29.03.2015, 12:29

18

Я вам продемонстрирую сейчас всю бессмысленность затеи с jmp esp:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <Windows.h>
 
int main()
{
    //
    // Assembly code to generate:
    //
    //      label:
    //          jmp label
    //
    byte const Opcode[] = { 0xE9, 0xFB, 0xFF, 0xFF, 0xFF };
 
    DWORD Stack;
    __asm
    {
        mov dword ptr [Stack], esp
    }
    printf("Stack pointer (ESP) = %pn", Stack);
    memcpy((void *)Stack, Opcode, sizeof (Opcode));
    __asm
    {
        jmp dword ptr [Stack]
    }
    return 0;
}

Переполнение буфера, адрес возврата

Что такое 0xC0000005, думаю, объяснять не надо ?



1



rjrf

0 / 0 / 0

Регистрация: 24.10.2013

Сообщений: 89

29.03.2015, 13:00

 [ТС]

19

Цитата
Сообщение от Убежденный
Посмотреть сообщение

Я вам продемонстрирую сейчас всю бессмысленность затеи с jmp esp:

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <Windows.h>
 
int main()
{
    //
    // Assembly code to generate:
    //
    //      label:
    //          jmp label
    //
    byte const Opcode[] = { 0xE9, 0xFB, 0xFF, 0xFF, 0xFF };
 
    DWORD Stack;
    __asm
    {
        mov dword ptr [Stack], esp
    }
    printf("Stack pointer (ESP) = %pn", Stack);
    memcpy((void *)Stack, Opcode, sizeof (Opcode));
    __asm
    {
        jmp dword ptr [Stack]
    }
    return 0;
}

Вложение 507273

Что такое 0xC0000005, думаю, объяснять не надо ?

Да, при отладке после шел кода как раз таки эта ошибка вылезает… спрпить с вами не могу, но могу предположить что это как то связано с тем что vs юзает ntldll

Добавлено через 16 минут

Цитата
Сообщение от Убежденный
Посмотреть сообщение

Я вам продемонстрирую сейчас всю бессмысленность затеи с jmp esp:

https://vk.com/im?act=browse_images&id=107465
прикрепил скрины, может понятней станет почему работает



0



Ушел с форума

Эксперт С++

16458 / 7422 / 1186

Регистрация: 02.05.2013

Сообщений: 11,617

Записей в блоге: 1

29.03.2015, 13:02

20

И зачем скрины ? Лучше опишите в деталях, как ваш эксплойт/шелл-код работает
(или как должен работать).



0



Понравилась статья? Поделить с друзьями:
  • Как составить анализ урока по физкультуре
  • Неделя как найти корень
  • Как найти полную программу компьютере
  • Как можно найти обои по фото
  • Как исправить если по ошибке внесли запись об увольнении