Skip to content

Commit

Permalink
Merge branch 'thalium'
Browse files Browse the repository at this point in the history
  • Loading branch information
Mixaill committed Jul 23, 2020
2 parents d4fa400 + 137f566 commit 5450b1b
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 86 deletions.
47 changes: 29 additions & 18 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,42 @@

Tool for PDB generation from IDA Pro database

Inspired by:
* pe_debug http://pefrm-units.osdn.jp/pe_debug.html

Based on:
* LLVM project https://llvm.org/
* LLD project https://lld.llvm.org/

## How to compile
Supports IDA >= 7.0

* run `<repository_root>/build.ps1`
## How to get

* grab `fakepdb.zip` from `<repository_root>/~build/deploy`
* Download latest release from release page: https://github.com/Mixaill/FakePDB/releases
* Or compile it from sources:
* run `<repository_root>/build.ps1`
* grab `fakepdb.zip` from `<repository_root>/~build/deploy`

## How to install

* copy content of `fakepdb.zip` to `<IDA_directory>/plugins`

## How to use

### 1. Export information from IDA database
There are several features in this plugin:

### 1. PDB file generation
* Open target executable in IDA >= 7.0
* `Edit` -> `FakePDB` -> `Generate .PDB file` (or `Ctrl`+`Shift`+`4`)
* get PDB file from the IDA database directory

The PDB can optionally include symbols for function labels: use `Generate .PDB file (with function labels)` (or `Ctrl`+`Shift`+`5`).

### 2. IDA database export to .json
* Open target executable in IDA >= 7.0
* `Edit` -> `FakePDB` -> `Dump info to .json` (or `Ctrl`+`Shift`+`1`)
* it will generate `filename.json` near the `.idb` file

### 2. Find binary signature of function
### 3. Binary signature search
* Open target executable in IDA >= 7.0
* Set cursor on start of the target function
* `Edit` -> `FakePDB` -> `Find signature` (or `Ctrl`+`Shift`+`2`)
* signature will be displayed in IDA console

### 3. Import function names from `.json` file
### 4. Function names import from `.json` file
* Open target executable in IDA >= 7.0
* `Edit` -> `FakePDB` -> `Import offset from .json` (or `Ctrl`+`Shift`+`3`)

Expand All @@ -51,12 +56,18 @@ where:
* `YYYY`: offset from the begining of the section in decimal numbers
* 0x0124567AF: IDA effective address

### 4. Generate PDB file
* Open target executable in IDA >= 7.0
* `Edit` -> `FakePDB` -> `Generate .PDB file` (or `Ctrl`+`Shift`+`4`)
* get PDB file from the IDA database directory

## TODO

* GHIDRA support
* Linux support
* Function arguments support


## Thanks

Inspired by:
* pe_debug http://pefrm-units.osdn.jp/pe_debug.html

Based on:
* LLVM project https://llvm.org/
* LLD project https://lld.llvm.org/
2 changes: 1 addition & 1 deletion src_ida/fakepdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
limitations under the License.
"""

FAKEPDB_VERSION = '0.1'
FAKEPDB_VERSION = '0.2'

import ida_idaapi

Expand Down
71 changes: 57 additions & 14 deletions src_ida/fakepdb/dumpinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
limitations under the License.
"""

from __future__ import print_function

import json
import os.path
import sys

if sys.version_info.major == 3:
from past.builtins import xrange

import ida_auto
import ida_bytes
Expand All @@ -25,6 +31,7 @@
import ida_loader
import ida_nalt
import ida_name
import ida_pro
import ida_segment
import ida_typeinf

Expand All @@ -37,6 +44,8 @@ def __init__(self):
#

def dump_info(self, filepath):
self._base = ida_nalt.get_imagebase()

