Я создаю 64-битный двоичный анализатор Mach-O для инструмента обратного проектирования в стиле Ghidra. Я хочу, чтобы программа выводила то, где мы находимся, на читаемом человеческом языке, используя только идентификаторы формата файла.
Позвольте мне показать вам пример:
0x100000004: cf fa ed fe
0x100000008: 0c 00 00 01
0x10000000c: 00 00 00 00
0x100000010: 02 00 00 00
0x100000014: 11 00 00 00
0x100000018: 20 04 00 00
0x10000001c: 85 00 20 00
0x100000020: 00 00 00 00
0x100000024: 19 00 00 00 LC_SEGMENT_64
Здесь LC_SEGMENT_64 находится на той стороне, где он начинается, я знаю это, потому что идентификатор LC_SEGMENT_64 равен 0x19. Но если я проделаю это со всеми возможными идентификаторами Mach-O, получится путаница. Как мне реализовать это правильно, не используя 50 тысяч операторов if-else?
Мой код банкомата:
#include <errno.h>
#include <mach-o/loader.h>
#include <mach/machine.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define BUFFER_SIZE 4
#define ERROR(msg) fprintf(stderr, "ERROR: %s | %s\n", msg, strerror(errno));
void HexPrinter(uint32_t buffer, FILE *binary) {
uint64_t mem_addr = 0x10000000;
fread(&buffer, 1, BUFFER_SIZE, binary);
if (buffer != MH_MAGIC_64) {
ERROR("Not a 64-bit Mach-O binary");
return;
} else {
printf("0x%llx: %02x %02x %02x %02x\n", mem_addr, (buffer & 0xFF),
((buffer >> 9) & 0xFF), ((buffer >> 16) & 0xFF),
((buffer >> 24) & 0xFF));
}
while ((fread(&buffer, 1, BUFFER_SIZE, binary)) == BUFFER_SIZE) {
printf("0x%llx: %02x %02x %02x %02x\t", mem_addr, (buffer & 0xFF),
((buffer >> 9) & 0xFF), ((buffer >> 16) & 0xFF),
((buffer >> 24) & 0xFF));
/* I don't want to write one of these for each identifier */
if (buffer == LC_SEGMENT_64) {
printf("LC_SEGMENT_64\n");
} else {
printf("\n");
}
mem_addr += BUFFER_SIZE;
}
if (ferror(binary)) {
ERROR("Error reading file");
fclose(binary);
return;
}
}
int main(int argc, char *argv[1]) {
FILE *binary;
char *pathname = argv[1];
uint32_t buffer;
if (!argv[1]) {
ERROR("Usage: ./nibBrev <pathname>");
return (-1);
}
binary = fopen(pathname, "r");
if (!binary) {
ERROR("Couldn't open file");
return (-1);
}
HexPrinter(buffer, binary);
fclose(binary);
return 0;
}
Похоже, ваш вопрос касается форматирования, а не того, как проанализировать двоичный файл в соответствующем контексте. Пожалуйста, предоставьте код, который вы написали на данный момент.
Вы можете создать карту, которая сопоставляет идентификатор со строкой, с помощью bsearch:
#include <stdlib.h> // for bsearch - binary search
// a struct to hold the value and string for _one_ identifier
typedef struct {
uint32_t val;
char const *str;
} identifier;
// all your identifiers goes here, in ascending order
const identifier identifiers[] = {
{LC_SEGMENT_64, "LC_SEGMENT_64"},
{SOMETHING_ELSE, "SOMETHING_ELSE"},
};
// comparison function for bsearch
int compare(const void *lhs, const void *rhs) {
const identifier *lid = lhs;
const identifier *rid = rhs;
if (lid->val < rid->val) return -1;
if (lid->val > rid->val) return 1;
return 0;
}
// a function to encapsulate the call to bsearch
const char *lookup(uint32_t id) {
identifier key = {id, NULL};
identifier *res = bsearch(&key,
identifiers,
sizeof identifiers / sizeof key,
sizeof key,
compare);
if (res) return res->str;
return NULL;
}
Тогда вместо
if (buffer == LC_SEGMENT_64) {
printf("LC_SEGMENT_64\n");
} else if (buffer == SOMETHING_ELSE) {
printf("SOMETHING_ELSE\n");
} else if (buffer == ...) {
printf("...\n");
} else {
printf("\n");
}
Вам понадобится только один if
:
const char *id = lookup(buffer);
if (id) {
puts(id);
} else {
putchar('\n');
}
Я прочитал файл loader.h , предоставленный из этого вопроса для формата Mach-O, с которым вы работаете, и постараюсь ответить на то, что я там прочитал. Если эта ссылка устарела или неверна, откорректируйте ее в зависимости от того, с чем вы работаете.
Упомянутые вами константы являются последовательными, начиная с #define LC_SEGMENT 0x1
и заканчивая #define LC_BUILD_VERSION 0x32
. Создайте сопоставление этих констант с индексами в таблице строк, как показано ниже.
/* This is how many identifiers I saw listed in loader.h add one
for the 0th unused slot. Number based on the last identifier in
the list of constants or count by hand. Either works. */
#define NUM_IDENTIFIERS LC_BUILD_VERSION + 1
static const char *const identifier_strings[NUM_IDENTIFIERS] = {
/* Probably unused 0th slot. */
[0] = "",
[LC_SEGMENT] = "LC_SEGMENT",
[LC_SYMTAB] = "LC_SYMTAB",
/* ...continues for rest of identifier constants */
/* careful with constants OR'd with LC_REQ_DYLD bit.
Or do this to all values to be safe, maybe? */
[LC_RPATH & ~LC_REQ_DYLD] = "LC_RPATH",
/* ...continues */
[LC_BUILD_VERSION] = "LC_BUILD_VERSION",
};
Строки теперь всегда синхронизированы с этими константами, а таблицу строк можно реорганизовать любым способом для удобства чтения благодаря используемой нотации [index] = "string",
.
Теперь функция поиска может выглядеть так.
const char *lookup(uint32_t identifier) {
/* While the identifiers are sequential be mindful of
the LC_REQ_DYLD bit that has been OR'd to some in
the list. That would mess up the indexing. See the
comment above this constant for more info. */
identifier &= ~LC_REQ_DYLD;
if (identifier && identifier < NUM_IDENTIFIERS) {
return identifier_strings[identifier];
}
return NULL;
}
Тогда то же самое, что и другой ответ.
const char *id = lookup(buffer);
if (id) {
puts(id);
} else {
puts('\n');
}
Предупреждение: я предполагаю, что вас интересует только раздел идентификаторов LC_*
. И ответ Теда Люнгмо, и мой ответ не сработали бы, если бы вы добавили больше констант для печати из loader.h
, потому что я вижу одни и те же значения, используемые для множества разных #define
по всему файлу.
Хороший улов с несколькими значениями. Я думаю, что можно было бы сопоставить это с массивом строк.
Да, меня интересовал только раздел LC_*. Мне удалось сделать решение на основе вашего, спасибо!
Пожалуйста, публикуйте код, данные и результаты в виде текста, а не скриншотов (как форматировать код в сообщениях ). Почему мне не следует загружать изображения кода/данных/ошибок? idownvotedbecau.se/imageofcode