Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@

Phrank helps with structure analysis and function pointers. Phrank works on top of HexRays ctrees.

## Installation
## Installation (IDA 9.x)

1) Copy/link phrank_plugin.py to IDAPRO/plugins/
2) Copy/link pyphrank and phrank.py to IDAPRO/python/3/ folder
- 사용자 설치(권장): `C:\Users\<User>\AppData\Roaming\Hex-Rays\IDA Pro\plugins`
- 전역 설치(관리자): `C:\Program Files\IDA Pro 9.x\plugins`

다음 파일/폴더를 같은 `plugins` 폴더 바로 아래에 둡니다.
- `phrank.py`
- `pyphrank/` 전체 폴더

참고: IDA 9에서는 `python/3` 경로가 필요하지 않습니다. `phrank.py`가 플러그인 엔트리(PLUGIN_ENTRY)를 제공합니다.

## Capabilities

Expand All @@ -21,4 +27,18 @@ Phrank helps with structure analysis and function pointers. Phrank works on top

## How to use

There are currently two ways to use phrank: hotkey actions (described [here](https://github.com/Mizari/phrank/wiki/Phrank-plugin-and-actions) and in comments [here](https://github.com/Mizari/phrank/blob/master/phrank_plugin.py)) and phrank api (described in docstring comments [here](https://github.com/Mizari/phrank/blob/master/phrank.py)).
There are two ways to use phrank:
1) Hex-Rays pseudocode popup menu: right-click in pseudocode and use the "Phrank" submenu.
2) Hotkeys and API: hotkeys below, and phrank API documented in `phrank.py` docstrings.

### Keyboard Shortcuts

Phrank provides the following keyboard shortcuts:
- `Shift-A`: Analyze item under cursor and its dependencies
- `Alt-T`: Print TypeFlowGraph for variable/function under cursor

### Context Menu (Hex-Rays)

In pseudocode view (F5), right-click to open the popup menu:
- Phrank → Analyze item under cursor and its dependencies
- Phrank → Print TypeFlowGraph for variable/function under cursor
9 changes: 7 additions & 2 deletions phrank.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def phrank_help():
funcs = {}
modules = {}
classes = {}
skips = {"sys", "idaapi", "typing", "ida_struct", "re", "logging", "idautils", "idc", "Any", "utils"}
skips = {"sys", "idaapi", "typing", "re", "logging", "idautils", "idc", "Any", "utils"}
for k, v in vars(mod).items():
if k.startswith("__"): continue
if k in skips:
Expand All @@ -99,4 +99,9 @@ def phrank_help():

__help_objects("MODULES", modules)
__help_objects("CLASSES", classes)
__help_objects("FUNCTIONS", funcs)
__help_objects("FUNCTIONS", funcs)


def PLUGIN_ENTRY():
"""IDA plugin entry point when this file is loaded as a plugin."""
return get_plugin_instance()
5 changes: 0 additions & 5 deletions phrank_plugin.py

This file was deleted.

21 changes: 12 additions & 9 deletions pyphrank/cfunction_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,18 @@ def get_cfunc(self, func_ea:int) -> idaapi.cfunc_t|None:
cfunc = self.cached_cfuncs.get(func_ea)
if isinstance(cfunc, idaapi.cfunc_t):
return cfunc
# Check if it's our sentinel value (-1) indicating a failed decompilation
if isinstance(cfunc, int) and cfunc == -1:
return None

if not settings.DECOMPILE_RECURSIVELY:
cfunc = utils.decompile_function(func_ea)
# -1 (instead of None) to cache failed decompilation
if cfunc is None:
cfunc = -1
self.cached_cfuncs[func_ea] = cfunc
if cfunc == -1:
self.cached_cfuncs[func_ea] = -1
return None
else:
return cfunc
self.cached_cfuncs[func_ea] = cfunc
return cfunc

decompilation_queue = [func_ea]
while len(decompilation_queue) != 0:
Expand All @@ -59,15 +60,17 @@ def get_cfunc(self, func_ea:int) -> idaapi.cfunc_t|None:
if len(new_functions_to_decompile) == 0:
cfunc = utils.decompile_function(func_ea)
if cfunc is None:
cfunc = -1
self.cached_cfuncs[func_ea] = cfunc
self.cached_cfuncs[func_ea] = -1
else:
self.cached_cfuncs[func_ea] = cfunc
decompilation_queue.pop()
else:
decompilation_queue += list(new_functions_to_decompile)

cfunc = self.cached_cfuncs.get(func_ea)
if cfunc == -1:
cfunc = None
# Check if it's our sentinel value (-1) indicating a failed decompilation
if isinstance(cfunc, int) and cfunc == -1:
return None
return cfunc

def clear_cfunc(self, func_ea:int) -> None:
Expand Down
Loading