Сейчас на форуме: asfa, _MBK_ (+6 невидимых)

 eXeL@B —› Вопросы новичков —› Извлечение массива. Требуется помощь и советы.
Посл.ответ Сообщение

Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 14 сентября 2009 00:20 · Поправил: uinor
· Личное сообщение · #1

Прошу проконсультировать меня, т.к. немного путаюсь.

Вводное
--
Есть приложение (win32 / native / c++), знаю что где-то расположен массив байт (myArray), знаю, что он передается в качестве одного из параметров в функцию из динамической библиотеки (myFunc, присутствует в таблице импорта приложения).

Задача
--
Получить массив целиком для своих нужд.

Дополнительные условия
--
* Обойтись минимальными жертвами (без сторонних дизасмов и прочего)
* Высокая скорость работы
* Никаких сигнатур не известно, иначе использовал бы binary search
* Извлечение статическое
* Речь идет не об одном анализируемом приложении, ситуация может меняться (адрес myFunc, положение массива, его размер и прочее). Во всех случаях не меняется лишь общая схема.

Подробнее
--
Так выглядит массив в исходном коде (исключительно для примера):
Code:
  1. static const unsigned char myArray[] = {
  2.   0x0,0x0,0x0,0x6,0x76,0x73,0x6d,0x61,0x72,0x6b,  
  3. };


Так выглядит вызов функции:
Code:
  1.  00404228                           SUB_L00404228:
  2.  00404228  55                                       push  ebp
  3.  00404229  89E5                                     mov   ebp,esp
  4.  0040422B  83EC18                                   sub   esp,00000018h
  5.  0040422E  C744240C80464100                         mov   dword ptr [esp+0Ch],L00414680
  6.  00404236  C7442408A0464100                         mov   dword ptr [esp+08h],L004146A0
  7.  0040423E  C7442404E0464100                         mov   dword ptr [esp+04h],L004146E0
  8.  00404246  C7042401000000                           mov   dword ptr [esp],00000001h
  9.  0040424D  E8060A0000                               call  myFunc
  10.  00404252  B801000000                               mov   eax,00000001h
  11.  00404257  C9                                       leave
  12.  00404258  C3                                       retn


Где, адрес 00414680h указывает на расположение массива (идет последним параметром).

Мои размышления
1. Берем таблицу импорта, находим адрес myFunc
2. Находим reference на него, попадаем в импорт(?) (jmp xxx)
3. Находим reference на импорт, попадаем в подпрограмму, приведенную выше
4. Получаем адрес массива, поднимаясь выше (не знаю как грамотно организовать)
5. Переходим по адресу

Я не так хорошо знаком с PE форматом, возможно это делается проще. У меня дикая путаница в голове относительно RVA & etc, буду курить материал, очень буду рад, если кто-то даст алгоритм максимально подробно. Как получить размер массива? Как грамотнее подняться до адреса массива в подпрограмме (можно дико захардкодить, но хотелось бы гибкости, ситуации могут быть разные)?



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 14 сентября 2009 09:38
· Личное сообщение · #2

Пока не очень понятно - задача слишком общая, а уж по методам - неясно что вы умеете и какие инструменты будете использовать, значит непонятно как делать. Исходя из слов "извлечение статическое" - самое быстрое в освоении : остановиться отладчиком на вызове функции и сдампить массив. А размышления ваши наводят на мысль, что постановка задачи глобальная : типа написать универсальный "статический" выдиратель(но он будет динамический), но тогда непонятно условие "Обойтись минимальными жертвами (без сторонних дизасмов и прочего)". Ваши размышления правильны, но адрес массива в общем случае без отладчика не получить - в вашем примере адрес просто "зашит" в программу, но так бывает не всегда.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 14 сентября 2009 12:14
· Личное сообщение · #3

tundra37, не настолько универсально.

Адрес всегда зашит, более того, приведенный код подпрограммы SUB_L00404228 всегда (99,99%) именно такой.



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 14 сентября 2009 12:39
· Личное сообщение · #4

А чем тогда способ с отладчиком не устраивает. Если нужно самому сделать - освой написание лоадера.
Можно сделать патч и сбрасывать массив в файл - тоже вариант.
Или у тебя задача другая : в разных вариантах проги найти вызов этой подпрограммы, потом найти адрес самого массива, а читать ты его уже умеешь?



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 14 сентября 2009 13:04
· Личное сообщение · #5

