Skip to content

Commit

Permalink
Merge pull request #6 from qtc-de/dev
Browse files Browse the repository at this point in the history
Prepare v1.2.0 Release
  • Loading branch information
qtc-de authored Jul 29, 2024
2 parents dda0f21 + 5729e62 commit 838883d
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 25 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## v1.2.0 - July 29, 2024

### Added

* Add RPC interface version information to `RpcInterfaceInfo`
* Add support for loading PDB symbols of method parameters (not in use yet)

### Changed

* Fix incorrect module locations for modules with uppercase filenames


## v1.1.1 - July 19, 2024

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

[![](https://github.com/qtc-de/rpv/actions/workflows/build-examples.yml/badge.svg?branch=main)](https://github.com/qtc-de/rpv/actions/workflows/build-examples.yml)
[![](https://github.com/qtc-de/rpv/actions/workflows/build-examples.yml/badge.svg?branch=dev)](https://github.com/qtc-de/rpv/actions/workflows/build-examples.yml)
[![](https://img.shields.io/badge/version-1.1.1-blue)](https://github.com/qtc-de/rpv/releases)
[![](https://img.shields.io/badge/version-1.2.0-blue)](https://github.com/qtc-de/rpv/releases)
[![](https://img.shields.io/badge/programming%20language-v-blue)](https://vlang.io/)
[![](https://img.shields.io/badge/license-GPL%20v3.0-blue)](https://github.com/qtc-de/rpv/blob/master/LICENSE)
[![](https://img.shields.io/badge/docs-fa6b05)](https://qtc-de.github.io/rpv)
Expand Down
5 changes: 1 addition & 4 deletions src/midl.v
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,6 @@ pub fn (intf RpcInterfaceInfo) decode_methods(pid u32, methods []int)! MidlInter
types.sort(a.id < b.id)

mut name := intf.name
v_major := intf.intf.server_interface.interface_id.VersMajor
v_minor := intf.intf.server_interface.interface_id.VersMinor

if name == ''
{
name = '${intf.id}'
Expand All @@ -167,7 +164,7 @@ pub fn (intf RpcInterfaceInfo) decode_methods(pid u32, methods []int)! MidlInter
return MidlInterface {
id: '${intf.id}'
name: name.replace('-', '')
version: '${v_major}.${v_minor}'
version: intf.version
types: types
functions: functions
}
Expand Down
22 changes: 21 additions & 1 deletion src/rpc.v
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub struct RpcInterfaceInfo {
dispatch_table_addr voidptr
location win.LocationInfo
id string
version string
annotation string
ep_registered bool
intf RpcInterface
Expand Down Expand Up @@ -110,6 +111,7 @@ pub struct RpcMethod {
offset u32
pub mut:
name string
symbols []string
}

// RpcType is used to determine the type if RPC servers that are associated with a process.
Expand Down Expand Up @@ -317,7 +319,14 @@ pub fn (mut pi RpvProcessInformation) update(mut resolver SymbolResolver)!

for mut method in intf_info.methods
{
/*
* The method.symbols property was supposed to hold symbol data for function parameters
* that is displayed during midl decompilation. However, at the time of writing, it seems
* that only combase.dll contains such symbols, unless the retrieval logic is wrong.
* Therefore, the symbol information is currently not further used, but might be in future.
*/
method.name = resolver.load_symbol(intf_info.location.path, method.addr) or { method.name }
method.symbols = resolver.load_symbols(intf_info.location.path, method.addr) or { []string{} }
}

if intf_info.sec_callback.addr != &voidptr(0)
Expand Down Expand Up @@ -647,12 +656,22 @@ pub fn (interface_info RpcInterfaceBasicInfo) enrich_h(process_handle win.HANDLE
base := win.read_proc_mem_s[usize](process_handle, unsafe { midl_server_info.DispatchTable + ctr }) or { break }
fmt := win.read_proc_mem_s[u16](process_handle, unsafe { &u16(midl_server_info.FmtStringOffset) + ctr }) or { break }

/*
* The method.symbols property was supposed to hold symbol data for function parameters
* that is displayed during midl decompilation. However, at the time of writing, it seems
* that only combase.dll contains such symbols, unless the retrieval logic is wrong.
* Therefore, the symbol information is currently not further used, but might be in future.
*/
name := resolver.load_symbol(location_info.path, u64(base)) or { 'Proc${ctr}' }
symbols := resolver.load_symbols(location_info.path, u64(base)) or { []string{} }

unsafe {
rpc_methods << RpcMethod {
addr: voidptr(base)
fmt: voidptr(&u8(midl_server_info.ProcString) + fmt)
offset: u32(usize(base) - usize(location_info.base))
name: resolver.load_symbol(location_info.path, u64(base)) or { 'Proc${ctr}' }
name: name
symbols: symbols
}
}
}
Expand Down Expand Up @@ -695,6 +714,7 @@ pub fn (interface_info RpcInterfaceBasicInfo) enrich_h(process_handle win.HANDLE
return RpcInterfaceInfo {
base: interface_info.base
id: win.uuid_to_str(interface_info.intf.server_interface.interface_id) or { 'unknown' }
version: win.get_interface_version(interface_info.intf.server_interface.interface_id)
intf: interface_info.intf
typ: interface_info.typ
dispatch_table_addr: dispatch_table.DispatchTable
Expand Down
115 changes: 98 additions & 17 deletions src/symbol-resolver.v
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,75 @@ import os
import win
import toml

// Symbol represents a single symbol. It contains the offset of the symbol and
// it's associated name.
struct Symbol {
mut:
name string
offset u64
}


// SymbolSet represents a set of symbols like method parameter names.
struct SymbolSet {
mut:
names []string
offset u64
}

// SymbolMap maps a location (usually a dll file on the file system)
// to symbols that are already known for this location.
type SymbolMap = map[string][]Symbol

// lookup checks whether a symbol is already known for the specified
// offset + location combination
fn (sym_map SymbolMap) lookup(location string, offset u64)? string
{
if location in sym_map
{
for symbol in sym_map[location]
{
if symbol.offset == offset
{
return symbol.name
}
}
}

return none
}

// SymbolSetMap maps a location (usually a dll file on the file system)
// to symbols that are already known for this location.
type SymbolSetMap = map[string][]SymbolSet

// lookup checks whether a symbol is already known for the specified
// offset + location combination
fn (sym_map SymbolSetMap) lookup(location string, offset u64)? []string
{
if location in sym_map
{
for symbol_set in sym_map[location]
{
if symbol_set.offset == offset
{
return symbol_set.names
}
}
}

return none
}

// SymbolResolver is used to resolve method and interface names during runtime.
// SymbolResolver uses a hybrid approach with PDB files and a custom rpv symbol
// file in toml format to resolve symbols. Apart from symbols, SymbolResolver
// can also track notes that were taken for RPC interfaces or methods.
pub struct SymbolResolver {
mut:
symbols map[string][]Symbol
symbols SymbolMap
sym_cache SymbolMap
param_cache SymbolSetMap
uuids map[string]InterfaceData
has_pdb bool
pdb_resolver win.PdbResolver
Expand All @@ -19,13 +81,6 @@ pub struct SymbolResolver {
symbol_path string
}

// Symbol represents a single symbol. It contains the offset of the symbol and
// it's associated name.
struct Symbol {
mut:
name string
offset u64
}

// InterfaceData is used to associate names with RPC interfaces. Moreover, the struct
// contains notes for the interface itself and for associated RPC methods.
Expand Down Expand Up @@ -113,25 +168,51 @@ pub fn parse_resolver(toml_data toml.Doc, pdb_path string, symbol_file string) S
// load_symbol attempts to resolve the location + offset information to a symbol
// name. If successful, the symbol name is returned. If the symbol cannot be found
// the function returns none.
pub fn (resolver SymbolResolver) load_symbol(location string, offset u64)? string
pub fn (mut resolver SymbolResolver) load_symbol(location string, offset u64)? string
{
if location in resolver.symbols
return resolver.symbols.lookup(location, offset) or
{
for symbol in resolver.symbols[location]
return resolver.sym_cache.lookup(location, offset) or
{
if symbol.offset == offset
if resolver.has_pdb
{
return symbol.name
if symbol := resolver.pdb_resolver.load_symbol(offset)
{
resolver.sym_cache[location] << Symbol {
name: symbol
offset: offset
}

return symbol
}
}

return none
}
}
}

if resolver.has_pdb
// load_symbols attempts to resolve function parameter names from the specified location
// and offset. Since some DLLs
pub fn (mut resolver SymbolResolver) load_symbols(location string, offset u64)? []string
{
return resolver.param_cache.lookup(location, offset) or
{
return resolver.pdb_resolver.load_symbol(offset) or { return none }
}
if resolver.has_pdb
{
if symbols := resolver.pdb_resolver.load_symbols(offset)
{
resolver.param_cache[location] << SymbolSet {
names: symbols
offset: offset
}

return none
return symbols
}
}

return none
}
}

// load_uuid attempts to resolve an interface name by looking up it's uuid.
Expand Down
2 changes: 1 addition & 1 deletion v.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Module
{
name: 'rpv'
author: 'Tobias Neitzel (@qtc_de)'
version: '1.1.1'
version: '1.2.0'
repo_url: 'https://github.com/qtc-de/rpv'
vcs: 'git'
tags: ['rpc', 'rpv', 'rpv-web', 'rpcview']
Expand Down
25 changes: 24 additions & 1 deletion win/interop.v
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,20 @@ pub struct C.COMM_FAULT_OFFSETS {
FaultOffset u16
}

@[typedef]
pub struct C.IMAGEHLP_STACK_FRAME {
InstructionOffset ULONG64
ReturnOffset ULONG64
FrameOffset ULONG64
StackOffset ULONG64
BackingStoreOffset ULONG64
FuncTableEntry ULONG64
Params[4] ULONG64
Reserved[5] ULONG64
Virtual BOOL
Reserved2 ULONG
}

// C.GUID represents the well known GUID struct from widows. In rpv,
// it is used for different purposes. Mainly to identify RPC interfaces,
// that all contain GUIDs as part of their internal definition.
Expand Down Expand Up @@ -1628,7 +1642,7 @@ pub fn get_location_info_h(process_handle HANDLE, address voidptr)! LocationInfo

for
{
if unsafe { string_from_wide(module_entry.szModule) == location.substr(location.last_index('\\') or { 0 } + 1, location.len) }
if unsafe { string_from_wide(module_entry.szModule).to_lower() == location.substr(location.last_index('\\') or { 0 } + 1, location.len).to_lower() }
{
base_addr = module_entry.modBaseAddr
base_size = module_entry.modBaseSize
Expand Down Expand Up @@ -1854,6 +1868,12 @@ pub fn uuid_to_str(interface_id C.RPC_IF_ID)! string
return unsafe { cstring_to_vstring(p_uuid_str) }
}

// get_interface_version returns the version of an RPC_IF_ID as string
pub fn get_interface_version(interface_id C.RPC_IF_ID) string
{
return '${interface_id.VersMajor}.${interface_id.VersMinor}'
}

// new_guid attempts to parse a C.GUID struct from the specified string.
pub fn new_guid(guid_str string)! C.GUID
{
Expand Down Expand Up @@ -1931,6 +1951,9 @@ fn C.RegQueryValueExA(key_handle HANDLE, value LPCSTR, resv &DWORD, reg_type &DW
fn C.SHGetFileInfoA(path &char, file_attrs DWORD, fileinfo &C.SHFILEINFOA, file_info_size UINT, flags UINT) bool
fn C.SymCleanup(process_handle HANDLE)
fn C.SymFromAddr(process_handle HANDLE, address DWORD64, displacement &DWORD64, symbol &SymbolInfoV) bool
fn C.SymEnumSymbolsForAddr(process_handle HANDLE, address DWORD64, callback fn(&SymbolInfoV, ULONG), context voidptr) bool
fn C.SymEnumSymbols(process_handle HANDLE, base ULONG64, mask &char, callback fn(&SymbolInfoV, ULONG), context voidptr) bool
fn C.SymSetContext(process_handle HANDLE, frame &C.IMAGEHLP_STACK_FRAME, context voidptr) bool
fn C.SymInitialize(process_handle HANDLE, search_path &char, invade_process BOOL) bool
fn C.SymLoadModuleEx(process_handle HANDLE, file_handle HANDLE, image PCSTR, mod PCSTR, dll_base u64, dll_size u32, data voidptr, flags u32) u64
fn C.SymUnloadModule(process_handle HANDLE, module_base u32)
Expand Down
31 changes: 31 additions & 0 deletions win/pdb-resolver.v
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub fn (context PdbResolver) cleanup()
pub fn (context PdbResolver) load_symbol(symbol u64)! string
{
mut disp := u64(0)

symbol_info := SymbolInfoV{
size_of_struct: sizeof(C.SYMBOL_INFO)
}
Expand All @@ -69,3 +70,33 @@ pub fn (context PdbResolver) load_symbol(symbol u64)! string

return unsafe { cstring_to_vstring(&char(symbol_info.name[..].data)) }
}

// load_symbols attempts to resolve the specified symbols for a function address
// from an already created pdb context.
pub fn (context PdbResolver) load_symbols(symbol u64)! []string
{
symbols := []string{}
symbols_ref := &symbols

frame := C.IMAGEHLP_STACK_FRAME{
InstructionOffset: symbol
}

if !C.SymSetContext(context.process_handle, &frame, &voidptr(0))
{
return error('Unable to set symbol context to 0x${symbol}')
}

symbol_closure := fn [symbols_ref] (symbol_info &SymbolInfoV, symbol_size u32) {
unsafe {
symbols_ref << cstring_to_vstring(&char(symbol_info.name[..].data))
}
}

if !C.SymEnumSymbols(context.process_handle, 0, &voidptr(0), symbol_closure, &voidptr(0))
{
return error('Unable to resolve symbols at 0x${symbol} via SymEnumSymbolsForAddr')
}

return symbols
}

0 comments on commit 838883d

Please sign in to comment.