Pull to refresh

Встраивание кода и опасность пиратского ПО

Reading time3 min
Views3.8K
О том, как можно встроить код без jmp в секцию кода и остаться незаметным, если не изучать досконально дизассемблированный код. Кому интересно, прошу под кат.

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

Для начала я делал дизассемблер и мой код сейчас выглядит так, то есть считывается байт и передаётся нужной функции.

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.

Спасибо что потратили своё время на мою статью.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 20: ↑7 and ↓13-6
Comments7

Articles