output = {
'general' : self.__process_general(),
'segments' : self.__process_segments(),
Expand Down Expand Up @@ -117,35 +126,37 @@ def __process_general(self):

def __process_segments(self):
segments = list()

for n in xrange(ida_segment.get_segm_qty()):
seg = ida_segment.getnseg(n)
if seg:
segm = {
'name' : ida_segment.get_segm_name(seg),
'start_ea' : seg.start_ea,
'class' : ida_segment.get_segm_class(seg),
'selector' : seg.sel
'name' : ida_segment.get_segm_name(seg),
'start_rva' : seg.start_ea - self._base,
'class' : ida_segment.get_segm_class(seg),
'selector' : seg.sel
}

segments.append(segm)

return segments

def __process_function_typeinfo(self, function):
def __process_function_typeinfo(self, info, func):

tinfo = ida_typeinf.tinfo_t()
func_type_data = ida_typeinf.func_type_data_t()
tinfo.get_named_type
ida_typeinf.guess_tinfo(function['start_ea'],tinfo)
if ida_pro.IDA_SDK_VERSION >= 740:
ida_typeinf.guess_tinfo(tinfo,func.start_ea)
else:
ida_typeinf.guess_tinfo(func.start_ea,tinfo)
tinfo.get_func_details(func_type_data)

#calling convention
function['calling_convention'] = self.__describe_callingconvention(func_type_data.cc)
info['calling_convention'] = self.__describe_callingconvention(func_type_data.cc)
func_type_data.rettype

#return tpye
function['return_type'] = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type_data.rettype, '', '')
info['return_type'] = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, func_type_data.rettype, '', '')

#arguments
arguments = list()
Expand All @@ -159,7 +170,28 @@ def __process_function_typeinfo(self, function):

arguments.append(arginfo)

function['arguments'] = arguments
info['arguments'] = arguments

def __process_function_labels(self, func):
labels = list()

it = ida_funcs.func_item_iterator_t()
if not it.set(func):
return labels

while it.next_code():
ea = it.current()
name = ida_name.get_visible_name(ea, ida_name.GN_LOCAL)

if name != '':
labels.append({
'offset' : ea - func.start_ea,
'name' : name,
'is_public' : ida_name.is_public_name(ea),
'is_autonamed' : ida_bytes.get_full_flags(ea) & ida_bytes.FF_LABL != 0
})

return labels

def __process_functions(self):
functions = list()
Expand All @@ -186,13 +218,20 @@ def __process_functions(self):
func_public = ida_name.is_public_name(start_ea)

function = {
'start_ea' : start_ea,
'start_rva' : start_ea - self._base,
'name' : func_name,
'is_public' : func_public,
'is_autonamed' : func_autonamed
}

self.__process_function_typeinfo(function)
# PE32/PE32+ only support binaries up to 2GB
if function['start_rva'] >= 2**32:
print('RVA out of range for function: ' + function['name'], file=sys.stderr)

self.__process_function_typeinfo(function, func)

function['labels'] = self.__process_function_labels(func)

functions.append(function)

func = ida_funcs.get_next_func(start_ea)
Expand All @@ -209,12 +248,16 @@ def __process_names(self):
continue

name = {
'ea' : ea,
'rva' : ea - self._base,
'name' : ida_name.get_nlist_name(i),
'is_public' : ida_name.is_public_name(ea),
'is_func' : ida_funcs.get_func(ea) is not None
}

# PE32/PE32+ only support binaries up to 2GB
if name['rva'] >= 2**32:
print('RVA out of range for name: ' + name['name'], file=sys.stderr)

names.append(name)

return names
Expand Down
33 changes: 26 additions & 7 deletions src_ida/fakepdb/generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import ida_kernwin
import ida_loader

from dumpinfo import InformationDumper
from .dumpinfo import InformationDumper

class PdbGenerator:

Expand All @@ -37,8 +37,12 @@ def __init__(self):
self.__executable_name = 'pdbgen.exe' if self.__executable_platform == 'win32' else 'pdbgen'
self.__executable_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), self.__executable_platform, self.__executable_name)

def generate(self, path_exe, path_json, path_pdb):
subprocess.call([self.__executable_path, 'generate', path_exe, path_json, path_pdb])
def generate(self, path_exe, path_json, path_pdb, with_labels):
cmd = [self.__executable_path, 'generate']
if with_labels:
cmd += ['-l']
cmd += [path_exe, path_json, path_pdb]
subprocess.call(cmd)

