Я хотел бы собрать небольшой 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
, Это просто интересный способ узнать о кодах сборки / шеллкодах, и к нему не следует относиться слишком серьезно 🙂
Я нашел три ошибки. 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 } );
Других решений пока нет …