tundra37: лоадер не вариант, должна быть статика - на вход exe, без запуска, на выходе - массив.

Т.е. найти физический оффсет массива, его размер и просто выдернуть.



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 14 сентября 2009 14:25 · Поправил: tundra37
· Личное сообщение · #6

Тогда по порядку, раз не хочешь самое простое с отладчиком:
1) Размер массива можно определить только зная конкретику про MyFunc. Он может быть в параметрах, если повезет, а может быть где угодно
2) Таблица импорта. Это легко сказать - все зависит от того на чем компилили. Импорт функции найти легко, но там может быть просто jump и ..... найти можно, но по разному.
3) Дальше все уже проще, если конечно слова про 99% верны - смотрим СООТВЕТСТВУЮЩИЙ адрес в exe и в данном случае по-моему там всего надо 400000 добавить. И из СООТВЕТСТВУЮЩЕГО адреса(используем таблицу секций) берем наш массив. Про длину я уже писал. Это конечно, если содержимое массива забито в exe, а не заполняется в процессе работы. А КТО СКАЗАЛ, что будет легко.
Вот отладчик это все сделает без напряга, кроме конечно обнаружения call на автомате. А если взять нормальную Ольгу можно и защищенные проги "драть".
Если все статично на 99%, то проще найти сигнатуру соседей call MyFunc или даже лучше самого массива - программа простая будет до ужаса.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 14 сентября 2009 18:33
· Личное сообщение · #7

tundra37 вы видели дефиницию массива?
В параметрах размер не передается. Чтоб его размерность где-либо хранилась тоже не видел.

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

Содержимое массива заполнено еще в стадии разработки. Оно не меняется в процессе работы.

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

Относительно сигнатуры. Массив вычислить никак не получится. Контент непредсказуем. Поясните что имели в виду относительно соседей myFunc? Допустим, по маске я нашел тело подпрограммы с вызовом myFunc, вычленил адрес массива. Как его преобразовать в оффсет файла по которому лежит массив?



Ранг: 19.9 (новичок)
Активность: 0.020
Статус: Участник

Создано: 14 сентября 2009 20:58
· Личное сообщение · #8

uinor пишет:
Как его преобразовать в оффсет файла по которому лежит массив?


а зачем его преобразовывать?можно же прямо в ольге сохранить кусок данных в файл. и вполне возможно что размер массива может в первом двойном слове содержаться если это структура. помнится вроде про рва в туторах ICZELION'a есть



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 15 сентября 2009 08:37 · Поправил: uinor
· Личное сообщение · #9

Стоп, речь идет о статическом анализе + полная автоматика. Вытащить массив действуя руками в дизассемблере - тривиально. Я прошу помощи именно в плане автоматизации этой работы.

Давайте рассмотрим вариант с binary search, хотя судя по экспериментам проблематично будет сделать сигнатуру для поиска вызова myFunc и получения адреса массива. Есть предложения по сигнатуре?

Далее, что делать с адресом массива (00414680h)? Это RVA? Следовательно остается просто RVA2Offset и вытащить массив? Размер действительно лежит в первом двойном слове, благодарю.



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 15 сентября 2009 10:52 · Поправил: tundra37
· Личное сообщение · #10

uinor С такими знаниями и подходами вы далеко пойдете. В 8:57 ваш "напарник"(а может альтерэго) написал на wasm.ru, что длина лежит по указателю. А если ваш пример взят из "жизни", то там лежит строка (сейчас не вспомню как этот тип зовется): в первом слове длина и далее сам ваш массив.
Можно найти вообще все такие строки и даже исходники найти для этого, но это не метод сигнатур.
Использования лоадера никак не означает динамики : после загрузки вашего exe не обязательно его выполнять, а можно просто(гораздо проще чем из файла-exe) найти вызов функции и ее параметры и массив извлечь прямо по адресу, а не пересчитывать "страшные" RVA.
А вы пытаетесь повторить часть загрузчика или часть дизассемблера - это гораздо сложнее. Если же ваша цель именно научиться, то тогда увы - одними советами тут точно не обойтись, даже всех профи с краклаба и васма.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 15 сентября 2009 11:10
· Личное сообщение · #11

