Как создать шаблон в сегменте кода, чтобы распознать его в дамп памяти? - PullRequest
4 голосов
/ 15 января 2010

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

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

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

char this_function_name[]="main";

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

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

Каким будет лучший шаблон для сегмента кода?

Ответы [ 7 ]

7 голосов
/ 15 января 2010

Если бы это был я, я бы использовал таблицу символов, например "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" },
};

Поиск по имени, и адрес будет сразу предшествовать ему. Перейти к адресу. ; -)

3 голосов
/ 15 января 2010

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

MOV r0,r0
MOV r0,r0
MOV r0,r0
MOV r0,r0
1 голос
/ 16 января 2010

Почему бы не заставить каждую функцию создавать свой собственный адрес?Примерно так:

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 () для поддержки любых выходных данных или средств ведения журналов, которые у вас есть в вашей системе.К сожалению, если система выполняет отложенную инициализацию, вы получите только адреса тех функций, которые действительно вызваны (что может быть достаточно).

1 голос
/ 16 января 2010

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

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

1 голос
/ 15 января 2010

Как вы заметили, это:

char this_function_name[]="main";

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

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

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

Надеюсь, это поможет

1 голос
/ 15 января 2010

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

т.е. вот представление разборки простой функции 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 превращается в источник функции. Обратите внимание, что то, что вы на самом деле видите в исполняемом файле, зависит от порядкового номера процессора (tx. Richard).

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


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

0 голосов
/ 17 января 2010

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

void identifFunction (беззнаковый идентификатор int) { }

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

...