Skip to content

Commit

Permalink
Add Android and iOS DBI framework for dynamic analysis of ELF and Mac…
Browse files Browse the repository at this point in the history
…ho-O binaries
  • Loading branch information
iamtorsten committed Sep 3, 2024
1 parent 3ff9892 commit 303e611
Show file tree
Hide file tree
Showing 44 changed files with 570,604 additions and 16 deletions.
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@

**SherlockElf** is a powerful tool designed for both static and dynamic analysis of Android ELF binaries and dynamic analysis of iOS Macho-O binaries (experimental). It helps security researchers, developers, and reverse engineers gain insights into ELF (Executable and Linkable Format) binaries used in Android applications and Mach-O (Mach Object) binaries used in iOS applications.
<br>

**Emulator:**

<p align="center">
<img src="assets/Emu.gif" alt="Emu"/>
</p>

**Dynamic Binary Instrumentor:**

<p align="center">
<img src="assets/Instrumentor.png" alt="Emu"/>
</p>

## Features ✨

- **Static Analysis**: Extracts and analyzes metadata, headers, and sections from ELF binaries.
Expand Down Expand Up @@ -44,7 +53,7 @@ Using SherlockElf is straightforward. Below are some common commands and their d

- **Static Analysis**:
```bash
python emulate.py
python emulator.py
```
This command performs a static analysis on the specified ELF binary and outputs the results.
<br><br>
Expand Down
Binary file added assets/Instrumentor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added dbi/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions dbi/android_disassembler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# Android Disassembler
# (c) 2024 Torsten Klement, [email protected]
# MIT

import lief

from capstone import Cs, CS_ARCH_ARM64, CS_MODE_ARM


class AndroidDisassembler:
def __init__(self, disassembly_area, function_list, function_positions):
self.disassembly_area = disassembly_area
self.function_list = function_list
self.function_positions = function_positions
self.recognized_functions = set() # Set zur Vermeidung doppelter Funktionen

def process_file(self, file_path):
elf = lief.parse(file_path)

self.insert_text(f"Type: {elf.header.file_type.name}\n", "black")
self.insert_text(f"Architecture: {elf.header.machine_type.name}\n\n", "black")

self.insert_text("Libraries:\n", "black")
for lib in elf.libraries:
self.insert_text(f"{lib}\n", "black")

self.insert_text("\nSegments:\n", "black")
for segment in elf.segments:
perm = f"{'r' if segment.has(lief.ELF.SEGMENT_FLAGS.R) else '-'}" + \
f"{'w' if segment.has(lief.ELF.SEGMENT_FLAGS.W) else '-'}" + \
f"{'x' if segment.has(lief.ELF.SEGMENT_FLAGS.X) else '-'}"
self.insert_text(
f"{perm} 0x{segment.virtual_address:08x}-0x{segment.virtual_address + segment.virtual_size:08x}\n",
"black")

self.insert_text("\n")

for section in elf.sections:
section_type = section.type.name if section.type else "UNKNOWN"
self.insert_text(
f"0x{section.virtual_address:08x}-0x{section.virtual_address + section.size:08x} {section.name} ({section_type}) "
f"{{{'Code' if section.flags == lief.ELF.SECTION_FLAGS.EXECINSTR else 'Read-only data' if section.flags == lief.ELF.SECTION_FLAGS.ALLOC else 'Writable data'}}}\n",
"black")
self.insert_text(
" " + " ".join(f"{byte:02x}" for byte in section.content[:64]) + "\n",
"black")

self.insert_text("\n\n\n")

# Disassemblieren der .text Sektion
text_section = elf.get_section(".text")
if text_section is not None:
base_address = text_section.virtual_address
segment_offset = self.get_segment_base_address(elf, base_address)

code = text_section.content
md = Cs(CS_ARCH_ARM64, CS_MODE_ARM)
md.detail = True

in_function = False
current_function_instructions = []
instructions = list(md.disasm(bytes(code), base_address))

for i in range(len(instructions)):
insn = instructions[i]
current_function_instructions.append(insn)

if self.is_potential_function_start(insn):
if not in_function and current_function_instructions:
if current_function_instructions:
return_type, args = self.analyze_function_signature(current_function_instructions)
function_name = f"sub_{instructions[i - 1].address:x}"

# Vermeidung doppelter Funktionen
if function_name in self.recognized_functions:
continue

self.recognized_functions.add(function_name)

start_line = self.get_current_index()
self.function_positions[function_name] = start_line
self.function_list.insert('end', function_name)
self.insert_text(
f"0x{instructions[i - 1].address:08x} {return_type} {function_name}({', '.join(args)})\n",
"function_start")
for instr in current_function_instructions:
self.insert_text(
f"0x{instr.address:08x} {' '.join(f'{b:02x}' for b in instr.bytes):<12} {instr.mnemonic:<7} {instr.op_str}\n",
"asm_code")
self.insert_text("\n")

in_function = True
current_function_instructions = [insn]
elif insn.mnemonic == "ret":
if in_function:
return_type, args = self.analyze_function_signature(current_function_instructions)
function_name = f"sub_{insn.address:x}"

# Vermeidung doppelter Funktionen
if function_name in self.recognized_functions:
continue

self.recognized_functions.add(function_name)

start_line = self.get_current_index()
self.function_positions[function_name] = start_line
self.function_list.insert('end', function_name)
self.insert_text(
f"0x{insn.address:08x} {return_type} {function_name}({', '.join(args)})\n",
"function_start")
for instr in current_function_instructions:
self.insert_text(
f"0x{instr.address:08x} {' '.join(f'{b:02x}' for b in instr.bytes):<12} {instr.mnemonic:<7} {instr.op_str}\n",
"asm_code")
self.insert_text("\n")

