Вызов функций внутри шеллкода

Я хотел бы скомпилировать небольшие double(...) функции как шеллкод x64. У меня уже есть работающая программа для создания закодированной сборки для простых математических операций, таких как a + b / a, где a и b — это double параметры функции.

Сгенерированный код загружается в исполняемый mmap буфер и может быть вызван позже.

У меня следующая проблема: я хотел бы вызвать math.h функции, такие как sin, exp и т. д. внутри моего сгенерированного шеллкода. Поскольку все коды операций call так или иначе используют 32-битные адреса или относительные переходы, я не могу легко использовать эти инструкции. Поэтому я попытался реализовать свою собственную call-инструкцию следующим образом:

lea rax, [rip+0x0] ;load instruction pointer into rax
add rax, <offset-to-return-to>
push rax

moveabs rax, <adress-of-function> ;load the function pointer to rax
jmp rax

Это мой код для создания этих инструкций:

//lea rax, [rip+0x0]
insertAll(code,std::list<uint8_t>{ 0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00 });
//add rax, <offset-to-return-to>
insertAll(code,std::list<uint8_t>{ 0x48, 0x83, 0xC0, <offset-to-return-to>});

//push rax
code.push_back(0x50);

//moveabs rax, address-of-function
uint8_t reg = 0; //rax
uint8_t rexWPrefix = 0x48 + (reg > 8);
uint8_t opCode = 0xB8 + (reg % 8);
insertAll(code,std::list<uint8_t>{ rexWPrefix, opCode });
code.insert(code.end(),reinterpret_cast<uint8_t*>(&fabs),reinterpret_cast<uint8_t*>(&fabs) + 8);

//jmp rax
insertAll(code,std::list<uint8_t>{ 0xFF, 0xE0 });

К сожалению, вызов функций таким способом не работает, программа вылетает с ошибкой SIGSEGV. Что-то не так с моим ассемблерным кодом или кодировкой инструкции? Какое значение должно иметь <offset-to-return-to>, чтобы функция вернулась в правильное положение?

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


person tly    schedule 01.01.2017    source источник
comment
На первый взгляд имеет смысл, используйте отладчик, чтобы увидеть, где и почему он дает сбой. PS: вы, конечно, можете объединить add rax в lea. Убедитесь, что вы используете правильное смещение.   -  person Jester    schedule 01.01.2017
comment
Что такое шеллкод x64?? А C и C++ - разные языки. Не спамьте тегами.   -  person too honest for this site    schedule 01.01.2017
comment
@Olaf Олаф: я хотел сказать, что использую инструкции x86-64 для кода. Это может быть важно для инструкции jmp, поскольку размер адреса составляет 64 бита.   -  person tly    schedule 01.01.2017


Ответы (1)


Я нашел три ошибки. Инструкция jmp не является абсолютной, а фактически RIP-относительной. Я использовал push + ret в качестве альтернативы, потому что ret переходит на абсолютный адрес, лежащий в стеке.

Кроме того, я не знал, что вызывающая сторона обязана резервировать 4*8 байт теневого пространства в стеке. Подробности можно найти здесь.

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

code.insert(code.end(),reinterpret_cast<uint8_t*>(&fabs),reinterpret_cast<uint8_t*>(&fabs) + 8);

Это готовый рабочий код:

//add rsp,0x20  --> shadow space of 4*8 bytes
insertAll(code,std::list<uint8_t>{ 0x48, 0x83, 0xC4, 0x20 } );
//lea rax, [rip+0x0]
insertAll(code,std::list<uint8_t>{ 0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00 });
//add rax, 18
insertAll(code,std::list<uint8_t>{ 0x48, 0x83, 0xC0, 18 });
//push rax
code.push_back(0x50);

//moveabs rax, address-of-function
uint8_t reg = 0; //rax
uint8_t rexWPrefix = 0x48 + (reg > 8);
uint8_t opCode = 0xB8 + (reg % 8);
insertAll(code,std::list<uint8_t>{ rexWPrefix, opCode });
void* address = reinterpret_cast<void*>(&my_abs);
code.insert(code.end(),reinterpret_cast<uint8_t*>(&address),reinterpret_cast<uint8_t*>(&address) + sizeof(address));

//push rax
code.push_back(0x50);
//retq
code.push_back(0xC3);
//sub rsp,0x20  --> shadow space of 4*8 bytes
insertAll(code,std::list<uint8_t>{ 0x48, 0x83, 0xEC, 0x20 } );
person tly    schedule 02.01.2017