дизассемблер
This commit is contained in:
parent
3ce19efae6
commit
a59a2302fe
192
kvs_data.py
192
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,
|
||||
}
|
||||
|
||||
# Инструкции с ПЕРЕМЕННЫМ размером (зависят от операндов)
|
||||
|
|
|
|||
325
kvsd.py
Normal file
325
kvsd.py
Normal file
|
|
@ -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()
|
||||
243
kvsd_data.py
Normal file
243
kvsd_data.py
Normal file
|
|
@ -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}"
|
||||
Loading…
Reference in a new issue