tundra37, позвольте. Во-первых, первым dword'ом; во-вторых оно там лежит не случайно и будет лежать всегда. Увидел это достаточно случайно, чему рад. Это массив, я в первом же посте приводил оригинальный код, который в последствие компилируется. "Строка" лежит лишь в моем примере (специально не стал преобразовывать char'ами, а выдал hex'е, чтоб это не сбивало никого с толку.

Спасибо относительно разъяснений по поводу лоадера. Т.е. запуска при этом не происходит? Таким же принципом судя по всему поступает PE Explorer?

Как происходит загрузка исполняемого файла и дальнейшая работа с ним (если ориентируемся на форм-фактор loader'a)? Подскажите пару ключевых слов, постараюсь разобраться. Интересно было бы увидеть примеры, конечно.



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 15 сентября 2009 12:20
· Личное сообщение · #12

А вот чтобы не было случайностей достаточно было в отладчике пробежать обработчик и вы бы увидели длину
Теперь про лоадер - какие-то исходики были здесь. Где - фиг знает. Есть лоадеры без исходников, но с возможностью "программирования". И о вкусах конечно не спорят, но ваше желание сделать автомат плохо подкреплено знаниями.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 15 сентября 2009 12:45
· Личное сообщение · #13

tundra37, я пробегал в отладчике и не только, вы не поверите. Этот массив кодогенерируется, там просто некий контент (я специально вставил туда метку, чтоб быстро найти его в коде). Чтоб первым двордом лежит длина - нетривиально, но можно заметить

Мне достаточно понять принцип. tundra37, я специализируюсь на форматах данных (DRE) + разработка ПО непосредственно, к сожалению, многие техники RE мне недоступны.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 15 сентября 2009 13:38
· Личное сообщение · #14

tundra37, вы под лоадером имели в виду mapping с флагом SEC_IMAGE? это меняет дело, понял ситуацию. просто запросы по loader'у & etc выводили на совершенно другие моменты.

Да, действительно это удобно (использование механизмов самой ОС в плане проецирования с целью дальнейшего "удобного" использования).



Ранг: 310.8 (мудрец), 29thx
Активность: 0.430
Статус: Участник

Создано: 16 сентября 2009 10:11 · Поправил: tundra37
· Личное сообщение · #15

Это ты уже полез вдебри. Исходники никто не торопится дать, а искать лень.
Давай попытаемся превратить твои размышления + мои в некий план действий.
1) Исходник лоадера собственно и не нужен. exe загружает в память какая-то системная функция и наверняка она где-то на сайте описана. Другой вариант: запустить exe на выполнение, предварительно пропатчив точку входа "бесконечным jump-ом".
2) Далее придется самому реализовать упрощенный дизассемблер : искать команды call, отбраковывать "ложные" команды call, по адресу забитому в call добираться до таблицы импорта и находить нашу функцию.
3) Зная нужную команду call поднимаемся немного выше по адресам и выуживаем адрес массива. Если других вариаций вызова кроме 2-х указанных на васм нет - это просто. Если нет: обработав все N приложений использующих функу из DLL получим K поиска адреса массива.
4) Извлекаем по адресу наш массив и все - задача решена.

Теперь оценим трудности для вас в реализации:
1) находим инфу и относительно легко реализуем. Такой функционал писали многие неленивые новички.
2) реализация этого пункта потребует "помощи зала" или кропотливого изучения.
3) Это уже детский сад после 1 и 2, ну в варианте с запуском exe немного мук с чтением памяти процесса.

Есть другой вариант, который вы изначально пробиваете : работать с exe-файлом. Все тоже самое, только добавляются сложности проецирования данных найденных в импорте и в секции команд на exe-файл. Это еще сложнее получается.

Если отказаться от автомата, то есть легкий путь, который вам и предлагали :
1) Грузим в olly ваше приложение и выбираем в контекстном меню поиск по именам внешних функций.
Найдя ее открываем пункт "поиск вызовов"(пишу по памяти, готов все нарисовать если будет подопытный кролик). Далее открываем найденный вызов в дизассемблере (ой вы не хотели...) , поднимаемся на две команды, открываем массив и легким кликом копируем куда надо.

olly имеет язык скриптов и можно это все автоматизировать. Сама olly небольшая и можно таскать на место поиска совершенно свободно.

