# #x86-64 #disassembly #portable-executable
Вопрос:
Я хочу разобрать список исполняемых файлов с форматом вывода для инструкций, которые я могу настроить. Как предыдущий веб-разработчик, iced-x86 с Node.js кажется, это простой способ начать. Я хочу использовать это.
Код, который я использую, в точности соответствует тому, что указано в документации для iced-x86: Дизассемблируйте (инструкции по декодированию и форматированию), хотя я загружаю Uint8Array из своего собственного exe — файла и устанавливаю RIP на 0x0, я не уверен, как следует определять это значение-оно кажется произвольным эквивалентом uint64_t exampleRip = 0x00007FFAC46ACDA4
:
// iced-x86 features needed: --features "decoder nasm"
const { Decoder, DecoderOptions, Formatter, FormatterSyntax } = require("iced-x86");
/*
This code produces the following output:
00007FFAC46ACDA4 48895C2410 mov [rsp 10h],rbx
00007FFAC46ACDA9 4889742418 mov [rsp 18h],rsi
00007FFAC46ACDAE 55 push rbp
00007FFAC46ACDAF 57 push rdi
00007FFAC46ACDB0 4156 push r14
00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]
00007FFAC46ACDBA 4881EC00020000 sub rsp,200h
00007FFAC46ACDC1 488B0518570A00 mov rax,[rel 7FFA`C475`24E0h]
00007FFAC46ACDC8 4833C4 xor rax,rsp
00007FFAC46ACDCB 488985F0000000 mov [rbp 0F0h],rax
00007FFAC46ACDD2 4C8B052F240A00 mov r8,[rel 7FFA`C474`F208h]
00007FFAC46ACDD9 488D05787C0400 lea rax,[rel 7FFA`C46F`4A58h]
00007FFAC46ACDE0 33FF xor edi,edi
*/
const exampleBitness = 64;
const exampleRipLo = 0xC46ACDA4;
const exampleRipHi = 0x00007FFA;
const exampleCode = new Uint8Array([
0x48, 0x89, 0x5C, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x55, 0x57, 0x41, 0x56, 0x48, 0x8D,
0xAC, 0x24, 0x00, 0xFF, 0xFF, 0xFF, 0x48, 0x81, 0xEC, 0x00, 0x02, 0x00, 0x00, 0x48, 0x8B, 0x05,
0x18, 0x57, 0x0A, 0x00, 0x48, 0x33, 0xC4, 0x48, 0x89, 0x85, 0xF0, 0x00, 0x00, 0x00, 0x4C, 0x8B,
0x05, 0x2F, 0x24, 0x0A, 0x00, 0x48, 0x8D, 0x05, 0x78, 0x7C, 0x04, 0x00, 0x33, 0xFF
]);
const hexBytesColumnByteLength = 10;
const decoder = new Decoder(exampleBitness, exampleCode, DecoderOptions.None);
// You have to enable the bigint feature to get i64/u64 APIs, not all browsers support BigInt
decoder.ipLo = exampleRipLo;
decoder.ipHi = exampleRipHi;
// This decodes all bytes. There's also `decode()` which decodes the next instruction,
// `decodeInstructions(count)` which decodes `count` instructions and `decodeOut(instruction)`
// which overwrites an existing instruction.
const instructions = decoder.decodeAll();
// Create a nasm formatter. It supports: Masm, Nasm, Gas (ATamp;T) and Intel (XED).
// There's also `FastFormatter` which uses less code (smaller wasm files).
// const formatter = new FastFormatter();
const formatter = new Formatter(FormatterSyntax.Nasm);
// Change some options, there are many more
formatter.digitSeparator = "`";
formatter.firstOperandCharIndex = 10;
// Format the instructions
instructions.forEach(instruction => {
const disasm = formatter.format(instruction);
// Eg. "00007FFAC46ACDB2 488DAC2400FFFFFF lea rbp,[rsp-100h]"
let line = ("0000000" instruction.ipHi.toString(16)).substr(-8).toUpperCase()
("0000000" instruction.ipLo.toString(16)).substr(-8).toUpperCase();
line = " ";
const startIndex = instruction.ipLo - exampleRipLo;
exampleCode.slice(startIndex, startIndex instruction.length).forEach(b => {
line = ("0" b.toString(16)).substr(-2).toUpperCase();
});
for (let i = instruction.length; i < hexBytesColumnByteLength; i )
line = " ";
line = " ";
line = disasm;
console.log(line);
});
// Free wasm memory
instructions.forEach(instruction => instruction.free());
formatter.free();
decoder.free();
Тем временем я также разбираю тот же файл с Гидрой, чтобы проверить свою работу.
Гидра выводит правильную разборку заголовка:
//
// Headers
// ram:00400000-ram:004001ff
//
assume DF = 0x0 (Default)
IMAGE_DOS_HEADER_00400000 XREF[1]: 004000b4(*)
00400000 4d 5a 90 IMAGE_DO
00 03 00
00 00 04
00400000 4d 5a char[2] "MZ" e_magic
00400002 90 00 dw 90h e_cblp Bytes of last page
00400004 03 00 dw 3h e_cp Pages in file
00400006 00 00 dw 0h e_crlc Relocations
00400008 04 00 dw 4h e_cparhdr Size of header in
0040000a 00 00 dw 0h e_minalloc Minimum extra para
0040000c ff ff dw FFFFh e_maxalloc Maximum extra para
0040000e 00 00 dw 0h e_ss Initial (relative)
00400010 b8 00 dw B8h e_sp Initial SP value
00400012 00 00 dw 0h e_csum Checksum
00400014 00 00 dw 0h e_ip Initial IP value
00400016 00 00 dw 0h e_cs Initial (relative)
00400018 40 00 dw 40h e_lfarlc File address of re
0040001a 00 00 dw 0h e_ovno Overlay number
While iced_x86 outputs the following, clearing not correctly handling the header:
0000000000000000 4D5A pop r10
0000000000000002 90 nop
0000000000000003 0003 add [rbx],al
0000000000000005 0000 add [rax],al
0000000000000007 000400 add [rax rax],al
000000000000000A 0000 add [rax],al
000000000000000C FFFF (bad)
000000000000000E 0000 add [rax],al
0000000000000010 B800000000 mov eax,0
0000000000000015 0000 add [rax],al
0000000000000017 004000 add [rax],al
000000000000001A 0000 add [rax],al
Я недавно изучаю сборку; Я не знаю, как данные заголовка должны быть разобраны иначе, чем в остальных инструкциях. Как я могу с этим справиться? Есть ли функция iced_x86, которую я должен использовать?
Что я пробовал:
Я просматриваю документацию по этой теме и нашел это представление в C:
struct DOS_Header
{
// short is 2 bytes, long is 4 bytes
char signature[2] = { 'M', 'Z' };
short lastsize;
short nblocks;
short nreloc;
short hdrsize;
short minalloc;
short maxalloc;
void *ss; // 2 byte value
void *sp; // 2 byte value
short checksum;
void *ip; // 2 byte value
void *cs; // 2 byte value
short relocpos;
short noverlay;
short reserved1[4];
short oem_id;
short oem_info;
short reserved2[10];
long e_lfanew; // Offset to the 'PE' signature relative to the beginning of the file
}
Что кажется достаточно простым. Но я все еще не уверен, нужно ли мне писать код для этого в Node.js сценарий, или если библиотека iced_x86 уже поддерживает его.
Комментарии:
1. iced-x86-это дизассемблер. IIRC он не понимает ни одного исполняемого файла, включая PE (тот, о котором вы спрашиваете), а данные-это код, данные-это код. Я уверен, что существует множество библиотек PE для node.js в любом случае, физкультура довольно проста. Кроме того, обратите внимание, что ghidra не разбирает заголовок MZ, он просто показывает вам значение полей, что вы можете или не хотите делать (на самом деле это мало что добавляет, и есть специальные инструменты для проверки PE).
2. Возможно, вы захотите начать с дизассемблера с открытым исходным кодом , который понимает метаданные объектных файлов / исполняемых файлов, например
llvm-objdump
, или GNU Binutilsobjdump
. Они оба переносимы не на x86, а на многие форматы файлов, поэтому они не будут такими «простыми». LLVM, будучи более новым, может иметь меньше накопленных ошибок, и он не GPL, поэтому вы можете использовать его как часть проектов, не связанных с GPL, в случае, если это имеет значение.3. Дизассемблер objconv Агнера Фога специфичен для x86 и имеет открытый исходный код (GPL). agner.org/optimize/#objconv
4. @PeterCordes Спасибо, функциональность командной строки этого инструмента намного проще использовать, чем Ghidra