Я занимаюсь разработкой дизассемблера на си для линукс. О себе я не сказал бы что я профессиональный программист. Я не работаю программистом, а то чему я научился, либо книжки читал, либо придумывал сам, либо изучая исходный код других программ. Поэтому если вам код покажется детским, не ругайтесь. )
Для начала я делал дизассемблер и мой код щас выглядит так, то есть считывается байт и передаётся нужной функции.
void disasm_intel ( unsigned char *ptr, int size, int byte_order, int show ) { show_asm = show; virt = global_virt_text; unsigned char *start = ptr; start_op = ptr; for ( int index = 0; index < size; index++ ) { if ( show_asm == TRUE ) printf ( "%lx: ", virt ); switch ( *ptr ) { case 0x30: intel_opcode_1_0x30 ( &ptr, &index, byte_order ); break; case 0x31: intel_opcode_1_0x31 ( &ptr, &index, byte_order ); break; case 0x66: intel_opcode_1_0x66 ( &ptr, &index, byte_order ); break; case 0x67: intel_opcode_1_0x67 ( &ptr, &index, byte_order ); break; case 0x83: intel_opcode_1_0x83 ( &ptr, &index, byte_order ); break; case 0x88: intel_opcode_1_0x88 ( &ptr, &index, byte_order ); break; // mov register to register byte case 0x89: intel_opcode_1_0x89 ( &ptr, &index, byte_order ); break; case 0x8a: intel_opcode_1_0x8a ( &ptr, &index, byte_order ); break; case 0x8b: intel_opcode_1_0x8b ( &ptr, &index, byte_order ); break; // mov esp, %x : mov ebp, %x case 0x8d: intel_opcode_1_0x8d ( &ptr, &index, byte_order ); break; // lea case 0xb0: intel_opcode_1_0xb0 ( &ptr, &index ); break; // mov al, %x case 0xb1: intel_opcode_1_0xb1 ( &ptr, &index ); break; // mov cl, %x case 0xb2: intel_opcode_1_0xb2 ( &ptr, &index ); break; // mov dl, %x case 0xb3: intel_opcode_1_0xb3 ( &ptr, &index ); break; // mov bl, %x case 0xb4: intel_opcode_1_0xb4 ( &ptr, &index ); break; // mov ah, %x case 0xb5: intel_opcode_1_0xb5 ( &ptr, &index ); break; // mov ch, %x case 0xb6: intel_opcode_1_0xb6 ( &ptr, &index ); break; // mov dh, %x case 0xb7: intel_opcode_1_0xb7 ( &ptr, &index ); break; // mov bh, %x case 0xb8: intel_opcode_1_0xb8 ( &ptr, &index, byte_order ); break; // mov eax, %x case 0xb9: intel_opcode_1_0xb9 ( &ptr, &index, byte_order ); break; // mov ecx, %x case 0xba: intel_opcode_1_0xba ( &ptr, &index, byte_order ); break; // mov edx, %x case 0xbb: intel_opcode_1_0xbb ( &ptr, &index, byte_order ); break; // mov ebx, %x case 0xbe: intel_opcode_1_0xbe ( &ptr, &index, byte_order ); break; // mov esi, %x case 0xbf: intel_opcode_1_0xbf ( &ptr, &index, byte_order ); break; // mov edi, %x case 0xc3: intel_opcode_1_0xc3 ( ); break; // ret case 0xcd: intel_opcode_1_0xcd ( &ptr, &index ); break; // int 0x%x } ptr++; virt += ptr - start; start = ptr; start_op = ptr; } show_asm = FALSE; }
И таких функций уже большая куча. В некоторых местах я сделал комментарии, чтобы уловить взаимосвязь машинных команд и может быть в последствии сделать более грамотный дизасм. Но в этом виде, в котором у меня щас код, я легко могу установить для кажого оператора сколько угодно условий.
И вот пока я делал это у меня появилась идея, возможно ли добавлять код в середину секции кода? Оказывается можно, но во всех ли случаях? Пока что чтобы добавить код я использую уже подготовленные машинные коды, если потом смогу, то сделаю транслятор ассемблера в машинных код, чтобы добавлять код было удобней. В моём случае надо указать смещение в секции кода и байты скопируются в нужное место. Также была проблема определённая: адресация в памяти. Я добавил в команде lea код, который сохраняет в структуру нужные данные, и если ты вставляешь новые операторы в секцию кода, то все смещения выстраиваются так, что указывают на данные в новых смещениях. Ну это не очень сложно, если вставил код, то секция кода увеличилась на столько же байт и все остальные секции после секции кода будут содержать уже новые смещения. Сделал так, чтобы были различия в том, куда ты вставляешь код, все смещения работают правильно. Потом появилась проблема в том что в адресации такой
mov eax, [eax + eax + 0x100]
Дело в том, что в такой адресации может быть ebp и указывать на стек, а не в другую секцию. Я решил сделать так, что если адрес указывает на секцию данных, то учитывать смещения при вставке кода, если же указывает на стек, то есть не на адрес в секции данных, то не учитывать смещения.
И ведь таким способом могут воспользоваться злоумышленники. Ведь вредоносный код могут вставить в начало какой нибудь функции в программе, например чтобы создался fork дочерний и скачался специальный файл. В линуксе это можно сделать вроде без проблем. Ведь в /usr/include есть файл со всеми системными функциями операционной системы. То есть можно использовать сетевую часть, даже если в программе нет сетевых функций. Не знаю как в windows, но я буду пробывать потом добавлять работу с pe форматом. Может получиться сделать тоже что и в линукс. Пока что у меня консольная версия. Но потом планирую делать на gtk.
Спасибо что потратили своё время на мою статью.
ссылка на оригинал статьи https://habr.com/ru/post/477590/
Добавить комментарий