2010-01-15 1 views
4

Я сбрасываю свою RAM (ее часть - только сегмент кода), чтобы найти, где находится функция C. У меня нет файла карты, и я не знаю, что именно делают процедуры загрузки/инициализации.C - Как создать шаблон в сегменте кода, чтобы распознать его в дампе памяти?

Загрузите свою программу в ОЗУ, а затем, если я сброшу ОЗУ, очень сложно найти, где именно находится функция. Я хотел бы использовать разные сборки шаблонов в источнике C, чтобы распознавать их в дампе памяти.

Я судимый начать все функции с различным первым переменным, содержащим именем функции, как:

char this_function_name[]="main"; 

, но он не работает, потому что эта строка будет помещена в сегменте данных.

У меня есть простой 16-разрядный RISC-процессор и экспериментальный собственный компилятор (без GCC или каких-либо известных). Система имеет 16 Мб ОЗУ, совместно используемую другими приложениями (загрузчик, загрузчик). Почти невозможно найти определенную последовательность N NOP или smth. как 0xABCD. Я хотел бы найти все функции в ОЗУ, поэтому мне нужны уникальные идентификаторы функций, видимых в RAM-дампе.

Что было бы лучшим образцом для сегмента кода?

+0

Какой компилятор/ОС? –

+0

@ Рихард: встроенная платформа, экспериментальный процессор и компилятор – psihodelia

+1

Ugh. Получите файл карты, как если бы ваша жизнь зависела от него. –

ответ

7

Если бы это был я, я бы использовал таблицу символов, например. «nm a.out | grep main». Получите реальный адрес любой функции, которую вы хотите.

Если у вас действительно нет таблицы символов, сделайте свой собственный.

struct tab { 
    void *addr; 
    char name[100]; // For ease of searching, use an array. 
} symtab[] = { 
    { (void*)main, "main" }, 
    { (void*)otherfunc, "otherfunc" }, 
}; 

Ищите имя, и адрес будет немедленно прерван. Перейти к адресу. ;-)

+0

Нет, у меня нет ни тени, ни других полезные инструменты, потому что это очень необычный компилятор и процессор. – psihodelia

+0

Хорошая идея, эта таблица символов «ваша». ++ к вам –

+0

Это не работает, потому что я могу сбросить только сегмент кода. Бинарный файл также находится в проприетарном формате, я не могу его прочитать. – psihodelia

1

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

I.e. вот вид разборки простой функции C с Visual C++:

void foo(void) 
{ 
00411380 push  ebp 
00411381 mov   ebp,esp 
00411383 sub   esp,0CCh 
00411389 push  ebx 
0041138A push  esi 
0041138B push  edi 
0041138C lea   edi,[ebp-0CCh] 
00411392 mov   ecx,33h 
00411397 mov   eax,0CCCCCCCCh 
0041139C rep stos dword ptr es:[edi] 
    unsigned id = 0xDEADBEEF; 
0041139E mov   dword ptr [id],0DEADBEEFh 

Вы можете увидеть 0xDEADBEEF, что делает его в источник функции. Обратите внимание, что то, что вы на самом деле видите в исполняемом файле, зависит от контентоспособности CPU (tx. Richard).

Это пример x86. Но RISC-процессоры (MIPS и т. Д.) Имеют инструкции, перемещающиеся непосредственно в регистры - эти непосредственные пользователи могут также иметь специальные распознаваемые значения (хотя только для 16-бит для MIPS, IIRC).


Psihodelia - становится все труднее и труднее поймать ваше намерение. Это всего лишь одна функция, которую вы хотите найти? Тогда вы не можете просто разместить 5 NOPs один за другим и искать их? Вы управляете компилятором/ассемблером/компоновщиком/загрузчиком? Какие инструменты в вашем распоряжении?

+0

Нет, в моем случае это не работает. У меня нет процессора x86, но простой RISC-процессор. – psihodelia

+0

Убедитесь, что вы ищете EFBEADDE на x86. ;-) Кроме того, не включайте слишком много оптимизаций. –

