Расспаковка Skype средствами динамической инструментализации бинарного кода. Часть 2.
GDB Server
На наш взгляд одна из лучших возможностей поддерживаемых Intel PIN Toolkit является поддержка опции "-appdebug". Этот параметр указывает нашему фрэймворку подключиться к серверу GDB для удаленной отладки приложения. Мы можем использовать эту возможность для удаленной отладки в дизассемблере IDA с помощью дистанционного пульта управления предоставляемого отладчиком GDB. Единственной проблемой(по идее это даже не проблема, просто доставляет определенные неудобства в работе) является то, что мы не можем указать целевой порт в PIN который мы будем слушать, т.к он при инициализации выбирается случайным образом. При этом нам приходится каждый раз заменять его в параметрах отлаживаемого процесса в дебагере.
$ pin -appdebug -t source/tools/ManualExamples/obj-ia32/inscount0.so -- `which skype`
Application stopped until continued from debugger.
Start GDB, then issue this command at the (gdb) prompt:
target remote :31337
Простейший "write and exec" расспаковщик.
Давайте вернемся к основной цели нашей статьи которая заключается в написании расспаковщика для Skype средствами PIN Toolkit. Все что нам нужно будет делать это проверять, есть ли у нас в основном теле бинарного кода какие либо инструкции которые модифицируют какой либо сегмент нашего приложения(например, если инструкция пишет в секцию кода .text), сохраняют его и если приложение переходит к выполнению каких либо модифицированных секций, мы выставляем точки останова для того чтобы сообщить вовремя нашему отладчику процесс который кажется возможно расспакован. Впринципе это идея не нова и более того она очень проста и используется в большинстве простых да и сложных упаковщиках. При разработке Proof of Concept средствами фрэймворка PIN нам надо определиться с алгоритмом последовательности решения задачи, основная из которых заключается в установке параметров инструментализации кода в функции main() :
int main(int argc, char *argv[])
{
// Инициализации библиотеки PIN. Принимаем аргументы , если не задано
// по умолчанию вызывается с ключом -h(elp) которая в свою очередь вызывает// справку по аргументам
if( PIN_Init(argc,argv) )
return Usage();// Регистроваяя функция вызывается при трассировке приложения
TRACE_AddInstrumentFunction(trace_cb, 0);
// Регистровая функция вызывается при старте приложения
PIN_AddApplicationStartFunction(app_start_cb, 0);
// Регистровая функция вызывается при выходе из приложения
PIN_AddFiniFunction(fini_cb, 0);
// Стартуем программу безвозвратно
PIN_StartProgram();
return 0;
}
// Смотрим каждый след. простой блок при трассировке for ( BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl) ) {
// Смотрим каждую след. инструкцию в блоке for( INS ins = BBL_InsHead(bbl); INS_Valid(ins); ins=INS_Next(ins) ) { ADDRINT ea = INS_Address(ins); if ( !valid_ea(ea) ) continue;
// если адресс был уже перезаписан и выполнен , скорее всего наш код уже расспакован if ( was_writen(ea) ) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)check_unpacked_cb, IARG_INST_PTR, IARG_CONST_CONTEXT, IARG_THREAD_ID, IARG_END); }
UINT32 mem_operands = INS_MemoryOperandCount(ins);
// Перебираем каждый операнд инструкции в памяти for ( UINT32 mem_op = 0; mem_op < mem_operands; mem_op++ ) { if ( INS_MemoryOperandIsWritten(ins, mem_op) ) { // модифицированы ли адресса в памяти? INS_InsertIfPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)valid_ea, IARG_MEMORYOP_EA, mem_op, IARG_END);
> // если так, инструметируем код INS_InsertThenPredicatedCall( ins, IPOINT_BEFORE, (AFUNPTR)record_mem_write_cb, IARG_INST_PTR, IARG_MEMORYOP_EA, mem_op, IARG_END); } } } } }
//--------------------------------------------------------------------------
// Хэндл памяти что пишет записи
VOID record_mem_write_cb(VOID * ip, VOID * addr)
{
ADDRINT ea = (ADDRINT)addr;
segmap_t::iterator p;
for ( p = seg_bytes.begin(); p != seg_bytes.end() && !p->second.written; ++p )
{
ADDRINT start_ea = p->first;
if ( ea >= start_ea )
{
segdata_t *seg = &p->second;
if ( ea size )
{
fprintf(stderr, "%p: W %p\n", ip, addr);
write_address.push_back((ADDRINT)addr);
seg->written = true;
break;
}
}
}
}
VOID check_unpacked_cb(VOID * ip, const CONTEXT *ctxt, THREADID tid)
{
ADDRINT ea = (ADDRINT)ip;
addrdeq_t::iterator it = std::find(write_address.begin(), write_address.end(), ea);
if ( it != write_address.end() )
write_address.erase(it);
fprintf(stderr, "Layer unpacked: %p\n", ip);
PIN_ApplicationBreakpoint(ctxt, tid, false, "Layer unpacked!");
}
Расспаковка Skype
Об авторе: блог ведет Sanjar Satsura
Senior Security Analyst, Articles Writer at CyberSafe
Около 6 лет разработки программного обеспечения и реверс инженеринга закрытых программных продуктов на Windows Платформах, на *nix платформах - 4 год
Автор более 25 статей в журналах Xakep, Hakin9, "IT Sec" Magazine, "Software Developer's" Journal и 40 крупных программных продуктов и проектов.