in_function = False
current_function_instructions = []

def analyze_function_signature(self, instructions):
args = []
return_type = "void"

arg_registers = ['x0', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7']
register_to_type = {reg: None for reg in arg_registers}

for insn in instructions:
if insn.mnemonic == 'mov' and insn.op_str == 'x0':
return_type = "int"
elif insn.mnemonic == 'ldr' and 'x0' in insn.op_str:
return_type = "pointer"

for reg in arg_registers:
if reg in insn.op_str:
if 'ldr' in insn.mnemonic:
register_to_type[reg] = "pointer"
elif 'mov' in insn.mnemonic or 'add' in insn.mnemonic:
register_to_type[reg] = "int32_t"
elif 'str' in insn.mnemonic:
register_to_type[reg] = "int32_t*"

for reg in arg_registers:
reg_type = register_to_type[reg]
if reg_type:
args.append(f"{reg_type} arg{arg_registers.index(reg) + 1}")

if not args:
args.append("void")

return return_type, args

def get_segment_base_address(self, elf, section_va):
for segment in elf.segments:
if segment.virtual_address <= section_va < (segment.virtual_address + segment.virtual_size):
return segment.virtual_address
return 0

def is_potential_function_start(self, insn):
if insn.mnemonic in {'bl', 'blr', 'b', 'br', 'cbz', 'cbnz', 'tbnz', 'tbz'}:
return True
if insn.mnemonic == 'adrp' and ('x' in insn.op_str):
return True
if insn.mnemonic == 'stp' and ('x29, x30' in insn.op_str):
return True
return False

def insert_text(self, text, tag=None):
self.disassembly_area.insert('end', text, tag)

def get_current_index(self):
return self.disassembly_area.index('end')
92 changes: 92 additions & 0 deletions dbi/code_editor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import os

import tkinter as tk
from tkinter import filedialog, messagebox, simpledialog
import tkinter.scrolledtext as scrolledtext

class CodeEditor:
def __init__(self, root):
self.root = root
self.filename = None

# Erstelle das Editor-Fenster
self.editor_window = tk.Toplevel(root)
self.editor_window.title("Code Editor")
self.editor_window.geometry("800x600")

# Erstelle das Textfeld mit Scrollbalken und Tabs auf 0,5 cm
self.text_area = scrolledtext.ScrolledText(self.editor_window, wrap=tk.WORD, undo=True, tabs=("0.5c"))
self.text_area.pack(fill=tk.BOTH, expand=True)

# Erstelle das Menü
self.menu = tk.Menu(self.editor_window)
self.editor_window.config(menu=self.menu)

# Datei-Menü
file_menu = tk.Menu(self.menu, tearoff=0)
self.menu.add_cascade(label="File", menu=file_menu)
file_menu.add_command(label="Open", command=self.open_file)
file_menu.add_command(label="Save", command=self.save_file)
file_menu.add_command(label="Save As", command=self.save_file_as)
file_menu.add_separator()
file_menu.add_command(label="Exit", command=self.editor_window.destroy)

# Bearbeiten-Menü
edit_menu = tk.Menu(self.menu, tearoff=0)
self.menu.add_cascade(label="Edit", menu=edit_menu)
edit_menu.add_command(label="Undo", command=self.text_area.edit_undo)
edit_menu.add_command(label="Redo", command=self.text_area.edit_redo)
edit_menu.add_separator()
edit_menu.add_command(label="Find", command=self.find_text)
edit_menu.add_command(label="Replace", command=self.replace_text)

def open_file(self):
self.filename = filedialog.askopenfilename(
defaultextension=".txt",
filetypes=[("Python Files", "*.py"), ("JavaScript Files", "*.js"), ("All Files", "*.*")]
)
if self.filename:
with open(self.filename, "r") as file:
content = file.read()
self.text_area.delete(1.0, tk.END)
self.text_area.insert(tk.INSERT, content)
self.editor_window.title(f"Code Editor - {os.path.normpath(self.filename)}")

def save_file(self):
if self.filename:
with open(self.filename, "w") as file:
file.write(self.text_area.get(1.0, tk.END))
else:
self.save_file_as()

def save_file_as(self):
self.filename = filedialog.asksaveasfilename(
defaultextension=".txt",
filetypes=[("Python Files", "*.py"), ("JavaScript Files", "*.js"), ("All Files", "*.*")]
)
if self.filename:
with open(self.filename, "w") as file:
file.write(self.text_area.get(1.0, tk.END))
self.editor_window.title(f"Code Editor - {os.path.normpath(self.filename)}")

def find_text(self):
search_term = simpledialog.askstring("Find", "Enter text to find:")
if search_term:
start_pos = "1.0"
while True:
start_pos = self.text_area.search(search_term, start_pos, stopindex=tk.END)
if not start_pos:
break
end_pos = f"{start_pos}+{len(search_term)}c"
self.text_area.tag_add("highlight", start_pos, end_pos)
self.text_area.tag_config("highlight", background="yellow", foreground="black")
start_pos = end_pos

def replace_text(self):
search_term = simpledialog.askstring("Find", "Enter text to replace:")
replace_term = simpledialog.askstring("Replace", "Enter replacement text:")
if search_term and replace_term:
content = self.text_area.get(1.0, tk.END)
new_content = content.replace(search_term, replace_term)
self.text_area.delete(1.0, tk.END)
self.text_area.insert(1.0, new_content)
Loading

0 comments on commit 303e611

Please sign in to comment.