Набросал небольшой прототип учебного эмулятора на 4 регистра. Память выделил, но для упрощения понимания принципа работы, пока не задействовал. Критика и замечания приветствуются. Код (C): #include <stdio.h> #include <stdint.h> #define MEMORY_SIZE 0x1000 // 4 Кб памяти #define NUM_REGS 4 // Количество регистров typedef struct { uint8_t memory[MEMORY_SIZE]; // Память uint32_t regs[NUM_REGS]; // Регистры } bdshemu_context; void print_registers(bdshemu_context* ctx) { printf("Registers:\n"); for (int i = 0; i < NUM_REGS; i++) { printf("R%d = 0x%08X\n", i, ctx->regs[i]); } } void process_instructions(bdshemu_context* ctx, uint8_t* code, size_t code_size) { size_t ip = 0; // Instruction pointer while (ip < code_size) { uint8_t opcode = code[ip++]; switch (opcode) { case 0x01: // MOV // MOV <reg>, <value> { uint8_t reg = code[ip++]; uint8_t value = code[ip++]; ctx->regs[reg] = value; printf("MOV R%d, 0x%02X\n", reg, value); } break; case 0x02: // ADD // ADD <reg>, <value> { uint8_t reg = code[ip++]; uint8_t value = code[ip++]; ctx->regs[reg] += value; printf("ADD R%d, 0x%02X\n", reg, value); } break; case 0x03: // SUB // SUB <reg>, <value> { uint8_t reg = code[ip++]; uint8_t value = code[ip++]; ctx->regs[reg] -= value; printf("SUB R%d, 0x%02X\n", reg, value); } break; case 0x90: // NOP // No operation printf("NOP\n"); break; case 0x04: // JMP // JMP <address> { uint8_t low = code[ip++]; uint8_t high = code[ip++]; uint16_t address = (high << 8) | low; printf("JMP to address: 0x%04X\n", address); } break; default: // Unknown byte printf("Unknown byte encountered: 0x%02X\n", opcode); break; } } // Print all registers after execution print_registers(ctx); } int main() { bdshemu_context ctx = { 0 }; // Initialize context, all registers are 0 // Example machine code to run on the emulator uint8_t code[] = { 0x01, 0x00, 0x10, // MOV R0, 0x10 0x66, // Undefined instruction 0x02, 0x00, 0x05, // ADD R0, 0x5 0x03, 0x00, 0x03, // SUB R0, 0x3 0x90, // NOP 0x04, 0x20, 0x00, // JMP 0x20 0x01, 0x01, 0x28, // MOV R1, 0x28 0x01, 0x02, 0x68, // MOV R2, 0x68 0x77, // Undefined instruction 0x03, 0x01, 0x03, // SUB R1, 0x3 0x01, 0x03, 0x88 // MOV R3 0x88 }; size_t code_size = sizeof(code) / sizeof(code[0]); // Process the instructions and print registers process_instructions(&ctx, code, code_size); return 0; }
GRAFik Это байт-кодовый интерпретатор, штатный цикл fetch-decode-execute и формально циклический поток наследования через указатель в байткод. Всем известная printf - хороший пример простой байткодовой вирты.
Entropy wtf, все по теме не просто формулировано, а введены годные понятия, теория наследования. Та трудность с введением дельты в modrm, IDP, разобрана. Что такое кодовый анклав. Позже выложу кодес.
Мой эмулятор вымышленной 16-битной машины с экстремально простой системой команд - SimpX: Попробовать в онлайн: https://aa-dav.github.io/ Эмуляция работает сразу из ассемблерного кода - он транслируется в виртуальный образ машины при запуске и эмулятор включается на только что оттранслированный образ. Файлы с исходниками есть библиотечные и их бесполезно транслировать, надо выделить в левом списке файл с названием вида test-0X.asm и нажать меню Compile and Run. Исходники и описание архитектуры процессора тут: https://github.com/aa-dav/SimpX/blob/master/README_simpleton_ru.md Очень простая 16-битная трёхоперандная машина - каждая инструкция выполняет шаблон "назначение = операнд1 операция операнд2" с небольшими вариациями для оптимизации процесса.
я хотел сказать мне в общих чертах понятно что такое эмуляция,а если углубляться, это надо разбирать исходники эмуляторов
Research Unicorn Engine Обработка системных событий в обычных фильтрах примитивна - ядро выполняет выборку аргументов, видимую в юзер.
При выборке аргумента, его проверке try-fetch-except или ProbeFor() происходит дереференс ссылки, это приводит к прокидыванию #guard и разблокировки страницы. Отсутствие этого механизма делает детект эмуляции сервисов элементарным, можно автоматикой накопить размеры всех аргументов любого сискола. Тупо фильтрация это бесполезная шляпа, юзер апи эмулируют обычно адресными ловушками if rIp = *Api: emulate(Api). В таком виде тела функции нет - его нельзя прочитать/переместить. Более продвинутая аверская метода это вставить шлюз(атом) if [rIp] = SignatureId(Api): emulate(Id).