+0

@psihodelia: даже процессоры RISC имеют инструкции по размещению сразу в регистрах. –

3

Если ваш компилятор имеет встроенный asm, вы можете использовать его для создания шаблона. Напишите некоторые инструкции NOP, которые вы можете легко распознать по опкодами в дамп памяти:

MOV r0,r0 
MOV r0,r0 
MOV r0,r0 
MOV r0,r0 
+1

Чтобы быть в безопасности, поставьте безусловную инструкцию перехода в начале блока сборки, которая пропустит все это. Таким образом, вы можете поместить все, что захотите, внутри него (обычно я устанавливаю коды операций, которые при сбросе заканчиваются как значения ascii, которые указывают что-то), не беспокоясь об изменении выполнения программы. О, и в любое время, когда вы делаете что-либо подобное, убедитесь, что вы отключили все оптимизации компилятора (хотя некоторые компиляторы автоматически отключают их для функций с встроенной сборкой). – bta

+0

Спасибо за трюк с безусловным прыжком! О встроенной сборке: я думаю, что она используется для оптимизации вручную и никогда не должна оптимизироваться компиляторами. –

1

Как уже отмечалось, это:

char this_function_name[]="main"; 

...закончит установку указателя в вашем стеке на сегмент данных, содержащий строку. Тем не менее, это:

char this_function_name[]= { 'm', 'a', 'i', 'n' }; 

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

Надеется, что это помогает

+0

Нет, честно говоря, это относится к сегменту данных в моем случае. – psihodelia

+0

@psihodelia Wow ... Я пробовал на двух разных платформах, как с GCC, так и в обоих случаях. Однако я убедился, что оптимизация не включена. Я не уверен, будет ли GCC оптимизировать такую ​​конструкцию. Вы строите без каких-либо оптимизаций? – figurassa

1

Как насчет совершенно другой подход к вашим реальным проблемам, которая заключается в поиске конкретного блока кода: Использование дифф.

Скомпилируйте код один раз с включенной функцией и один раз с ним закомментируйте. Произведите отвалы RAM того и другого. Затем разделите две дампы, чтобы увидеть, что изменилось - и это будет новый блок кода. (Возможно, вам придется выполнить некоторую обработку дампов для удаления адресов памяти, чтобы получить чистый diff, но порядок инструкций должен быть одинаковым в любом случае.)

1

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

void* fnaddr(char* fname, void* addr) 
{ 
    printf("%s\t0x%p\n", fname, addr) ; 
    return addr ; 
} 


void test(void) 
{ 
    static void* fnaddr_dummy = fnaddr(__FUNCTION__, test) ; 
} 

int main (int argc, const char * argv[]) 
{ 
    static void* fnaddr_dummy = fnaddr(__FUNCTION__, main) ; 
    test() ; 
    test() ; 
} 

Делая fnaddr_dummy статичны, дамп выполняется один раз за функции. Очевидно, вам нужно будет адаптировать fnaddr() для поддержки любого вывода или ведения журнала, которые у вас есть в вашей системе. К сожалению, если система выполняет ленивую инициализацию, вы получите только адреса функций, которые на самом деле вызываются (что может быть достаточно хорошим).

0

Вы можете начать каждую функцию с вызовом той же фиктивной функцией, как:

недействительного identifyFunction (без знака идентификатор ИНТА) { }

Каждые из функций будет вызывать identifyFunction-функцию с другой параметр (1, 2, 3, ...). Это не даст вам волшебный файл карт, но когда вы проверяете дамп кода, вы должны быстро узнать, где функция identFunction, потому что будет много переходов на этот адрес. Затем сканируйте эти прыжки и проверьте перед прыжком, чтобы увидеть, какой параметр передан. Затем вы можете создать свой собственный файл. С некоторыми сценариями это должно быть довольно автоматическим.

 Смежные вопросы

  • Нет связанных вопросов^_^