//Пример, показывающий, как считать и получить информацию о секциях PE или PE+ файла
int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		std::cout << "Usage: pe_sections_reader.exe PE_FILE" << std::endl;
		return 0;
	}

	//Открываем файл
	std::ifstream pe_file(argv[1], std::ios::in | std::ios::binary);
	if(!pe_file)
	{
		std::cout << "Cannot open " << argv[1] << std::endl;
		return -1;
	}

	try
	{
		//Создаем экземпляр PE или PE+ класса с помощью фабрики
		pe_base image(pe_factory::create_pe(pe_file));

		//Получаем список секций
		std::cout << "Reading PE sections..." << std::hex << std::showbase << std::endl << std::endl;
		const section_list sections(image.get_image_sections());

		//Перечисляем секции и выводим информацию о них
		for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it)
		{
			const section& s = *it; //Секция
			std::cout << "Section [" << s.get_name() << "]" << std::endl //Имя секции
				<< "Characteristics: " << s.get_characteristics() << std::endl //Характеристики
				<< "Size of raw data: " << s.get_size_of_raw_data() << std::endl //Размер данных в файле
				<< "Virtual address: " << s.get_virtual_address() << std::endl //Виртуальный адрес
				<< "Virtual size: " << s.get_virtual_size() << std::endl //Виртуальный размер
				<< std::endl;
		}
	}
	catch(const pe_exception& e)
	{
		//Если возникла ошибка
		std::cout << "Error: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}
//Пример, показывающий, как посчитать энтропию файла и секций PE
int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		std::cout << "Usage: entropy_calculator.exe PE_FILE" << std::endl;
		return 0;
	}

	//Открываем файл
	std::ifstream pe_file(argv[1], std::ios::in | std::ios::binary);
	if(!pe_file)
	{
		std::cout << "Cannot open " << argv[1] << std::endl;
		return -1;
	}


	try
	{
		//Считаем энтропию файла
		std::cout << "File entropy: " << entropy_calculator::calculate_entropy(pe_file) << std::endl;

		//Создаем экземпляр PE или PE+ класса с помощью фабрики
		pe_base image(pe_factory::create_pe(pe_file));

		std::cout << "Sections entropy: " << entropy_calculator::calculate_entropy(image) << std::endl; //Считаем энтропию всех секций

		//Перечисляем секции и считаем их энтропию по отдельности
		const section_list sections = image.get_image_sections();
		for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it)
		{
			if(!(*it).empty()) //Если секция не пуста - посчитаем ее энтропию
				std::cout << "Section [" << (*it).get_name() << "] entropy: " << entropy_calculator::calculate_entropy(*it) << std::endl;
		}
	}
	catch(const pe_exception& e)
	{
		//Если возникла ошибка
		std::cout << "Error: " << e.what() << std::endl;
		return -1;
	}

	return 0;
}
Beispiel #3
0
int InstructionAnalysis::AnalyzeInstruction(Disasm::InstructionInformation *InsInfo) {
	// these are the instructions for x86 that have to be realigned...
	// make this modular later to handle other architectures!
	const char *realign[] = { "call","jmp","jz","jnz","jbe","jle","jge","ja","jb","js","jl","jg","jnp","jo","jns","jp","jecxz","push", "jne","je", NULL};
	char *AssemblyCodeString = 0;
	Disasm::CodeAddr DestAddr = 0;

	if (!InsInfo) return 0;

	//printf("Analyze Instruction %p\n", InsInfo->Address);

	// analyzed an instruction and determines properties like where it might jmp, or call...
	// whether it needs to add things into the queue... etc.. anything we need should be handled here

	// obtain ANSI string
	AssemblyCodeString = (char *)InsInfo->InstructionMnemonicString->c_str();
	// first lets check if it should be realigned later...


	if (!InsInfo->Analyzed) {
		InsInfo->Analyzed = 1;

		// change this later so it only executes once!
		uint32_t EntryPoint = PE_Handle->get_image_base_32() + PE_Handle->get_ep();

		//printf("ERROR Analyzing %p\n", InsInfo->Address);

		if (InsInfo->Address == EntryPoint) InsInfo->IsEntryPoint = 1;

		for (int n = 0; realign[n] != NULL; n++) {
			if (strstr(AssemblyCodeString, realign[n]) != NULL || (strstr(ud_insn_asm(&InsInfo->ud_obj), realign[n]))) {
				InsInfo->Requires_Realignment = 1;
				RealignCount++;
				break;
			}
		}

		// we need to add function level analysis here.. to find functions (push ebp, mov ebp, esp, ... ret)
		// also for x64... this will help us figure out any alignment issues later through analysis
	}

	InsInfo->AnalysisCount++;

	if (!InsInfo->Requires_Realignment) {
		return 1;
	}

	/*
	if (ud_insn_opr(&InsInfo->ud_obj, 1) != NULL) {
		InsInfo->Requires_Realignment = 0;
		return 1;
	}*/




	// Here's we we find which instructions need to be aligned.. i started so nice but ended up really sloppy
	// and feeding off of TWO libraries!  Capstone wont give me the offset like udis86 does! I have to either
	// modify capstone or find another solution (brute force?).. need a perm solution for all architectures
	// maybe i can go backwards from the last byte of the instruction and determine from the instruction
	// or i can learn more about ModRM and calulate it properly ;)
	Disasm::CodeAddr ToAddr = 0;
	signed char baby = 0;
	uint16_t boy = 0;
	uint32_t man = 0;

	const ud_operand_t *udop = ud_insn_opr(&InsInfo->ud_obj, 0);
	if (udop) {
		switch (udop->size) {
			case 8:
				baby = (signed char)udop->lval.sbyte;
				ToAddr =  baby;
				break;
			case 16:
				boy = (udop->lval.sword & 0xffff);
				ToAddr = boy;
				break;
			case 32:
				man = (udop->lval.sdword);
				ToAddr = man;
				break;
		}
		if (udop->type == UD_OP_MEM) InsInfo->IsPointer = 1;
		else if (udop->type == UD_OP_IMM) InsInfo->IsImmediate = 1;
	}

	if (InsInfo->_InsDetail.x86.operands[0].type != X86_OP_REG) {
		if (InsInfo->_InsDetail.x86.operands[0].type ==X86_OP_IMM) {
			DestAddr = (uint32_t)InsInfo->_InsDetail.x86.operands[0].imm;
			InsInfo->IsImmediate = 1;

		} else if (InsInfo->_InsDetail.x86.operands[0].type == X86_OP_MEM) {
			DestAddr = (uint32_t)InsInfo->_InsDetail.x86.operands[0].mem.disp;
			InsInfo->IsPointer = 1;

		} else if (InsInfo->_InsDetail.x86.disp) {
			DestAddr = (uint32_t)InsInfo->_InsDetail.x86.disp;
		}
	}

	//printf("[%p] ERROR Addr %p\n", InsInfo->Address, DestAddr);

	// Check if both engines match each other....
	/*if (InsInfo->OpDstAddress && InsInfo->OpDstAddress != ToAddr) {
		//	std::cout << "Error on this instruction: " << InsInfo->Address << std::endl;
		//	std::cout << "InsInfo " << InsInfo->Address << " Capstone " << InsInfo->OpDstAddress <<" ToAddr " << ToAddr << std::endl;
	}*/


		/* // if we found in udis86 and not capstone.. use udis86
		 if (!found && found2) {
			 InsInfo->OpDstAddress = ToAddr;
		 }*/


		 // some various addresses that were giving me trouble probably due to type conversions for sure
		 /*
		 if ((found == 0 && found2 == 0) || InsInfo->OpDstAddress == 0xFFFFFFFF || InsInfo->OpDstAddress < 8196
				 || InsInfo->OpDstAddress > 0xff000000) {
			 //InsInfo->OpDstAddress = 0;
		 }*/

	// Copy the raw destination out in case we want to verify it (match against OpDstAddress or ToAddr)
	signed char dist8 = 0;
	long dist32 = 0;
	long which;
	int bytes = InsInfo->Size - InsInfo->Displacement_Offset;
	switch (bytes) {
		case 1:
			std::memcpy((void *)&dist8, InsInfo->RawData + InsInfo->Size - 1, 1);
			which = dist8;
			break;

		case 4:
			std::memcpy((void *)&dist32, InsInfo->RawData + InsInfo->Size - 4, 4);
			which = dist32;
			break;

	}

	uint32_t newaddr = ((InsInfo->Address + InsInfo->Size) - which) & 0xff;
	uint32_t newaddr2 = ((InsInfo->Address + InsInfo->Size) + ToAddr) & 0xffffffff;


	DestAddr = newaddr2;
	//if (InsInfo->IsPointer)
	//printf("[%p:%d] %d \"%s\" which %X Orig goes to: %p [got %p] %d %ld %d\n", InsInfo->Address, InsInfo->Size,InsInfo->IsPointer, AssemblyCodeString, which, newaddr, InsInfo->OpDstAddress, dist8, dist32, ToAddr);
	//printf("[%p:%d] %d \"%s\" which %X Orig goes to: %p [got %p] [%s] %d\n", InsInfo->Address, InsInfo->Size, InsInfo->IsPointer, AssemblyCodeString, which, newaddr2, InsInfo->OpDstAddress, (char *)Disassembler_Handle->disasm_str(InsInfo->Address,(char *) InsInfo->RawData, InsInfo->Size).c_str(), ToAddr);
	InsInfo->orig = ToAddr;

	int found = 0, found2 = 0;
	// Ensure the address is within one of our sections & executable
	if (DestAddr) {
		 const section_list sections(PE_Handle->get_image_sections());
		 for(section_list::const_iterator it = sections.begin(); it != sections.end(); ++it) {
			 const section &s = *it;
			 Disasm::CodeAddr SecStart = (Disasm::CodeAddr)(PE_Handle->get_image_base_32() + s.get_virtual_address());
			 Disasm::CodeAddr SecEnd = (Disasm::CodeAddr)(PE_Handle->get_image_base_32() + s.get_virtual_address() + s.get_size_of_raw_data());

			 if (((uint32_t)(DestAddr) > (uint32_t)(0x4000)) && ((uint32_t)DestAddr >= SecStart && ((uint32_t)DestAddr < SecEnd))) {
				 if (s.executable()) {
					 InsInfo->OpDstAddress = DestAddr;
					 found = 1;
				 }
			 }

			 if ((which >= SecStart) && (which < SecEnd)) {
				 ToAddr = DestAddr = which;
				 InsInfo->IsImmediate = 1;
				 found2 = 1;
			 } else {
				 if ((ToAddr > 0x4000) && (ToAddr < 0xefff0000) &&
						 ((uint32_t)ToAddr >= SecStart) &&
						 ((uint32_t)ToAddr < SecEnd)) {
					 if (s.executable()) {
						 found2 = 1;
						 break;
					 }
				 }
			 }

			 if (found || found2) break;
		 }
	}

	if (InsInfo->IsImmediate && !found2) {
		InsInfo->IsImmediate = 0;
	}
	if (found || found2) InsInfo->OpDstAddress = DestAddr;


	// various other checks..
	if (strstr((const char *)AssemblyCodeString,(const char *) "push")) {
		PushCount++;
		InsInfo->IsPush = 1;
		if ((ud_insn_opr(&InsInfo->ud_obj, 2) != NULL)) {// || ((ToAddr < 0x4000) || (ToAddr > 0xefffff0000))) {
			//printf("Removing %p toaddr %p point %d %s\n", InsInfo->Original_Address, ToAddr, InsInfo->IsPointer, (char *)Disassembler_Handle->disasm_str(InsInfo->Address,(char *) InsInfo->RawData, InsInfo->Size).c_str());
			//printf("push %p not found %p\n", InsInfo->Address, DestAddr);
			InsInfo->Requires_Realignment = 0;
			InsInfo->OpDstAddress = 0;
			InsInfo->orig = 0;
		} else {
			//printf("ERROR push %p %X found %p - %d %d %p [op count%d]\n", InsInfo->Address, which, ToAddr, DestAddr, found, found2, ud_insn_opr(&InsInfo->ud_obj, 1));
			//InsInfo->OpDstAddress = ((InsInfo->Address + InsInfo->Size) + which) & 0xffffffff;
			//printf("ERROR push %X %p %p found %p - %d %d %p [[%X]]\n", which, InsInfo->Address, ToAddr, DestAddr, found, found2, ud_insn_opr(&InsInfo->ud_obj, 1), InsInfo->OpDstAddress&0xffffffff);
			InsInfo->OpDstAddress = ToAddr;
		}
	}

	if (strstr((const char *)AssemblyCodeString,(const char *) "call")) {
		CallCount++;
		InsInfo->IsCall = 1;
		if ((ud_insn_opr(&InsInfo->ud_obj, 2) != NULL)) {// || ((ToAddr < 0x4000) || (ToAddr > 0xefffff0000))) {
			//printf("Removing %p toaddr %p point %d - %s\n", InsInfo->Original_Address, ToAddr, InsInfo->IsPointer, (char *)Disassembler_Handle->disasm_str(InsInfo->Address,(char *) InsInfo->RawData, InsInfo->Size).c_str());

			InsInfo->Requires_Realignment = 0;
			InsInfo->OpDstAddress = 0;
			InsInfo->orig = 0;
		}
	}

	// If it's in the code section.. and a call or a push.. then we should queue it for analysis
	// just in case it leads us to other branches
	if (found == 1 && ((InsInfo->IsCall || InsInfo->IsPush))) {
		//Queue(Addr Of Ins Dest, Current Priority, 50 instructions max, 0 bytes max, redo?);
		QueueAddressForDisassembly(InsInfo->OpDstAddress, InsInfo->Priority, 500, 0, 0);
	}

	return 1;
}