Есть другой вариант - использовать дизассемблер, но IDA относительно тяжеловесна и по времени это дольше. Скрипты там тоже есть, но по сравнению с olly по-моему тяжелее в освоении. Это я к тому, что скрипты olly я могу вам написать, а для IDA не гарантирую. Если есть быстрый дизассемблер, который нормально определяет вызов вашей функции, то можно написать простенький анализатор его выдачи для выдирания вашего массива - чем не автомат.

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



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 16 сентября 2009 17:14 · Поправил: _ruzmaz_
· Личное сообщение · #16

tundra37 пишет:
...искать команды call, отбраковывать "ложные" команды call, по адресу забитому в call добираться до таблицы импорта и находить нашу функцию

А можт проще myFunc просплайсить?



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 16 сентября 2009 18:44 · Поправил: uinor
· Личное сообщение · #17

_ruzmaz_, нужна статика, тут сплайс мимо.

Вроде ситуация решена, остался последний момент с вычленением адреса массива из параметра. Подключил мелкий и быстрый дизасм длин, осталось теперь только определиться со схемой работы. Вот, кстати, лог с утильки (D2007, грязновато, но все же):
Code:
  1. ...
  2. Name RVA = 0291D754, Library Name = myLibrary.dll
  3.   Function = Func1, Hint = 0000000B, RVA = 0291B514
  4.   Function = myFunc, Hint = 00000034, RVA = 0291B518
  5. [!] Founded myFunc @ [0041B518]
  6. Section Name = .text, Section VA = 00001000, Section Size = 00012C00
  7. [!] Jumper founded @ [00404C58]
  8. [!] JumperCall founded @ [0040424D]
  9. [!] SubRange [00404228 - 00404258]
  10. [C] 00404228: 55
  11. [C] 00404229: 89E5
  12. [C] 0040422B: 83EC18
  13. [C] 0040422E: C744240C80464100
  14. [C] 00404236: C7442408A0464100
  15. [C] 0040423E: C7442404E0464100
  16. [C] 00404246: C7042401000000
  17. [C] 0040424D: E8060A0000
  18. [C] 00404252: B801000000
  19. [C] 00404257: C9
  20. [C] 00404258: C3
  21. Section Name = .data, Section VA = 00014000, Section Size = 00001200
  22. Section Name = .bss, Section VA = 00016000, Section Size = 00000000
  23. Section Name = .idata, Section VA = 0001B000, Section Size = 00002A00
  24. Section Name = .rsrc, Section VA = 0001E000, Section Size = 00006C00
  25. Section Name = .stub1, Section VA = 00025000, Section Size = 00019200
  26. Section Name = .stub2, Section VA = 0003F000, Section Size = 00214A00
  27. ...
(... = skipped)



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 16 сентября 2009 19:25
· Личное сообщение · #18

Ну дык C74424 0C 80464100 => mov dword ptr [esp+0C], 00414680
В чем проблема?



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 16 сентября 2009 19:37
· Личное сообщение · #19

_ruzmaz_, я понимаю это отлично ) Речь идет о том, что там может быть проталкивание параметров не только через регистры, но и через стэк (via push), поэтому варианта два, либо накладывать какую-то гибкую маску, либо анализировать детально что-куда-как (предусматривая некую библиотеку вариантов).

Может мысли будут по оптимальной схеме какие?



Ранг: 114.8 (ветеран), 41thx
Активность: 0.10
Статус: Участник

Создано: 16 сентября 2009 20:02 · Поправил: _ruzmaz_
· Личное сообщение · #20

Либо предполагаем, что прога не запрочена, мусора нет, компилер без финтов, тогда push imm32 и mov dword ptr [esp+imm8],imm32 вполне хватит. Что-либо отличное от этого в следующих версиях добавишь ))
Либо не предполагаем, тогда
tundra37 пишет:
это работа на год




Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 16 сентября 2009 20:45
· Личное сообщение · #21

_ruzmaz_, согласен с вами. Реализовал два варианта, вроде фурычит.

Всем спасибо.



Ранг: 18.6 (новичок)
Активность: 0.010
Статус: Участник

Создано: 16 сентября 2009 23:03
· Личное сообщение · #22

Все, проблема решена окончательно. Закрываю тему, спасибо за помощь.


 eXeL@B —› Вопросы новичков —› Извлечение массива. Требуется помощь и советы.
Эта тема закрыта. Ответы больше не принимаются.
   Для печати Для печати