diff --git a/kvs_data.py b/kvs_data.py index 6a4eedf..4fb4482 100644 --- a/kvs_data.py +++ b/kvs_data.py @@ -50,16 +50,16 @@ REGISTERS = { "р15": 15, # R15 (общего назначения, пятнадцатый) # 32-битные младшие части (обнуляют старшие 32 бита при записи) - "еаикс": 0, # EAX - младшие 32 бита RAX - "есикс": 1, # ECX - младшие 32 бита RCX - "едикс": 2, # EDX - младшие 32 бита RDX - "ебикс": 3, # EBX - младшие 32 бита RBX - "есипи": 4, # ESP - младшие 32 бита RSP - "ебипи": 5, # EBP - младшие 32 бита RBP - "есиай": 6, # ESI - младшие 32 бита RSI - "едиай": 7, # EDI - младшие 32 бита RDI - "р8д": 8, # R8D - младшие 32 бита R8 - "р9д": 9, # R9D - младшие 32 бита R9 + "еаикс": 0, # EAX - младшие 32 бита RAX + "есикс": 1, # ECX - младшие 32 бита RCX + "едикс": 2, # EDX - младшие 32 бита RDX + "ебикс": 3, # EBX - младшие 32 бита RBX + "есипи": 4, # ESP - младшие 32 бита RSP + "ебипи": 5, # EBP - младшие 32 бита RBP + "есиай": 6, # ESI - младшие 32 бита RSI + "едиай": 7, # EDI - младшие 32 бита RDI + "р8д": 8, # R8D - младшие 32 бита R8 + "р9д": 9, # R9D - младшие 32 бита R9 "р10д": 10, # R10D - младшие 32 бита R10 "р11д": 11, # R11D - младшие 32 бита R11 "р12д": 12, # R12D - младшие 32 бита R12 @@ -68,16 +68,16 @@ REGISTERS = { "р15д": 15, # R15D - младшие 32 бита R15 # 16-битные младшие части (не обнуляют старшие биты) - "аикс": 0, # AX - младшие 16 бит RAX - "сикс": 1, # CX - младшие 16 бит RCX - "дикс": 2, # DX - младшие 16 бит RDX - "бикс": 3, # BX - младшие 16 бит RBX - "эсп": 4, # SP - младшие 16 бит RSP - "бипи": 5, # BP - младшие 16 бит RBP - "эс": 6, # SI - младшие 16 бит RSI - "ди": 7, # DI - младшие 16 бит RDI - "р8в": 8, # R8W - младшие 16 бит R8 - "р9в": 9, # R9W - младшие 16 бит R9 + "аикс": 0, # AX - младшие 16 бит RAX + "сикс": 1, # CX - младшие 16 бит RCX + "дикс": 2, # DX - младшие 16 бит RDX + "бикс": 3, # BX - младшие 16 бит RBX + "эсп": 4, # SP - младшие 16 бит RSP + "бипи": 5, # BP - младшие 16 бит RBP + "эс": 6, # SI - младшие 16 бит RSI + "ди": 7, # DI - младшие 16 бит RDI + "р8в": 8, # R8W - младшие 16 бит R8 + "р9в": 9, # R9W - младшие 16 бит R9 "р10в": 10, # R10W - младшие 16 бит R10 "р11в": 11, # R11W - младшие 16 бит R11 "р12в": 12, # R12W - младшие 16 бит R12 @@ -87,15 +87,15 @@ REGISTERS = { # 8-битные младшие части "ал": 0, # AL - младший байт AX (биты 0-7) - "кл": 1, # CL - младший байт CX (используется в сдвигах) - "дл": 2, # DL - младший байт DX - "бл": 3, # BL - младший байт BX - "спл": 4, # SPL - младший байт SP - "бпл": 5, # BPL - младший байт BP - "сил": 6, # SIL - младший байт SI - "дил": 7, # DIL - младший байт DI - "р8б": 8, # R8B - младший байт R8 - "р9б": 9, # R9B - младший байт R9 + "кл": 1, # CL - младший байт CX (используется в сдвигах) + "дл": 2, # DL - младший байт DX + "бл": 3, # BL - младший байт BX + "спл": 4, # SPL - младший байт SP + "бпл": 5, # BPL - младший байт BP + "сил": 6, # SIL - младший байт SI + "дил": 7, # DIL - младший байт DI + "р8б": 8, # R8B - младший байт R8 + "р9б": 9, # R9B - младший байт R9 "р10б": 10, # R10B - младший байт R10 "р11б": 11, # R11B - младший байт R11 "р12б": 12, # R12B - младший байт R12 @@ -242,75 +242,75 @@ INSTRUCTIONS = { # Инструкции с ФИКСИРОВАННЫМ размером (не зависят от операндов) FIXED_SIZE_INSTRUCTIONS = { - "переместить": 3, - "сравнить": 3, - "сравнить_байт": 3, - "проверить": 3, - "прибавить": 3, - "прибавить_байт": 3, - "вычесть": 3, - "вычесть_байт": 3, - "увеличить": 3, - "уменьшить": 3, - "умножить": 3, - "умножить_знаковое": 3, - "разделить": 3, - "разделить_знаковое": 3, - "и": 3, - "или": 3, - "исключающее_или": 3, - "инвертировать": 3, - "отрицать": 3, - "переход": 5, - "короткий_переход": 2, - "переход_если_равно": 6, - "переход_если_неравно": 6, - "переход_если_ноль": 6, - "переход_если_не_ноль": 6, - "переход_если_меньше": 6, - "переход_если_больше": 6, - "переход_если_меньше_или_равно": 6, - "переход_если_больше_или_равно": 6, - "переход_если_перенос": 6, - "переход_если_нет_переноса": 6, - "короткий_переход_если_равно": 2, - "короткий_переход_если_неравно": 2, - "короткий_переход_если_меньше": 2, - "короткий_переход_если_больше": 2, + "переместить" : 3, + "сравнить" : 3, + "сравнить_байт" : 3, + "проверить" : 3, + "прибавить" : 3, + "прибавить_байт" : 3, + "вычесть" : 3, + "вычесть_байт" : 3, + "увеличить" : 3, + "уменьшить" : 3, + "умножить" : 3, + "умножить_знаковое" : 3, + "разделить" : 3, + "разделить_знаковое" : 3, + "и" : 3, + "или" : 3, + "исключающее_или" : 3, + "инвертировать" : 3, + "отрицать" : 3, + "переход" : 5, + "короткий_переход" : 2, + "переход_если_равно" : 6, + "переход_если_неравно" : 6, + "переход_если_ноль" : 6, + "переход_если_не_ноль" : 6, + "переход_если_меньше" : 6, + "переход_если_больше" : 6, + "переход_если_меньше_или_равно" : 6, + "переход_если_больше_или_равно" : 6, + "переход_если_перенос" : 6, + "переход_если_нет_переноса" : 6, + "короткий_переход_если_равно" : 2, + "короткий_переход_если_неравно" : 2, + "короткий_переход_если_меньше" : 2, + "короткий_переход_если_больше" : 2, "короткий_переход_если_меньше_или_равно": 2, "короткий_переход_если_больше_или_равно": 2, - "короткий_переход_если_перенос": 2, - "короткий_переход_если_нет_переноса": 2, - "короткий_переход_если_ноль": 2, - "короткий_переход_если_не_ноль": 2, - "вызвать": 5, - "вернуться": 1, - "вызов_системы": 2, - "нет_операции": 1, - "остановить": 1, - "отладка": 1, - "цикл": 2, - "обменять": 4, - "сдвиг_влево": 4, - "сдвиг_вправо": 4, - "сдвиг_арифметический_вправо": 4, - "вращать_влево": 4, - "вращать_вправо": 4, - "прервать": 2, - "ввод_байта": 2, - "вывод_байта": 2, - "установить_перенос": 1, - "сбросить_перенос": 1, - "установить_направление": 1, - "сбросить_направление": 1, - "втолкнуть_флаги": 1, - "вытолкнуть_флаги": 1, - "переместить_байт": 1, - "переместить_слово": 1, - "сравнить_байты": 1, - "сканировать_байт": 1, - "идентифицировать_процессор": 2, - "прочитать_счётчик": 2, + "короткий_переход_если_перенос" : 2, + "короткий_переход_если_нет_переноса" : 2, + "короткий_переход_если_ноль" : 2, + "короткий_переход_если_не_ноль" : 2, + "вызвать" : 5, + "вернуться" : 1, + "вызов_системы" : 2, + "нет_операции" : 1, + "остановить" : 1, + "отладка" : 1, + "цикл" : 2, + "обменять" : 4, + "сдвиг_влево" : 4, + "сдвиг_вправо" : 4, + "сдвиг_арифметический_вправо" : 4, + "вращать_влево" : 4, + "вращать_вправо" : 4, + "прервать" : 2, + "ввод_байта" : 2, + "вывод_байта" : 2, + "установить_перенос" : 1, + "сбросить_перенос" : 1, + "установить_направление" : 1, + "сбросить_направление" : 1, + "втолкнуть_флаги" : 1, + "вытолкнуть_флаги" : 1, + "переместить_байт" : 1, + "переместить_слово" : 1, + "сравнить_байты" : 1, + "сканировать_байт" : 1, + "идентифицировать_процессор" : 2, + "прочитать_счётчик" : 2, } # Инструкции с ПЕРЕМЕННЫМ размером (зависят от операндов) diff --git a/kvsd.py b/kvsd.py new file mode 100644 index 0000000..cce3bcf --- /dev/null +++ b/kvsd.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +kvsd.py +Дизассемблер ELF-файлов для КВС с кастомным синтаксисом +""" + +import argparse +import os +import subprocess +import sys + +from kvsd_data import translate_mnemonic, translate_operand + +try: + from elfreader import ( + read_elf_header, read_section_headers, get_section_name, + SHT_PROGBITS, EM_X86_64 + ) +except ImportError: + print("Ошибка: не найден модуль elfreader.py", file=sys.stderr) + sys.exit(1) + + +def parse_bytes_hex(bytes_str): + """Преобразует строку шестнадцатеричных байтов в список байтов.""" + result = [] + for i in range(0, len(bytes_str), 2): + byte_hex = bytes_str[i:i+2] + if byte_hex: + result.append(int(byte_hex, 16)) + return result + + +def extract_displacement(bytes_list, pos, size, little_endian=True): + """Извлекает смещение из байтов.""" + if pos + size > len(bytes_list): + return None + if little_endian: + result = 0 + for i in range(size): + result |= bytes_list[pos + i] << (i * 8) + return result + else: + result = 0 + for i in range(size): + result = (result << 8) | bytes_list[pos + i] + return result + + +def get_instruction_length(mnemonic, bytes_list): + """Определяет длину инструкции в байтах.""" + fixed_lengths = { + "вернуться": 1, "нет_операции": 1, "остановить": 1, "отладка": 1, + "втолкнуть_флаги": 1, "вытолкнуть_флаги": 1, + "установить_перенос": 1, "сбросить_перенос": 1, + "установить_направление": 1, "сбросить_направление": 1, + "переместить_байт": 1, "переместить_слово": 1, + "сравнить_байты": 1, "сканировать_байт": 1, + "вызов_системы": 2, "прервать": 2, "цикл": 2, + "короткий_переход": 2, "короткий_переход_если_равно": 2, + "короткий_переход_если_неравно": 2, "короткий_переход_если_меньше": 2, + "короткий_переход_если_больше": 2, "ввод_байта": 2, "вывод_байта": 2, + "переместить": 3, "сравнить": 3, "проверить": 3, + "прибавить": 3, "вычесть": 3, "и": 3, "или": 3, "исключающее_или": 3, + "увеличить": 3, "уменьшить": 3, "загрузить": 3, "сохранить": 3, + "загрузить_адрес": 3, "обменять": 4, + "сдвиг_влево": 4, "сдвиг_вправо": 4, "сдвиг_арифметический_вправо": 4, + "вращать_влево": 4, "вращать_вправо": 4, + "вращать_через_перенос_влево": 4, "вращать_через_перенос_вправо": 4, + "переход": 5, "вызвать": 5, "переход_если_равно": 6, + "переход_если_неравно": 6, "переход_если_ноль": 6, + "переход_если_не_ноль": 6, "переход_если_меньше": 6, + "переход_если_больше": 6, + } + + if mnemonic in fixed_lengths: + return fixed_lengths[mnemonic] + + if mnemonic == "переместить_имм": + if len(bytes_list) >= 2: + opcode = bytes_list[0] if bytes_list else 0 + if opcode == 0x48 and len(bytes_list) > 1 and bytes_list[1] >= 0xB8: + return 10 + elif opcode >= 0xB8 and opcode <= 0xBF: + return 5 + return 7 + + if mnemonic in ["сравнить_с", "прибавить_непосредственно", "вычесть_непосредственно"]: + return 7 + + return len(bytes_list) + + +def compute_target_address(address, mnemonic, bytes_list): + """Вычисляет целевой адрес для инструкций перехода.""" + relative_jumps = { + "переход", "короткий_переход", "переход_если_равно", "переход_если_неравно", + "переход_если_меньше", "переход_если_больше", "переход_если_ноль", + "переход_если_не_ноль", "короткий_переход_если_равно", "короткий_переход_если_неравно", + "короткий_переход_если_меньше", "короткий_переход_если_больше", + "короткий_переход_если_перенос", "короткий_переход_если_нет_переноса", + "вызвать", "цикл" + } + + if mnemonic not in relative_jumps: + return "" + + instr_len = get_instruction_length(mnemonic, bytes_list) + + if instr_len == 2 and len(bytes_list) >= 2: + displacement = bytes_list[1] + if displacement > 127: + displacement = displacement - 256 + target = address + instr_len + displacement + return f"0x{target:x}" + + elif instr_len == 5 and len(bytes_list) >= 5: + displacement = extract_displacement(bytes_list, 1, 4) + if displacement is not None: + if displacement > 0x7FFFFFFF: + displacement = displacement - 0x100000000 + target = address + instr_len + displacement + return f"0x{target:x}" + + elif instr_len == 6 and len(bytes_list) >= 6: + displacement = extract_displacement(bytes_list, 2, 4) + if displacement is not None: + if displacement > 0x7FFFFFFF: + displacement = displacement - 0x100000000 + target = address + instr_len + displacement + return f"0x{target:x}" + + return "" + + +def parse_ndisasm_line(line): + """Разбирает строку вывода ndisasm и переводит её.""" + line = line.strip() + if not line: + return None + + parts = line.split(maxsplit=2) + if len(parts) < 3: + return None + + address_str = parts[0] + bytes_hex = parts[1] + rest = parts[2] + + # Разделяем мнемонику и операнды + rest_parts = rest.split(maxsplit=1) + mnemonic_orig = rest_parts[0] + operands_orig = rest_parts[1] if len(rest_parts) > 1 else "" + + # Вычисляем длину инструкции + bytes_list = parse_bytes_hex(bytes_hex) + instr_len = len(bytes_list) + + # Переводим мнемонику (с учётом операндов и длины) + translated_mnemonic = translate_mnemonic(mnemonic_orig, operands_orig, instr_len) + # Переводим операнды (регистры внутри) + translated_operands = translate_operand(operands_orig) + + return { + 'address': int(address_str, 16), + 'address_hex': address_str, + 'bytes_hex': bytes_hex, + 'mnemonic': translated_mnemonic, + 'operands': translated_operands, + 'instr_len': instr_len + } + + +def filter_by_address_range(disasm_output, start_addr, end_addr): + """ + Оставляет только строки, адрес которых входит в диапазон [start_addr, end_addr). + """ + filtered_lines = [] + for line in disasm_output.splitlines(): + if not line.strip(): + continue + addr_str = line.split(maxsplit=1)[0] + try: + addr = int(addr_str, 16) + if start_addr <= addr < end_addr: + filtered_lines.append(line) + except ValueError: + continue + return '\n'.join(filtered_lines) + + +def generate_csv_table(disasm_output): + """Генерирует CSV-таблицу из вывода ndisasm.""" + rows = [] + + for line in disasm_output.splitlines(): + parsed = parse_ndisasm_line(line) + if not parsed: + continue + + address = parsed['address'] + bytes_hex = parsed['bytes_hex'] + mnemonic = parsed['mnemonic'] + operands = parsed['operands'] + + # Формируем исходную команду + if operands: + original_cmd = f"{mnemonic} {operands}" + else: + original_cmd = mnemonic + + bytes_list = parse_bytes_hex(bytes_hex) + target = compute_target_address(address, mnemonic, bytes_list) + + # Разбиваем байты на отдельные + byte_parts = [bytes_hex[i:i+2] for i in range(0, len(bytes_hex), 2)] + + for i, byte in enumerate(byte_parts): + byte_address = address + i + if i == 0: + rows.append({ + 'address': f"0x{byte_address:x}", + 'bytes': byte, + 'target': target, + 'cmd': original_cmd + }) + else: + rows.append({ + 'address': f"0x{byte_address:x}", + 'bytes': byte, + 'target': "", + 'cmd': "" + }) + + return rows + + +def disassemble_elf_to_csv(elf_file, output_file=None): + """Дизассемблирует ELF-файл и сохраняет результат в CSV.""" + try: + if not os.path.exists(elf_file): + return False, f"Файл {elf_file} не найден" + + if not output_file: + base_name = os.path.splitext(elf_file)[0] + output_file = f"{base_name}.elf.csv" + + with open(elf_file, 'rb') as f: + hdr = read_elf_header(f) + if hdr['e_machine'] != EM_X86_64: + print(f"Предупреждение: архитектура не x86-64") + + if hdr['e_shnum'] == 0: + return False, "Файл не содержит таблицы секций" + + sections = read_section_headers(f, hdr['e_shoff'], hdr['e_shnum']) + shstrtab = sections[hdr['e_shstrndx']] + shstrtab_offset = shstrtab['sh_offset'] + shstrtab_size = shstrtab['sh_size'] + + offset, size, vaddr = None, None, None + for sec in sections: + name = get_section_name(f, shstrtab_offset, shstrtab_size, sec['sh_name']) + if name == '.text' and sec['sh_type'] == SHT_PROGBITS: + offset = sec['sh_offset'] + size = sec['sh_size'] + vaddr = sec['sh_addr'] + break + + if offset is None: + return False, "Секция .text не найдена" + + print(f"Секция .text: адрес 0x{vaddr:x}, размер 0x{size:x} ({size} байт)") + + cmd = ['ndisasm', '-b64', '-o', f'0x{vaddr - offset:x}', elf_file] + print(f"Запуск: {' '.join(cmd)}") + + result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + if result.returncode != 0: + return False, f"Ошибка ndisasm: {result.stderr.strip()}" + + end_addr = vaddr + size + filtered_output = filter_by_address_range(result.stdout, vaddr, end_addr) + + if not filtered_output: + print("Предупреждение: после фильтрации не осталось строк") + rows = [] + else: + rows = generate_csv_table(filtered_output) + + with open(output_file, 'w', encoding='utf-8') as out_f: + out_f.write("адрес;байт;целевой_адрес;исходная_команда\n") + for row in rows: + out_f.write(f"{row['address']};{row['bytes']};{row['target']};{row['cmd']}\n") + + print(f"CSV сохранён: {output_file} (строк: {len(rows)})") + return True, output_file + + except FileNotFoundError: + return False, "ndisasm не найден. Установите NASM" + except Exception as e: + return False, str(e) + + +def main(): + parser = argparse.ArgumentParser(description='Дизассемблер ELF-файлов для КВС') + parser.add_argument('input_file', help='Путь к ELF-файлу') + parser.add_argument('-o', '--output', help='Выходной CSV-файл') + args = parser.parse_args() + + print(f"Дизассемблирование: {args.input_file}") + success, result = disassemble_elf_to_csv(args.input_file, args.output) + + if success: + print(f"\nГотово! Результат: {result}") + else: + print(f"Ошибка: {result}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/kvsd_data.py b/kvsd_data.py new file mode 100644 index 0000000..a588034 --- /dev/null +++ b/kvsd_data.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +kvsd_data.py +Словари для дизассемблера КВС +Трансляция x86-64 мнемоник в кастомный синтаксис +""" + +# === СЛОВАРЬ ТРАНСЛЯЦИИ МНЕМОНИК === +MNEMONIC_TRANSLATION = { + "mov": "переместить", + "movzx": "переместить_с_нулями", + "movsx": "переместить_со_знаком", + "lea": "загрузить_адрес", + "add": "прибавить", + "sub": "вычесть", + "inc": "увеличить", + "dec": "уменьшить", + "mul": "умножить", + "div": "разделить", + "imul": "умножить_знаковое", + "idiv": "разделить_знаковое", + "neg": "отрицать", + "and": "и", + "or": "или", + "xor": "исключающее_или", + "not": "инвертировать", + "cmp": "сравнить", + "test": "проверить", + "jmp": "переход", + "je": "переход_если_равно", + "jz": "переход_если_ноль", + "jne": "переход_если_неравно", + "jnz": "переход_если_не_ноль", + "jl": "переход_если_меньше", + "jg": "переход_если_больше", + "jle": "переход_если_меньше_или_равно", + "jge": "переход_если_больше_или_равно", + "jc": "переход_если_перенос", + "jnc": "переход_если_нет_переноса", + "jo": "переход_если_переполнение", + "jno": "переход_если_нет_переполнения", + "js": "переход_если_знак", + "jns": "переход_если_нет_знака", + "jp": "переход_если_чётность", + "jnp": "переход_если_не_чётность", + "jbe": "переход_если_ниже_или_равно", + "ja": "переход_если_выше", + "call": "вызвать", + "ret": "вернуться", + "retn": "вернуться", + "push": "втолкнуть", + "pop": "вытолкнуть", + "pushf": "втолкнуть_флаги", + "popf": "вытолкнуть_флаги", + "shl": "сдвиг_влево", + "sal": "сдвиг_влево", + "shr": "сдвиг_вправо", + "sar": "сдвиг_арифметический_вправо", + "rol": "вращать_влево", + "ror": "вращать_вправо", + "rcl": "вращать_через_перенос_влево", + "rcr": "вращать_через_перенос_вправо", + "syscall": "вызов_системы", + "int": "прервать", + "sysenter": "войти_в_систему", + "hlt": "остановить", + "cpuid": "идентифицировать_процессор", + "rdtsc": "прочитать_счётчик", + "nop": "нет_операции", + "int3": "отладка", + "movsb": "переместить_байт", + "movsw": "переместить_слово", + "movsd": "переместить_двойное_слово", + "movsq": "переместить_квадрослово", + "cmpsb": "сравнить_байты", + "cmpsw": "сравнить_слова", + "scasb": "сканировать_байт", + "stosb": "сохранить_байт", + "stc": "установить_перенос", + "clc": "сбросить_перенос", + "std": "установить_направление", + "cld": "сбросить_направление", + "in": "ввод_байта", + "out": "вывод_байта", + "loop": "цикл", + "loope": "цикл_если_равно", + "loopne": "цикл_если_не_равно", + "xchg": "обменять", +} + +# === СЛОВАРЬ ТРАНСЛЯЦИИ РЕГИСТРОВ === +REGISTER_TRANSLATION = { + "rax": "раикс", "rcx": "рсикс", "rdx": "рдикс", "rbx": "рбикс", + "rsp": "рсипи", "rbp": "рбипи", "rsi": "рсиай", "rdi": "рдиай", + "r8": "р8", "r9": "р9", "r10": "р10", "r11": "р11", + "r12": "р12", "r13": "р13", "r14": "р14", "r15": "р15", + "eax": "еаикс", "ecx": "есикс", "edx": "едикс", "ebx": "ебикс", + "esp": "есипи", "ebp": "ебипи", "esi": "есиай", "edi": "едиай", + "r8d": "р8д", "r9d": "р9д", "r10d": "р10д", "r11d": "р11д", + "r12d": "р12д", "r13d": "р13д", "r14d": "р14д", "r15d": "р15д", + "ax": "аикс", "cx": "сикс", "dx": "дикс", "bx": "бикс", + "sp": "эсп", "bp": "бипи", "si": "эс", "di": "ди", + "r8w": "р8в", "r9w": "р9в", "r10w": "р10в", "r11w": "р11в", + "r12w": "р12в", "r13w": "р13в", "r14w": "р14в", "r15w": "р15в", + "al": "ал", "cl": "кл", "dl": "дл", "bl": "бл", + "spl": "спл", "bpl": "бпл", "sil": "сил", "dil": "дил", + "r8b": "р8б", "r9b": "р9б", "r10b": "р10б", "r11b": "р11б", + "r12b": "р12б", "r13b": "р13б", "r14b": "р14б", "r15b": "р15б", + "ah": "аш", "ch": "чш", "dh": "дш", "bh": "бш", + "rip": "рип", "rflags": "рфлаги", +} + + +def translate_mnemonic(mnemonic, operand_str="", instr_len=0): + """ + Переводит мнемонику x86-64 в кастомный синтаксис КВС. + + Аргументы: + mnemonic (str): исходная мнемоника + operand_str (str): строка операндов (для определения short) + instr_len (int): длина инструкции в байтах (для определения коротких/длинных переходов) + """ + # Для JMP: определяем по наличию "short" в операндах + if mnemonic == "jmp" and "short" in operand_str: + return "короткий_переход" + + # Для условных переходов: определяем по длине инструкции + if mnemonic in ["je", "jz", "jne", "jnz", "jl", "jg", "jle", "jge", + "jc", "jnc", "jo", "jno", "js", "jns", "jp", "jnp", + "jbe", "ja"]: + # Длина 2 байта = короткий переход (rel8) + if instr_len == 2: + translation = { + "je": "короткий_переход_если_равно", + "jz": "короткий_переход_если_ноль", + "jne": "короткий_переход_если_неравно", + "jnz": "короткий_переход_если_не_ноль", + "jl": "короткий_переход_если_меньше", + "jg": "короткий_переход_если_больше", + "jle": "короткий_переход_если_меньше_или_равно", + "jge": "короткий_переход_если_больше_или_равно", + "jc": "короткий_переход_если_перенос", + "jnc": "короткий_переход_если_нет_переноса", + "jo": "короткий_переход_если_переполнение", + "jno": "короткий_переход_если_нет_переполнения", + "js": "короткий_переход_если_знак", + "jns": "короткий_переход_если_нет_знака", + "jp": "короткий_переход_если_чётность", + "jnp": "короткий_переход_если_не_чётность", + "jbe": "короткий_переход_если_ниже_или_равно", + "ja": "короткий_переход_если_выше", + } + return translation.get(mnemonic, mnemonic) + # Длина 6 байт = длинный переход (rel32) + elif instr_len == 6: + translation = { + "je": "переход_если_равно", + "jz": "переход_если_ноль", + "jne": "переход_если_неравно", + "jnz": "переход_если_не_ноль", + "jl": "переход_если_меньше", + "jg": "переход_если_больше", + "jle": "переход_если_меньше_или_равно", + "jge": "переход_если_больше_или_равно", + "jc": "переход_если_перенос", + "jnc": "переход_если_нет_переноса", + "jo": "переход_если_переполнение", + "jno": "переход_если_нет_переполнения", + "js": "переход_если_знак", + "jns": "переход_если_нет_знака", + "jp": "переход_если_чётность", + "jnp": "переход_если_не_чётность", + "jbe": "переход_если_ниже_или_равно", + "ja": "переход_если_выше", + } + return translation.get(mnemonic, mnemonic) + + return MNEMONIC_TRANSLATION.get(mnemonic, mnemonic) + + +def translate_operand(operand): + """ + Переводит операнд: заменяет все регистры на кастомные имена. + Также удаляет слово "short" из операндов. + """ + if not operand: + return operand + + result = operand + + # Удаляем "short" из операндов (для коротких переходов) + result = result.replace("short ", "") + result = result.replace(" short", "") + + # Сортируем регистры по длине (от длинных к коротким) + sorted_regs = sorted(REGISTER_TRANSLATION.keys(), key=len, reverse=True) + + for reg in sorted_regs: + trans_reg = REGISTER_TRANSLATION[reg] + for sep in ['', ' ', ',', '+', '-', '*', '/', '(', ')', '[', ']', ':', '&', '|']: + result = result.replace(reg + sep, trans_reg + sep) + result = result.replace(sep + reg, sep + trans_reg) + if result.startswith(reg): + result = trans_reg + result[len(reg):] + if result.endswith(reg): + result = result[:-len(reg)] + trans_reg + result = result.replace(reg, trans_reg) + + return result + + +def translate_line(line): + """ + Переводит строку дизассемблера. + Формат: "00401000 48B80A0000000000 mov rax,0xa" + """ + if not line.strip(): + return line + + parts = line.split(maxsplit=2) + if len(parts) < 3: + return line + + address = parts[0] + bytes_hex = parts[1] + rest = parts[2] + + rest_parts = rest.split(maxsplit=1) + mnemonic = rest_parts[0] + operands = rest_parts[1] if len(rest_parts) > 1 else "" + + # Вычисляем длину инструкции + instr_len = len(bytes_hex) // 2 if bytes_hex else 0 + + new_mnemonic = translate_mnemonic(mnemonic, operands, instr_len) + new_operands = translate_operand(operands) + + if new_operands: + return f"{address} {bytes_hex} {new_mnemonic} {new_operands}" + else: + return f"{address} {bytes_hex} {new_mnemonic}" \ No newline at end of file