mirror of
https://github.com/bitdefender/bddisasm.git
synced 2024-12-22 05:58:07 +00:00
Use SWIG to create bindings between C and Python.
This commit is contained in:
parent
096b583c25
commit
4f182b2c11
@ -186,7 +186,7 @@
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2020 Bitdefender
|
||||
Copyright 2023 Bitdefender
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -4,3 +4,7 @@ include *.md
|
||||
# Include the license file
|
||||
include LICENSE
|
||||
|
||||
# Exclude SWIG/C files
|
||||
global-exclude pybddisasm/*.c
|
||||
global-exclude pybddisasm/*.i
|
||||
|
||||
|
@ -11,30 +11,30 @@ python3 setup.py install
|
||||
|
||||
## Usage
|
||||
|
||||
Use it by importing the pybddisasm.disasm module:
|
||||
Use it by importing the pybddisasm module:
|
||||
|
||||
```python
|
||||
from pybddisasm.bddisasm import *
|
||||
import pybddisasm
|
||||
|
||||
instr = nd_decode_ex2(buff, arch, arch, arch, vendor, current_rip)
|
||||
ret, instr = pybddisasm.nd_decode_ex(code, def_code, def_data, def_stack)
|
||||
```
|
||||
|
||||
## Example
|
||||
|
||||
```python
|
||||
from pybddisasm.bddisasm import *
|
||||
from sys import *
|
||||
import pybddisasm
|
||||
|
||||
buff = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"
|
||||
code = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"
|
||||
offset = 0
|
||||
|
||||
while offset < getsizeof(buff):
|
||||
instr = nd_decode_ex2(buff[offset:], 64, 64, 64)
|
||||
while offset < len(code):
|
||||
_, instr = pybddisasm.nd_decode_ex(code[offset:], len(code[offset:]), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64)
|
||||
|
||||
if instr is None:
|
||||
break
|
||||
|
||||
print(instr.Text)
|
||||
_, text = pybddisasm.nd_to_text(instr, 0x0)
|
||||
print(text)
|
||||
|
||||
offset += instr.Length
|
||||
```
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include "pybddisasm.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
int nd_decode_ex(INSTRUX *instrux, void *code, size_t size, int def_code, int def_data)
|
||||
{
|
||||
return NdDecodeEx(instrux, code, size, def_code, def_data);
|
||||
}
|
||||
|
||||
|
||||
int nd_decode_ex2(INSTRUX *instrux, void *code, size_t size, int def_code, int def_data, int def_stack, int prefered_vendor)
|
||||
{
|
||||
return NdDecodeEx2(instrux, code, size, def_code, def_data, def_stack, prefered_vendor);
|
||||
}
|
||||
|
||||
|
||||
int nd_to_text(INSTRUX *instrux, size_t rip, size_t bufsize, char *buf)
|
||||
{
|
||||
return NdToText(instrux, rip, bufsize, buf);
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _PYBDDISASM_H_
|
||||
#define _PYBDDISASM_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bddisasm.h"
|
||||
|
||||
int nd_decode_ex(INSTRUX *instrux, void *code, size_t size, int def_code, int def_data);
|
||||
|
||||
int nd_decode_ex2(INSTRUX *instrux, void *code, size_t size, int def_code, int def_data, int def_stack, int prefered_vendor);
|
||||
|
||||
int nd_to_text(INSTRUX *instrux, size_t rip, size_t bufsize, char *buf);
|
||||
|
||||
#endif // _PYBDDISASM_H_
|
@ -1,7 +1,6 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# Copyright (c) 2023 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from pybddisasm.bddisasm import NdInstruction, nd_decode, nd_decode_ex, nd_decode_ex2, nd_to_text
|
||||
|
||||
from pybddisasm.helpers import disassemble_file, disassemble_hexstring, print_instruction
|
||||
from pybddisasm.pybddisasm import *
|
||||
|
@ -1,11 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
"""Early initialization and main entry point."""
|
||||
|
||||
from pybddisasm.core import main
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
@ -1,15 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
from pybddisasm.bddisasm import *
|
||||
from pybddisasm.helpers import *
|
||||
|
||||
|
||||
def run(args):
|
||||
if args.file:
|
||||
return disassemble_file(args.file, offset=args.offset, size=args.size, rip=args.rip,
|
||||
arch=args.arch, highlight=args.highlight, ext_info=args.ext_info)
|
||||
elif args.hexstring:
|
||||
return disassemble_hexstring(args.hexstring, offset=args.offset, size=args.size, rip=args.rip,
|
||||
arch=args.arch, highlight=args.highlight, ext_info=args.ext_info)
|
@ -1,71 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
"""Interface for disassembling code."""
|
||||
|
||||
import _pybddisasm
|
||||
|
||||
class NdInstruction(dict):
|
||||
"""Magic wrapper around a dictionary, that makes all the keys available
|
||||
through dot notation (recursively). Examples:
|
||||
|
||||
instruction['Length'] is now instruction.Length
|
||||
instruction['InstructionBytes'] is now instruction.InstructionBytes
|
||||
|
||||
You can still access the fileds the old way."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(NdInstruction, self).__init__(*args, **kwargs)
|
||||
self.__dict__ = self
|
||||
|
||||
# now set all the dict children to be the same
|
||||
for key in self.__dict__:
|
||||
if not isinstance(self.__dict__[key], dict):
|
||||
continue
|
||||
|
||||
self.__dict__[key] = NdInstruction(self.__dict__[key])
|
||||
|
||||
if 'Operands' in self.__dict__ and isinstance(self.Operands, dict):
|
||||
for op in self.Operands:
|
||||
self.Operands[op] = NdInstruction(self.Operands[op])
|
||||
|
||||
|
||||
def nd_to_text(code, arch_code_size, arch_data_size=0, rip=0):
|
||||
if not arch_data_size:
|
||||
arch_data_size = arch_code_size
|
||||
|
||||
return _pybddisasm.nd_to_text(code, arch_code_size, arch_data_size, rip)
|
||||
|
||||
|
||||
def nd_decode(code, arch_code_size, arch_data_size=0, rip=0):
|
||||
if not arch_data_size:
|
||||
arch_data_size = arch_code_size
|
||||
|
||||
instruction = _pybddisasm.nd_decode_ex(code, arch_code_size, arch_data_size, rip)
|
||||
|
||||
if not instruction:
|
||||
return None
|
||||
|
||||
return NdInstruction(instruction)
|
||||
|
||||
|
||||
def nd_decode_ex(code, arch_code_size, arch_data_size=0, rip=0):
|
||||
if not arch_data_size:
|
||||
arch_data_size = arch_code_size
|
||||
|
||||
instruction = _pybddisasm.nd_decode_ex(code, arch_code_size, arch_data_size, rip)
|
||||
|
||||
if not instruction:
|
||||
return None
|
||||
|
||||
return NdInstruction(instruction)
|
||||
|
||||
|
||||
def nd_decode_ex2(code, arch_code_size, arch_data_size, arch_stack_size, vendor='intel', rip=0):
|
||||
instruction = _pybddisasm.nd_decode_ex2(code, arch_code_size, arch_data_size,
|
||||
arch_stack_size, vendor, rip)
|
||||
|
||||
if not instruction:
|
||||
return None
|
||||
|
||||
return NdInstruction(instruction)
|
@ -1,52 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
"""Early initialization and main entry point."""
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
import pybddisasm
|
||||
|
||||
|
||||
def _auto_int(x):
|
||||
return int(x, 0)
|
||||
|
||||
|
||||
def _get_argparser():
|
||||
"""Get the argpase parser."""
|
||||
parser = argparse.ArgumentParser(prog='pybddisasm')
|
||||
|
||||
parser.add_argument('-b', '--arch', choices=[16, 32, 64], default=64, type=int)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument('-f', '--file', dest='file')
|
||||
group.add_argument('--hex', dest='hexstring', nargs='*')
|
||||
|
||||
parser.add_argument('-r', '--rip', dest='rip', default=0, type=_auto_int,
|
||||
help='Disassemble considering the given RIP.')
|
||||
parser.add_argument('--offset', dest='offset', default=0, type=_auto_int,
|
||||
help='Disassemble starting at the given offset. Only valid for --file option.')
|
||||
parser.add_argument('--size', dest='size', default=0, type=_auto_int,
|
||||
help='Disassemble only the given size. Only valid for --file option.')
|
||||
parser.add_argument('-c', '--highlight', dest='highlight', action='store_true',
|
||||
help='Color the instruction bytes by group (opcode, prefix, etc).')
|
||||
parser.add_argument('-e', '--ext-info', dest='ext_info', action='store_true')
|
||||
parser.add_argument('--vendor', dest='vendor',
|
||||
choices=['intel', 'amd', 'cyrix', 'geode', 'any'], default='any')
|
||||
|
||||
return parser
|
||||
|
||||
|
||||
def main():
|
||||
"""Parse the args and run the app."""
|
||||
parser = _get_argparser()
|
||||
|
||||
args = parser.parse_args(sys.argv[1:])
|
||||
|
||||
if not args.file and not args.hexstring:
|
||||
parser.print_usage()
|
||||
return 1
|
||||
|
||||
from pybddisasm import app
|
||||
app.run(args)
|
@ -1,156 +0,0 @@
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
|
||||
from pybddisasm.bddisasm import *
|
||||
|
||||
try:
|
||||
from termcolor import colored
|
||||
except:
|
||||
colored = None
|
||||
|
||||
|
||||
_SPACES = [
|
||||
'',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
' ',
|
||||
]
|
||||
|
||||
|
||||
def print_internal(string, foreground=None, highlight=True):
|
||||
no_colors = (foreground is None) or (not highlight)
|
||||
|
||||
if not colored or no_colors:
|
||||
sys.stdout.write(string)
|
||||
else:
|
||||
text = colored(string, foreground)
|
||||
sys.stdout.write(text)
|
||||
|
||||
|
||||
def print_instruction(instruction, rip, highlight=False, ext_info=False):
|
||||
k = 0
|
||||
|
||||
print_internal('%016x ' % rip)
|
||||
|
||||
# prefixes
|
||||
for ibyte in range(0, instruction.PrefLength):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte])
|
||||
|
||||
# opcodes
|
||||
k += instruction.PrefLength
|
||||
for ibyte in range(k, k + instruction.OpLength):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte], 'green', highlight)
|
||||
|
||||
# modrm and sib
|
||||
k += instruction.OpLength
|
||||
for ibyte in range(k, k + instruction.HasModRm + instruction.HasSib):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte], 'yellow', highlight)
|
||||
|
||||
# displacement
|
||||
k += instruction.HasModRm + instruction.HasSib
|
||||
for ibyte in range(k, k + instruction.DispLength):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte], 'blue', highlight)
|
||||
|
||||
# relative offset/moffset/immediates
|
||||
rest = instruction.Imm1Length + instruction.Imm2Length + instruction.RelOffsLength + \
|
||||
instruction.MoffsetLength + instruction.HasSseImm + instruction.AddrLength
|
||||
|
||||
k += instruction.DispLength
|
||||
for ibyte in range(k, k + rest):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte], 'red', highlight)
|
||||
|
||||
# the rest of the bytes
|
||||
k += rest
|
||||
for ibyte in range(k, instruction.Length):
|
||||
print_internal('%02x' % instruction.InstructionBytes[ibyte])
|
||||
|
||||
print_internal('%s' % _SPACES[16 - instruction.Length])
|
||||
print_internal('%s' % instruction.Text)
|
||||
|
||||
if ext_info:
|
||||
print_internal('\n')
|
||||
print_internal('%r' % instruction)
|
||||
|
||||
print_internal('\n')
|
||||
|
||||
|
||||
def disassemble_file(filepath, offset=0, size=0, rip=0, arch=64,
|
||||
highlight=True, vendor='any', ext_info=False):
|
||||
if not filepath:
|
||||
return
|
||||
|
||||
with open(filepath, 'rb') as f:
|
||||
total = 0
|
||||
file_size = os.path.getsize(filepath)
|
||||
|
||||
if not size:
|
||||
size = file_size
|
||||
|
||||
while offset < file_size and total < size:
|
||||
to_read = file_size - offset
|
||||
if to_read > 15:
|
||||
to_read = 15
|
||||
|
||||
f.seek(offset, 0)
|
||||
buff = f.read(to_read)
|
||||
|
||||
current_rip = rip + total
|
||||
|
||||
instr = nd_decode_ex2(buff, arch, arch, arch, vendor, current_rip)
|
||||
if instr:
|
||||
print_instruction(instr, current_rip, highlight, ext_info)
|
||||
offset += instr['Length']
|
||||
total += instr['Length']
|
||||
else:
|
||||
sys.stdout.write('%016x %02x %s db 0x%02x' % (current_rip, buff[0],
|
||||
_SPACES[15], buff[0]))
|
||||
|
||||
if str.isalpha(chr(buff[0])):
|
||||
sys.stdout.write(str(buff[0]))
|
||||
|
||||
sys.stdout.write('\n')
|
||||
|
||||
offset += 1
|
||||
total += 1
|
||||
|
||||
|
||||
def disassemble_hexstring(hexstring, offset=0, size=0, rip=0, arch=64,
|
||||
highlight=True, vendor='any', ext_info=False):
|
||||
if not hexstring:
|
||||
return
|
||||
|
||||
buff = bytes.fromhex(''.join(hexstring))
|
||||
|
||||
total = 0
|
||||
if not size:
|
||||
size = len(buff)
|
||||
|
||||
while total < size:
|
||||
current_rip = rip + total
|
||||
|
||||
instr = nd_decode_ex2(buff[total:total+16], arch, arch, arch, vendor, current_rip)
|
||||
if instr:
|
||||
print_instruction(instr, current_rip, highlight, ext_info)
|
||||
offset += instr['Length']
|
||||
total += instr['Length']
|
||||
else:
|
||||
sys.stdout.write('%016x %02x %s db 0x%02x\n' % (current_rip, buff[offset],
|
||||
_SPACES[15], buff[offset]))
|
||||
offset += 1
|
||||
total += 1
|
96
bindings/pybddisasm/pybddisasm/pybddisasm.c
Normal file
96
bindings/pybddisasm/pybddisasm/pybddisasm.c
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pybddisasm.h"
|
||||
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
|
||||
int nd_vsnprintf_s(char *buffer, size_t size, size_t count, const char *format, va_list argptr)
|
||||
{
|
||||
return vsnprintf(buffer, size, format, argptr);
|
||||
}
|
||||
|
||||
void * nd_memset(void *s, int c, size_t n)
|
||||
{
|
||||
return memset(s, c, n);
|
||||
}
|
||||
|
||||
void nd_get_version(ND_UINT32 *major, ND_UINT32 *minor, ND_UINT32 *revision, char **build_date, char **build_time)
|
||||
{
|
||||
NdGetVersion(major, minor, revision, build_date, build_time);
|
||||
}
|
||||
|
||||
NDSTATUS nd_decode(INSTRUX *__output_instr, const ND_UINT8 *code, ND_UINT8 def_code, ND_UINT8 def_data)
|
||||
{
|
||||
return NdDecode(__output_instr, code, def_code, def_data);
|
||||
}
|
||||
|
||||
NDSTATUS nd_decode_ex(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_UINT8 def_code, ND_UINT8 def_data)
|
||||
{
|
||||
return NdDecodeEx(__output_instr, code, size, def_code, def_data);
|
||||
}
|
||||
|
||||
NDSTATUS nd_decode_ex2(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_UINT8 def_code, ND_UINT8 def_data,
|
||||
ND_UINT8 def_stack, ND_UINT8 prefered_vendor)
|
||||
{
|
||||
return NdDecodeEx2(__output_instr, code, size, def_code, def_data, def_stack, prefered_vendor);
|
||||
}
|
||||
|
||||
NDSTATUS nd_decode_with_context(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_CONTEXT *context)
|
||||
{
|
||||
return NdDecodeWithContext(__output_instr, code, size, context);
|
||||
}
|
||||
|
||||
NDSTATUS nd_to_text(const INSTRUX *__input_instr, ND_UINT64 rip, ND_UINT32 buffer_size, char *__output_buffer)
|
||||
{
|
||||
return NdToText(__input_instr, rip, buffer_size, __output_buffer);
|
||||
}
|
||||
|
||||
ND_BOOL nd_is_instrux_rip_relative(const INSTRUX *__input_instr)
|
||||
{
|
||||
return NdIsInstruxRipRelative(__input_instr);
|
||||
}
|
||||
|
||||
NDSTATUS nd_get_full_access_map(const INSTRUX *__input_instr, ND_ACCESS_MAP *__output_access_map)
|
||||
{
|
||||
return NdGetFullAccessMap(__input_instr, __output_access_map);
|
||||
}
|
||||
|
||||
NDSTATUS nd_get_operand_rlut(const INSTRUX *__input_instr, ND_OPERAND_RLUT *__output_rlut)
|
||||
{
|
||||
return NdGetOperandRlut(__input_instr, __output_rlut);
|
||||
}
|
||||
|
||||
void nd_init_context(ND_CONTEXT *__input_context)
|
||||
{
|
||||
NdInitContext(__input_context);
|
||||
}
|
||||
|
||||
ND_UINT8 *nd_get_instrux_bytes(const INSTRUX *__input_instr, size_t *__output_convert_length)
|
||||
{
|
||||
*__output_convert_length = sizeof(__input_instr->InstructionBytes);
|
||||
return (ND_UINT8 *)__input_instr->InstructionBytes;
|
||||
}
|
||||
|
||||
ND_UINT8 *nd_get_intrux_op_code_bytes(const INSTRUX *__input_instr, size_t *__output_convert_length)
|
||||
{
|
||||
*__output_convert_length = sizeof(__input_instr->OpCodeBytes);
|
||||
return (ND_UINT8 *)__input_instr->OpCodeBytes;
|
||||
}
|
||||
|
||||
|
||||
ND_OPERAND *nd_get_intrux_operands(const INSTRUX *__input_instr, size_t *__output_convert_length)
|
||||
{
|
||||
*__output_convert_length = ARRAY_SIZE(__input_instr->Operands);
|
||||
return (ND_OPERAND *)__input_instr->Operands;
|
||||
}
|
25
bindings/pybddisasm/pybddisasm/pybddisasm.h
Normal file
25
bindings/pybddisasm/pybddisasm/pybddisasm.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#ifndef _PYBDDISASM_H_
|
||||
#define _PYBDDISASM_H_
|
||||
|
||||
#include "bddisasm.h"
|
||||
|
||||
void nd_get_version(ND_UINT32 *major, ND_UINT32 *minor, ND_UINT32 *revision, char **build_date, char **build_time);
|
||||
NDSTATUS nd_decode(INSTRUX *__output_instr, const ND_UINT8 *code, ND_UINT8 def_code, ND_UINT8 def_data);
|
||||
NDSTATUS nd_decode_ex(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_UINT8 def_code, ND_UINT8 def_data);
|
||||
NDSTATUS nd_decode_ex2(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_UINT8 def_code, ND_UINT8 def_data,
|
||||
ND_UINT8 def_stack, ND_UINT8 prefered_vendor);
|
||||
NDSTATUS nd_decode_with_context(INSTRUX *__output_instr, const ND_UINT8 *code, ND_SIZET size, ND_CONTEXT *context);
|
||||
NDSTATUS nd_to_text(const INSTRUX *__input_instr, ND_UINT64 rip, ND_UINT32 buffer_size, char *__output_buffer);
|
||||
ND_BOOL nd_is_instrux_rip_relative(const INSTRUX *__input_instr);
|
||||
NDSTATUS nd_get_full_access_map(const INSTRUX *__input_instr, ND_ACCESS_MAP *__output_access_map);
|
||||
NDSTATUS nd_get_operand_rlut(const INSTRUX *__input_instr, ND_OPERAND_RLUT *__output_rlut);
|
||||
void nd_init_context(ND_CONTEXT *__input_context);
|
||||
ND_UINT8 *nd_get_instrux_bytes(const INSTRUX *__input_instr, size_t *__output_convert_length);
|
||||
ND_UINT8 *nd_get_intrux_op_code_bytes(const INSTRUX *__input_instr, size_t *__output_convert_length);
|
||||
ND_OPERAND *nd_get_intrux_operands(const INSTRUX *__input_instr, size_t *__output_convert_length);
|
||||
|
||||
#endif // !_PYBDDISASM_H_
|
177
bindings/pybddisasm/pybddisasm/pybddisasm.i
Normal file
177
bindings/pybddisasm/pybddisasm/pybddisasm.i
Normal file
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright (c) 2023 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
%module (package="pybddisasm", moduleimport="import $module") pybddisasm
|
||||
%{
|
||||
#define SWIG_FILE_WITH_INIT
|
||||
|
||||
#include "pybddisasm.h"
|
||||
|
||||
#include "bddisasm.h"
|
||||
#include "constants.h"
|
||||
#include "cpuidflags.h"
|
||||
#include "disasmstatus.h"
|
||||
#include "disasmtypes.h"
|
||||
#include "registers.h"
|
||||
#include "version.h"
|
||||
|
||||
%}
|
||||
|
||||
%include "typemaps.i"
|
||||
%include "stdint.i"
|
||||
|
||||
%{
|
||||
#define ND_ARCH_X64
|
||||
static PyObject * __uint8_to_pylist(uint8_t *arr, size_t size)
|
||||
{
|
||||
PyObject *list = PyList_New(size);
|
||||
for (uint8_t idx = 0; idx < size; idx++) {
|
||||
PyObject *obj = PyInt_FromLong(arr[idx]);
|
||||
PyList_SetItem(list, idx, obj);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject * __nd_operand_to_pylist(ND_OPERAND *arr, size_t size)
|
||||
{
|
||||
PyObject *list = PyList_New(size);
|
||||
|
||||
for (uint8_t idx = 0; idx < size; idx++) {
|
||||
PyObject *obj = SWIG_NewPointerObj(&arr[idx], SWIGTYPE_p__ND_OPERAND, 0);
|
||||
PyList_SetItem(list, idx, obj);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
%}
|
||||
|
||||
%typemap(in,numinputs=0) ND_UINT32 * (ND_UINT32 tmp) %{
|
||||
$1 = &tmp;
|
||||
%}
|
||||
|
||||
%typemap(argout) ND_UINT32 * (PyObject* obj) %{
|
||||
obj = PyLong_FromLong(*$1);
|
||||
$result = SWIG_Python_AppendOutput($result,obj);
|
||||
%}
|
||||
|
||||
%typemap(freearg) ND_UINT32 * %{
|
||||
|
||||
%}
|
||||
|
||||
%typemap(in,numinputs=0) char** (char* tmp) %{
|
||||
$1 = &tmp;
|
||||
%}
|
||||
|
||||
%typemap(argout) char** (PyObject* obj) %{
|
||||
obj = PyUnicode_FromString(*$1);
|
||||
$result = SWIG_Python_AppendOutput($result,obj);
|
||||
%}
|
||||
|
||||
%typemap(freearg) char** %{
|
||||
|
||||
%}
|
||||
|
||||
|
||||
%typemap(in, numinputs = 0) (ND_UINT32 buffer_size, char *__output_buffer) {
|
||||
$1 = ND_MIN_BUF_SIZE;
|
||||
$2 = (char *)malloc($1 * sizeof(char));
|
||||
}
|
||||
|
||||
%typemap(freearg) (ND_UINT32 buffer_size, char *__output_buffer) {
|
||||
free($2);
|
||||
}
|
||||
|
||||
%typemap(argout) char *__output_buffer {
|
||||
// TODO: check if function failed
|
||||
PyObject *o, *o2, *o3;
|
||||
o = PyString_FromString($1);
|
||||
if ((!$result) || ($result == Py_None)) {
|
||||
$result = o;
|
||||
} else {
|
||||
if (!PyTuple_Check($result)) {
|
||||
PyObject *o2 = $result;
|
||||
$result = PyTuple_New(1);
|
||||
PyTuple_SetItem($result,0,o2);
|
||||
}
|
||||
o3 = PyTuple_New(1);
|
||||
PyTuple_SetItem(o3,0,o);
|
||||
o2 = $result;
|
||||
$result = PySequence_Concat(o2,o3);
|
||||
Py_DECREF(o2);
|
||||
Py_DECREF(o3);
|
||||
}
|
||||
}
|
||||
|
||||
%typemap(in, numinputs=0) INSTRUX * __output_instr {
|
||||
$1 = malloc(sizeof(INSTRUX));
|
||||
|
||||
}
|
||||
|
||||
%typemap(freearg) INSTRUX * __output_instr {
|
||||
}
|
||||
|
||||
%typemap(argout) INSTRUX * __output_instr {
|
||||
%append_output(SWIG_NewPointerObj($1,$1_descriptor,SWIG_POINTER_OWN));
|
||||
}
|
||||
|
||||
%typemap(in,numinputs=0,noblock=1) size_t *__output_convert_length {
|
||||
size_t tmp_len;
|
||||
$1 = &tmp_len;
|
||||
}
|
||||
|
||||
%typemap(out) ND_UINT8 * {
|
||||
size_t size = tmp_len;
|
||||
$result = __uint8_to_pylist($1, size);
|
||||
}
|
||||
|
||||
%typemap(out) ND_OPERAND * {
|
||||
size_t size = tmp_len;
|
||||
$result = __nd_operand_to_pylist($1, size);
|
||||
}
|
||||
|
||||
%typemap(in, numinputs = 1) (const ND_UINT8 *) {
|
||||
Py_buffer view;
|
||||
|
||||
if (!(PyObject_CheckBuffer($input) || PyBytes_Check($input) || PyByteArray_Check($input))) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument "
|
||||
"$argnum"" of type '" "$type""'");
|
||||
}
|
||||
|
||||
if (PyObject_GetBuffer($input, &view, 0) != 0) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument "
|
||||
"$argnum"" of type '" "$type""'");
|
||||
}
|
||||
|
||||
$1 = (uint8_t *)malloc(sizeof(uint8_t) * view.len);
|
||||
if (!($1)) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument "
|
||||
"$argnum"" of type '" "$type""'");
|
||||
}
|
||||
|
||||
if (PyBuffer_ToContiguous($1, &view, view.len, 'C') != 0) {
|
||||
SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument "
|
||||
"$argnum"" of type '" "$type""'");
|
||||
}
|
||||
|
||||
PyBuffer_Release(&view);
|
||||
}
|
||||
|
||||
%typemap(freearg) (const ND_UINT8 *) {
|
||||
free($1);
|
||||
}
|
||||
|
||||
%define __x86_64__
|
||||
%enddef
|
||||
|
||||
|
||||
%include "bddisasm.h"
|
||||
%include "constants.h"
|
||||
%include "cpuidflags.h"
|
||||
%include "disasmstatus.h"
|
||||
%include "disasmtypes.h"
|
||||
%include "registers.h"
|
||||
%include "version.h"
|
||||
|
||||
%include "pybddisasm.h"
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (c) 2020 Bitdefender
|
||||
# Copyright (c) 2023 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
@ -11,16 +11,32 @@ import re
|
||||
from setuptools import find_packages, setup, Command, Extension, Distribution
|
||||
from codecs import open
|
||||
|
||||
VERSION = (0, 1, 3)
|
||||
VERSION = (0, 3, 0)
|
||||
LIBRARY_VERSION = (1, 37, 0)
|
||||
LIBRARY_INSTRUX_SIZE = 856
|
||||
DIR_INCLUDE = '../../inc'
|
||||
|
||||
packages = ['pybddisasm']
|
||||
requires = ['setuptools']
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
def _check_library_version():
|
||||
version_header = '../../inc/version.h'
|
||||
__swig_opts = ['-I%s' % (DIR_INCLUDE)]
|
||||
__extra_compile_args = ['-march=westmere']
|
||||
__sources = ['pybddisasm/pybddisasm.c', 'pybddisasm/pybddisasm.i']
|
||||
__libraries = ['bddisasm']
|
||||
__library_dirs = ['../../build', '../../bin/x64/Release']
|
||||
__packages = ['pybddisasm']
|
||||
__requires = ['setuptools']
|
||||
|
||||
|
||||
class BinaryDistribution(Distribution):
|
||||
def has_ext_modules(arg):
|
||||
return True
|
||||
|
||||
def is_pure(self):
|
||||
return False
|
||||
|
||||
|
||||
def __fn_validate_compatibility():
|
||||
print(os.getcwd())
|
||||
version_header = '%s/version.h' % (DIR_INCLUDE)
|
||||
with open(version_header, 'r') as file:
|
||||
data = file.read()
|
||||
|
||||
@ -46,21 +62,15 @@ def _check_library_version():
|
||||
if int(major) != LIBRARY_VERSION[0] or int(minor) != LIBRARY_VERSION[1] or int(revision) != LIBRARY_VERSION[2]:
|
||||
print('error: The version of the library is not compatible with the pybddisasm!')
|
||||
print('error: Library : %s.%s.%s - pybddisasm : %d.%d.%d' % (major, minor, revision, LIBRARY_VERSION[0],
|
||||
LIBRARY_VERSION[1], LIBRARY_VERSION[2]))
|
||||
LIBRARY_VERSION[1], LIBRARY_VERSION[2]))
|
||||
sys.exit(1)
|
||||
|
||||
_check_library_version()
|
||||
|
||||
__fn_validate_compatibility()
|
||||
|
||||
with open('README.md', 'r', 'utf-8') as f:
|
||||
readme = f.read()
|
||||
|
||||
class BinaryDistribution(Distribution):
|
||||
def has_ext_modules(arg):
|
||||
return True
|
||||
|
||||
def is_pure(self):
|
||||
return False
|
||||
|
||||
setup(
|
||||
name="pybddisasm",
|
||||
version='.'.join(map(str, VERSION)),
|
||||
@ -69,31 +79,34 @@ setup(
|
||||
long_description = readme,
|
||||
long_description_content_type = "text/markdown",
|
||||
url = "https://github.com/bitdefender/bddisasm",
|
||||
packages=packages,
|
||||
license="Apache Software License",
|
||||
package_data={'': ['LICENSE', 'NOTICE'], 'pybddisasm': ['*.pem']},
|
||||
package_data={'': ['LICENSE', 'NOTICE'], 'pybddisasm': ['pybddisasm/pybddisasm.py']},
|
||||
package_dir={'pybddisasm': 'pybddisasm'},
|
||||
platforms = ["Windows", "Linux"],
|
||||
packages=__packages,
|
||||
include_package_data=True,
|
||||
py_modules = ["pybddisasm"],
|
||||
python_requires=">=3.5",
|
||||
setup_requires=['wheel'],
|
||||
install_requires=requires,
|
||||
install_requires=__requires,
|
||||
zip_safe=False,
|
||||
classifiers=[
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3.7',
|
||||
'Programming Language :: Python :: 3.8',
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'License :: OSI Approved :: Apache Software License',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Operating System :: POSIX :: Linux'
|
||||
],
|
||||
ext_modules = [Extension("_pybddisasm",
|
||||
extra_compile_args = ["-march=westmere"],
|
||||
sources = ["_pybddisasm/_pybddisasm.c", "_pybddisasm/pybddisasm.c"],
|
||||
define_macros = [('AMD64', None), ('LIBRARY_INSTRUX_SIZE', LIBRARY_INSTRUX_SIZE)],
|
||||
include_dirs = ['../../inc'],
|
||||
libraries = ['bddisasm'],
|
||||
library_dirs = ['/usr/local/lib', '../../build', '../../bin/x64/Release'])],
|
||||
swig_opts = __swig_opts,
|
||||
extra_compile_args = __extra_compile_args,
|
||||
sources = __sources,
|
||||
define_macros = [('AMD64', None)],
|
||||
include_dirs = [DIR_INCLUDE],
|
||||
libraries = __libraries,
|
||||
library_dirs = __library_dirs)],
|
||||
distclass=BinaryDistribution
|
||||
)
|
||||
|
125
bindings/pybddisasm/tests/nd_decode.py
Normal file
125
bindings/pybddisasm/tests/nd_decode.py
Normal file
@ -0,0 +1,125 @@
|
||||
#
|
||||
# Copyright (c) 2023 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import sys
|
||||
import unittest
|
||||
import pybddisasm
|
||||
|
||||
class TestNdDecode(unittest.TestCase):
|
||||
def test_nd_decode_bytes(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = b"\x48\x8b\x05\xb8\x13\x00\x00"
|
||||
ret, instrux = pybddisasm.nd_decode(instr_bytes, pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, len(instr_bytes)):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_decode_bytes_array(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00')
|
||||
ret, instrux = pybddisasm.nd_decode(instr_bytes, pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
|
||||
def test_nd_decode_memory_view(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = memoryview(bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00'))
|
||||
ret, instrux = pybddisasm.nd_decode(instr_bytes, pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_decode_ex_bytes(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = b"\x48\x8b\x05\xb8\x13\x00\x00"
|
||||
ret, instrux = pybddisasm.nd_decode_ex(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_decode_ex_bytes_array(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00')
|
||||
ret, instrux = pybddisasm.nd_decode_ex(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
|
||||
def test_nd_decode_ex_memory_view(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = memoryview(bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00'))
|
||||
ret, instrux = pybddisasm.nd_decode_ex(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_decode_ex2_bytes(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = b"\x48\x8b\x05\xb8\x13\x00\x00"
|
||||
ret, instrux = pybddisasm.nd_decode_ex2(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64,
|
||||
pybddisasm.ND_STACK_64, pybddisasm.ND_VEND_INTEL);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_decode_ex2_bytes_array(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00')
|
||||
ret, instrux = pybddisasm.nd_decode_ex2(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64,
|
||||
pybddisasm.ND_STACK_64,pybddisasm.ND_VEND_INTEL);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
|
||||
def test_nd_decode_ex2_memory_view(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = memoryview(bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00'))
|
||||
ret, instrux = pybddisasm.nd_decode_ex2(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64,
|
||||
pybddisasm.ND_STACK_64,pybddisasm.ND_VEND_INTEL);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
for idx in range(0, instrux.Length):
|
||||
self.assertEqual(pybddisasm.nd_get_instrux_bytes(instrux)[idx], instr_bytes[idx])
|
||||
|
||||
def test_nd_rip_relative(self):
|
||||
# mov rax,QWORD PTR [rip+0x13b8]
|
||||
instr_bytes = memoryview(bytearray(b'\x48\x8b\x05\xb8\x13\x00\x00'))
|
||||
ret, instrux = pybddisasm.nd_decode_ex2(instr_bytes, len(instr_bytes), pybddisasm.ND_CODE_64, pybddisasm.ND_DATA_64,
|
||||
pybddisasm.ND_STACK_64,pybddisasm.ND_VEND_INTEL);
|
||||
|
||||
self.assertIsInstance(ret, int, "'return-value' is not instance of 'int'")
|
||||
self.assertEqual(ret, 0, "'return-value' is not equal to 0")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
26
bindings/pybddisasm/tests/nd_get_version.py
Normal file
26
bindings/pybddisasm/tests/nd_get_version.py
Normal file
@ -0,0 +1,26 @@
|
||||
#
|
||||
# Copyright (c) 2023 Bitdefender
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
import pybddisasm
|
||||
|
||||
class TestNdGetVersion(unittest.TestCase):
|
||||
def test_nd_get_version_exception(self):
|
||||
self.assertRaises(Exception, pybddisasm.nd_get_version())
|
||||
|
||||
def test_nd_get_version_output(self):
|
||||
major, minor, revision, build_date, build_time = pybddisasm.nd_get_version()
|
||||
|
||||
print("\nversion = %ld.%ld.%ld (%s, %s)\n" % (major, minor, revision, build_date, build_time))
|
||||
|
||||
self.assertIsInstance(major, int, "'Major' is not instance of 'int'")
|
||||
self.assertIsInstance(minor, int, "'Minor' is not instance of 'int'")
|
||||
self.assertIsInstance(revision, int, "'Revision' is not instance of 'int'")
|
||||
self.assertIsInstance(build_date, str, "'BuildDate' is not instance of 'str'")
|
||||
self.assertIsInstance(build_time, str, "'BuildDate' is not instance of 'str'")
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user