-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathpyhiew_decompile.py
More file actions
155 lines (128 loc) · 4.84 KB
/
Copy pathpyhiew_decompile.py
File metadata and controls
155 lines (128 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# Copyright (c) 2024-2026 Elias Bachaalany
# SPDX-License-Identifier: LicenseRef-Human-Origin-Source-1.0
#
# This file is licensed under the Human-Origin Source License v1.0.
# See LICENSE.
"""
Decompile - Offline decompilation via libghidra for PyHiew
Decompiles at the current cursor position using libghidra's offline
Sleigh decompiler engine. No Ghidra JVM required.
Features:
- Auto-detects architecture from PE/ELF/Mach-O headers
- Decompiles at cursor offset
- Displays pseudocode in a scrollable Hiew window
- Caches the LocalClient across invocations for speed
- F2: copy pseudocode to clipboard
Requirements:
pip install libghidra (the wheel includes the native decompiler)
pip install pyperclip (optional, for clipboard support)
"""
from __future__ import annotations
import hiew
class Decompiler:
"""Persistent decompiler state across Hiew invocations."""
def __init__(self):
self._client = None
self._opened_file: str | None = None
self._last_pseudocode: str = ""
self._window = hiew.Window()
def _ensure_client(self, filepath: str) -> bool:
"""Open the binary in LocalClient, reusing if same file."""
if self._client is not None and self._opened_file == filepath:
return True
try:
import libghidra
except ImportError:
hiew.Message("Decompile", "libghidra not installed.\npip install libghidra")
return False
# Close previous if different file
if self._client is not None:
try:
self._client.close_program()
except Exception:
pass
self._client = None
self._opened_file = None
try:
client = libghidra.local()
client.open_program(filepath)
self._client = client
self._opened_file = filepath
return True
except ImportError:
hiew.Message(
"Decompile",
"Native extension not available.\n\n"
"Install the libghidra wheel:\n"
" pip install libghidra-0.0.3-cp312-abi3-win_amd64.whl"
)
return False
except Exception as e:
hiew.Message("Decompile", f"Failed to open binary:\n{e}")
return False
def _decompile(self, address: int) -> str | None:
"""Decompile at address, return pseudocode or error string."""
if self._client is None:
return None
try:
resp = self._client.get_decompilation(address)
if resp.decompilation and resp.decompilation.pseudocode:
return resp.decompilation.pseudocode
elif resp.decompilation and resp.decompilation.error_message:
return f"// Error: {resp.decompilation.error_message}"
return "// No decompilation result"
except Exception as e:
return f"// Decompilation failed: {e}"
def _copy_to_clipboard(self) -> None:
"""Copy last pseudocode to clipboard."""
if not self._last_pseudocode:
return
try:
import pyperclip
pyperclip.copy(self._last_pseudocode)
hiew.Message("Decompile", "Copied to clipboard.")
except ImportError:
hiew.Message("Decompile", "pyperclip not installed.\npip install pyperclip")
def run(self) -> None:
"""Main plugin entry — called each time user invokes the script."""
data = hiew.GetData()
filepath = data.filename
offset = data.offsetCurrent
if not self._ensure_client(filepath):
return
pseudocode = self._decompile(offset)
if pseudocode is None:
hiew.Message("Decompile", "Decompilation returned no result.")
return
self._last_pseudocode = pseudocode
# Display in a window
lines = pseudocode.split("\n")
title = f"Decompile @ 0x{offset:X}"
width = min(max(max((len(line) for line in lines), default=40) + 4, 40), 120)
self._window.Create(
title=title,
lines=lines,
width=width,
main_keys={2: "Copy"},
)
while True:
_, key = self._window.Show()
if key == hiew.HEM_FNKEY_F2:
self._copy_to_clipboard()
continue
break
# ---------------------------------------------------------------------------
# Module-level instance (persists across invocations in same Hiew session)
_decompiler: Decompiler | None = None
def DecompileMain() -> None:
global _decompiler
if _decompiler is None:
_decompiler = Decompiler()
_decompiler.run()
try:
DecompileMain()
except SystemExit:
pass
except Exception:
import traceback
hiew.Window.FromString("Decompile Error", traceback.format_exc(), width=80)