//Пример, показывающий, как считать и получить информацию о секциях 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; }
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; }