def get_symserv_exe(self, path_exe):
p = subprocess.Popen([self.__executable_path, 'symserv_exe', path_exe], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Expand All @@ -56,13 +60,17 @@ def get_symserv_pdb(self, path_exe):
#

class __fakepdb_pdbgeneration_actionhandler(ida_kernwin.action_handler_t):
def __init__(self):
def __init__(self, with_labels):
ida_kernwin.action_handler_t.__init__(self)
self.with_labels = with_labels

# Say hello when invoked.
def activate(self, ctx):
ida_auto.set_ida_state(ida_auto.st_Work)
print('FakePDB/generate pdb:')
if self.with_labels:
print('FakePDB/generate pdb (with function labels):')
else:
print('FakePDB/generate pdb:')

dumper = InformationDumper()
generator = PdbGenerator()
Expand All @@ -80,7 +88,7 @@ def activate(self, ctx):
dumper.dump_info(filepath_json)

print(' * generating PDB: %s' % filepath_pdb)
generator.generate(filepath_exe, filepath_json, filepath_pdb)
generator.generate(filepath_exe, filepath_json, filepath_pdb, self.with_labels)

print(' * symserv EXE id: %s' % generator.get_symserv_exe(filepath_exe))
print(' * symserv PDB id: %s' % generator.get_symserv_pdb(filepath_exe))
Expand All @@ -96,11 +104,22 @@ def register_actions():
action_desc = ida_kernwin.action_desc_t(
'fakepdb_pdb_generation', # The action name. This acts like an ID and must be unique
'Generate .PDB file', # The action text.
__fakepdb_pdbgeneration_actionhandler(), # The action handler.
__fakepdb_pdbgeneration_actionhandler(False), # The action handler.
'Ctrl+Shift+4', # Optional: the action shortcut
'', # Optional: the action tooltip (available in menus/toolbar)
0) # Optional: the action icon (shows when in menus/toolbars)

ida_kernwin.register_action(action_desc)
ida_kernwin.attach_action_to_menu('Edit/FakePDB/', 'fakepdb_pdb_generation', ida_kernwin.SETMENU_APP)

action_desc = ida_kernwin.action_desc_t(
'fakepdb_pdb_generation_labels', # The action name. This acts like an ID and must be unique
'Generate .PDB file (with function labels)', # The action text.
__fakepdb_pdbgeneration_actionhandler(True), # The action handler.
'Ctrl+Shift+5', # Optional: the action shortcut
'', # Optional: the action tooltip (available in menus/toolbar)
0) # Optional: the action icon (shows when in menus/toolbars)

ida_kernwin.register_action(action_desc)
ida_kernwin.attach_action_to_menu('Edit/FakePDB/', 'fakepdb_pdb_generation_labels', ida_kernwin.SETMENU_APP)

4 changes: 4 additions & 0 deletions src_ida/fakepdb/offsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@
import json
import os
import traceback
import sys

if sys.version_info.major == 3:
from past.builtins import xrange

import ida_idaapi
import ida_kernwin
Expand Down
5 changes: 5 additions & 0 deletions src_ida/fakepdb/signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
limitations under the License.
"""

import sys

if sys.version_info.major == 3:
from past.builtins import xrange

import ida_bytes
import ida_ida
import ida_idaapi
Expand Down
10 changes: 10 additions & 0 deletions src_pdbgen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(LLVM REQUIRED CONFIG)

if (MSVC)
# silence warnings from LLVM headers
add_compile_options(/experimental:external /external:anglebrackets /external:W0)
add_compile_definitions(_SILENCE_CXX17_ITERATOR_BASE_CLASS_DEPRECATION_WARNING)

add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra)
endif()

add_library(pdbgen_common STATIC)
target_sources(pdbgen_common PRIVATE
guidhelper.h
Expand Down
Loading

0 comments on commit 5450b1b

Please sign in to comment.