2012-05-04 4 views
7

Я работаю над проектом (C++), для которого требует полностью динамически распределенных функций, что означает malloc/new и mprotect, а затем вручную модифицирует буфер код сборки. Из-за этого я точно задался вопросом, что требуется в этом «моем буфере», так как это будет реплицировать любой другой функции _cdecl. Например:Использование C++ с сборкой для выделения и создания новых функций во время выполнения

int ImAcDeclFunc(int a, int b) 
{ 
    return a + b; 
} 

Если я хотел бы буквально создать дубликат этой функции, но вполне динамично, на что это потребует (и помните, что это C++ с инлайн сборки)? Для начала, я думаю, я должен был бы сделать что-то вроде этого (или похожий раствора):

// My main.... 
byte * ImAcDeclFunc = new byte[memory]; 
mprotect(Align(ImAcDeclFunc), pageSize, PROT_EXEC | PROT_READ | PROT_WRITE); 

После этого я должен был бы узнать код сборки для ImAcDeclFunc(int a, int b);. Теперь я все еще паршиво на сборке, так как эта функция будет в AT & T синтаксисе? Вот моя смелая попытка:

push %ebp 
movl %%ebp, %%esp 
movl 8(%ebp), %%eax 
movl 12(%ebp), %%edx 
addl edx, eax 
pop ebp 
ret 

Теперь, если этот код правильно (я очень сомневаюсь, пожалуйста, поправьте меня) мне нужно только, чтобы найти значение этого кодекса в шестнадцатеричной (например, «ПСМ» является 0xE9 и ' inc 'равно 0xFE) и использовать эти значения непосредственно в C++? Если я продолжу свой предыдущий C++ код:

*ImAcDeclFunc = 'hex value for push'; // This is 'push' from the first line 
*(uint)(ImAcDeclFunc + 1) = 'address to push'; // This is %ebp from the first line 
*(ImAcDeclFunc + 5) = 'hex value for movl' // This is movl from the second line 
// and so on... 

После того как я сделал это для всего кода/буфера, который был бы достаточно для полностью динамической функции _cdecl (то есть я мог просто бросить его в указатель на функцию и сделать int result = ((int (*)(int, int))ImAcDeclFunc)(firstArg, secondArg)?). И я не заинтересован в использовании наддува :: функции или что-то похож, я нужно функцию, чтобы быть полностью динамичны, поэтому мой интерес :)

Примечание: Этот вопрос является продолжением на мой previous one, но с гораздо больше.

+0

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

+0

@ н.м. Да, это был всего лишь пример для меня, чтобы понять и легко представить все для вас. Мне легко понадобится около двадцати из них. Если вы прочтете мою ссылку (на мой другой вопрос), вы точно поймете, почему :) –

+0

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

ответ

5

Если вы отнесете lala.c:

int ImAcDeclFunc(int a, int b) 
{ 
    return a + b; 
} 

int main(void) 
{ 
    return 0; 
} 

Вы можете скомпилировать его с gcc -Wall lala.c -o lala. Затем вы можете разобрать исполняемый файл с помощью objdump -Dslx lala >> lala.txt. Вы найдете ImAcDeclFunc собран на:

00000000004004c4 <ImAcDeclFunc>: 
ImAcDeclFunc(): 
    4004c4: 55      push %rbp 
    4004c5: 48 89 e5    mov %rsp,%rbp 
    4004c8: 89 7d fc    mov %edi,-0x4(%rbp) 
    4004cb: 89 75 f8    mov %esi,-0x8(%rbp) 
    4004ce: 8b 45 f8    mov -0x8(%rbp),%eax 
    4004d1: 8b 55 fc    mov -0x4(%rbp),%edx 
    4004d4: 8d 04 02    lea (%rdx,%rax,1),%eax 
    4004d7: c9      leaveq 
    4004d8: c3      retq 

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

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

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


В вашем простом случае выше, вы можете сделать это:

#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <malloc.h> 
#include <sys/mman.h> 
#include <unistd.h> 

int main(void) 
{ 
    char func[] = {0x55, 0x48, 0x89, 0xe5, 0x89, 0x7d, 0xfc, 
    0x89, 0x75, 0xf8, 0x8b, 0x45, 0xf8, 
    0x8b, 0x55, 0xfc, 0x8d, 0x04, 0x02, 
    0xc9, 0xc3}; 

    int (* func_copy)(int,int) = mmap(NULL, sizeof(func), 
     PROT_WRITE | PROT_READ | PROT_EXEC, 
     MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 

    memcpy(func_copy, func, sizeof(func)); 
    printf("1 + 2 = %d\n", func_copy(1,2)); 

    munmap(func_copy, sizeof(func)); 
    return EXIT_SUCCESS; 
} 

Это прекрасно работает на x86-64. Он печатает:

1 + 2 = 3 
+1

Буду признателен, если бы вы могли предоставить мне рабочий пример. Для меня это было бы солидным золотом! Об относительных звонках, из того, что я знаю, это просто так; 'targetAddress - currentAddress -/+ любые смещения'? Что касается «обработки вызовов библиотеки», это будет любая проблема, если я буду только вызывать функции-члены? Поскольку я использую GCC, это _exactly_, как вызов cdecl, но с дополнительным указателем («этот» указатель). Или это создаст проблемы, если я тогда вызову библиотечные функции, возможно, _stdcall из функции-члена; i.e dynamic_func-> member_func-> library_func? –

+0

О, между прочим, не mprotect fail, потому что вы не выровняете память? Я попробую себя :) –

+0

@ElliottDarfink: Да, я просто заметил выравнивание. После изменения это по-прежнему претерпевает изменения, поэтому нужно будет сыграть еще немного. Да, относительные смещения работают в основном от принятия дельта targetAddress и currentAddress. –

1

Возможно, вы захотите проверить молнию GNU: http://www.gnu.org/software/lightning/. Это может помочь вам в том, что вы пытаетесь сделать.

+1

Да, я читал об этом, но я не совсем понял, как это работает. Не говоря уже о том, насколько тонка документация. Вы не знаете, есть ли какие-либо ресурсы документации, которые могли бы предложить руку помощи? Кажется, я хочу, я просто не знаю, как. –

1

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

Если я хотел бы буквально создать дубликат этой функции, но полностью динамически, что бы требовало (и помните, что это C++ с встроенной сборкой)?

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

+0

«Это потребует человека с дизассемблером». Это неверно. Существуют автоматические инструменты, которые выполняют статический анализ, что противоречит этому (например, Dyninst). –

+0

@MikeKwan: ​​Нет никакого противоречия, и я прав. Хотя есть автоматизированные инструменты, они не на 100% надежны, могут потребовать помощи человека, и они часто выводят вспомогательные данные из отладочной информации. SOmething like IDA pro занимает несколько минут, чтобы разделить файл на подпрограммы и все еще может пропустить несколько из них. Это станет еще более забавным, если вы попытаетесь проанализировать программное обеспечение, которое было запутано, чтобы запутать дизассемблер. – SigTerm

+0

И вы верите, что человек с дизассемблером может сделать лучше в таких случаях? В основном статический анализ падает с непрямым ветвлением. В этих случаях человеческий анализ не намного лучше. В вашем ответе также больше неточностей.Размер функции может быть определен (по крайней мере на ELF) с помощью информации символа. –

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

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