update rust crate

This commit is contained in:
2024-08-17 16:20:28 +02:00
parent 670fa334db
commit 2167e0512a
88 changed files with 20508 additions and 1741 deletions

View File

@@ -26,9 +26,7 @@ fn main() {
.get_data(),
addr,
) {
tokens
.iter()
.for_each(|token| print!("{}", token.text().as_str()));
tokens.iter().for_each(|token| print!("{}", token.text()));
println!();
}
}

View File

@@ -26,7 +26,7 @@ fn decompile_to_c(view: &BinaryView, func: &Function) {
let last = view.get_next_linear_disassembly_lines(&mut cursor.duplicate());
let first = view.get_previous_linear_disassembly_lines(&mut cursor);
let lines = first.into_iter().chain(last.into_iter());
let lines = first.into_iter().chain(&last);
for line in lines {
println!("{}", line.as_ref());

View File

@@ -8,6 +8,6 @@ crate-type = ["cdylib"]
[dependencies]
binaryninja = {path="../../../"}
gimli = "^0.28"
gimli = "^0.31"
log = "^0.4"
object = { version = "0.32.1", features = ["write"] }

View File

@@ -522,13 +522,11 @@ fn export_data_vars(
for data_variable in &bv.data_variables() {
if let Some(symbol) = data_variable.symbol(bv) {
if symbol.sym_type() == SymbolType::External {
continue;
} else if symbol.sym_type() == SymbolType::Function {
continue;
} else if symbol.sym_type() == SymbolType::ImportedFunction {
continue;
} else if symbol.sym_type() == SymbolType::LibraryFunction {
if let SymbolType::External
| SymbolType::Function
| SymbolType::ImportedFunction
| SymbolType::LibraryFunction = symbol.sym_type()
{
continue;
}
}
@@ -551,7 +549,7 @@ fn export_data_vars(
dwarf.unit.get_mut(var_die_uid).set(
gimli::DW_AT_name,
AttributeValue::String(
format!("data_{:x}", data_variable.address)
format!("data_{:x}", data_variable.address())
.as_bytes()
.to_vec(),
),
@@ -559,15 +557,15 @@ fn export_data_vars(
}
let mut variable_location = Expression::new();
variable_location.op_addr(Address::Constant(data_variable.address));
variable_location.op_addr(Address::Constant(data_variable.address()));
dwarf.unit.get_mut(var_die_uid).set(
gimli::DW_AT_location,
AttributeValue::Exprloc(variable_location),
);
if let Some(target_die_uid) = export_type(
format!("{}", data_variable.t.contents),
data_variable.t.contents.as_ref(),
format!("{}", data_variable.t()),
data_variable.t(),
bv,
defined_types,
dwarf,
@@ -739,7 +737,7 @@ fn export_dwarf(bv: &BinaryView) {
} else {
BnString::new("Unknown")
};
let responses = present_form(&arch_name);
let responses = present_form(arch_name.as_str());
let encoding = gimli::Encoding {
format: gimli::Format::Dwarf32,

View File

@@ -10,5 +10,8 @@ crate-type = ["cdylib"]
[dependencies]
dwarfreader = { path = "../shared/" }
binaryninja = { path = "../../../" }
gimli = "0.28"
gimli = "0.31"
log = "0.4.20"
iset = "0.2.2"
cpp_demangle = "0.4.3"
regex = "1"

View File

@@ -13,7 +13,7 @@
// limitations under the License.
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
use crate::helpers::*;
use crate::{helpers::*, ReaderType};
use crate::types::get_type;
use binaryninja::{
@@ -21,9 +21,11 @@ use binaryninja::{
types::{EnumerationBuilder, FunctionParameter, ReferenceType, Type, TypeBuilder},
};
use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Reader, Unit};
use gimli::Dwarf;
use gimli::{constants, AttributeValue::Encoding, DebuggingInformationEntry, Unit};
pub(crate) fn handle_base_type<R: Reader<Offset = usize>>(
pub(crate) fn handle_base_type<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
@@ -37,7 +39,7 @@ pub(crate) fn handle_base_type<R: Reader<Offset = usize>>(
// *Some indication of signedness?
// * = Optional
let name = debug_info_builder_context.get_name(unit, entry)?;
let name = debug_info_builder_context.get_name(dwarf, unit, entry)?;
let size = get_size_as_usize(entry)?;
match entry.attr_value(constants::DW_AT_encoding) {
Ok(Some(Encoding(encoding))) => {
@@ -69,7 +71,8 @@ pub(crate) fn handle_base_type<R: Reader<Offset = usize>>(
}
}
pub(crate) fn handle_enum<R: Reader<Offset = usize>>(
pub(crate) fn handle_enum<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
@@ -107,17 +110,18 @@ pub(crate) fn handle_enum<R: Reader<Offset = usize>>(
let mut children = tree.root().unwrap().children();
while let Ok(Some(child)) = children.next() {
if child.entry().tag() == constants::DW_TAG_enumerator {
let name = debug_info_builder_context.get_name(unit, child.entry())?;
let value = get_attr_as_u64(
&child
.entry()
.attr(constants::DW_AT_const_value)
.unwrap()
.unwrap(),
)
.unwrap();
enumeration_builder.insert(name, value);
let name = debug_info_builder_context.get_name(dwarf, unit, child.entry())?;
let attr = &child
.entry()
.attr(constants::DW_AT_const_value)
.unwrap()
.unwrap();
if let Some(value) = get_attr_as_u64(attr) {
enumeration_builder.insert(name, value);
} else {
log::error!("Unhandled enum member value type - please report this");
return None;
}
}
}
@@ -131,7 +135,7 @@ pub(crate) fn handle_enum<R: Reader<Offset = usize>>(
pub(crate) fn handle_typedef(
debug_info_builder: &mut DebugInfoBuilder,
entry_type: Option<TypeUID>,
typedef_name: String,
typedef_name: &String,
) -> (Option<Ref<Type>>, bool) {
// All base types have:
// DW_AT_name
@@ -140,12 +144,8 @@ pub(crate) fn handle_typedef(
// This will fail in the case where we have a typedef to a type that doesn't exist (failed to parse, incomplete, etc)
if let Some(entry_type_offset) = entry_type {
if let Some((name, t)) = debug_info_builder.get_type(entry_type_offset) {
if typedef_name == name {
return (Some(t), false);
} else if typedef_name != name {
return (Some(t), true);
}
if let Some(t) = debug_info_builder.get_type(entry_type_offset) {
return (Some(t.get_type()), typedef_name != t.get_name());
}
}
@@ -153,7 +153,7 @@ pub(crate) fn handle_typedef(
(None, false)
}
pub(crate) fn handle_pointer<R: Reader<Offset = usize>>(
pub(crate) fn handle_pointer<R: ReaderType>(
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
@@ -172,7 +172,7 @@ pub(crate) fn handle_pointer<R: Reader<Offset = usize>>(
if let Some(pointer_size) = get_size_as_usize(entry) {
if let Some(entry_type_offset) = entry_type {
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
Some(Type::pointer_of_width(
parent_type.as_ref(),
pointer_size,
@@ -190,7 +190,7 @@ pub(crate) fn handle_pointer<R: Reader<Offset = usize>>(
))
}
} else if let Some(entry_type_offset) = entry_type {
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
Some(Type::pointer_of_width(
parent_type.as_ref(),
debug_info_builder_context.default_address_size(),
@@ -209,7 +209,7 @@ pub(crate) fn handle_pointer<R: Reader<Offset = usize>>(
}
}
pub(crate) fn handle_array<R: Reader<Offset = usize>>(
pub(crate) fn handle_array<R: ReaderType>(
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder: &mut DebugInfoBuilder,
@@ -228,7 +228,7 @@ pub(crate) fn handle_array<R: Reader<Offset = usize>>(
// For multidimensional arrays, DW_TAG_subrange_type or DW_TAG_enumeration_type
if let Some(entry_type_offset) = entry_type {
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
let mut tree = unit.entries_tree(Some(entry.offset())).unwrap();
let mut children = tree.root().unwrap().children();
@@ -255,7 +255,8 @@ pub(crate) fn handle_array<R: Reader<Offset = usize>>(
}
}
pub(crate) fn handle_function<R: Reader<Offset = usize>>(
pub(crate) fn handle_function<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
@@ -289,29 +290,25 @@ pub(crate) fn handle_function<R: Reader<Offset = usize>>(
debug_info_builder
.get_type(entry_type_offset)
.expect("Subroutine return type was not processed")
.1
.get_type()
}
None => Type::void(),
};
// Alias function type in the case that it contains itself
if let Some(name) = debug_info_builder_context.get_name(unit, entry) {
if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) {
debug_info_builder.add_type(
get_uid(unit, entry),
name.clone(),
get_uid(dwarf, unit, entry),
&name,
Type::named_type_from_type(
name,
&Type::function::<String, &binaryninja::types::Type>(
return_type.as_ref(),
&[],
false,
),
&name,
&Type::function::<&binaryninja::types::Type>(return_type.as_ref(), &[], false),
),
false,
);
}
let mut parameters: Vec<FunctionParameter<String>> = vec![];
let mut parameters: Vec<FunctionParameter> = vec![];
let mut variable_arguments = false;
// Get all the children and populate
@@ -322,15 +319,16 @@ pub(crate) fn handle_function<R: Reader<Offset = usize>>(
if let (Some(child_uid), Some(name)) = {
(
get_type(
dwarf,
unit,
child.entry(),
debug_info_builder_context,
debug_info_builder,
),
debug_info_builder_context.get_name(unit, child.entry()),
debug_info_builder_context.get_name(dwarf, unit, child.entry()),
)
} {
let child_type = debug_info_builder.get_type(child_uid).unwrap().1;
let child_type = debug_info_builder.get_type(child_uid).unwrap().get_type();
parameters.push(FunctionParameter::new(child_type, name, None));
}
} else if child.entry().tag() == constants::DW_TAG_unspecified_parameters {
@@ -338,8 +336,8 @@ pub(crate) fn handle_function<R: Reader<Offset = usize>>(
}
}
if debug_info_builder_context.get_name(unit, entry).is_some() {
debug_info_builder.remove_type(get_uid(unit, entry));
if debug_info_builder_context.get_name(dwarf, unit, entry).is_some() {
debug_info_builder.remove_type(get_uid(dwarf, unit, entry));
}
Some(Type::function(
@@ -362,7 +360,7 @@ pub(crate) fn handle_const(
// ?DW_AT_type
if let Some(entry_type_offset) = entry_type {
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
Some((*parent_type).to_builder().set_const(true).finalize())
} else {
Some(TypeBuilder::void().set_const(true).finalize())
@@ -382,7 +380,7 @@ pub(crate) fn handle_volatile(
// ?DW_AT_type
if let Some(entry_type_offset) = entry_type {
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().1;
let parent_type = debug_info_builder.get_type(entry_type_offset).unwrap().get_type();
Some((*parent_type).to_builder().set_volatile(true).finalize())
} else {
Some(TypeBuilder::void().set_volatile(true).finalize())

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::helpers::{get_uid, resolve_specification, DieReference};
use crate::{helpers::{get_uid, resolve_specification, DieReference}, ReaderType};
use binaryninja::{
binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
@@ -21,13 +21,14 @@ use binaryninja::{
rc::*,
symbol::SymbolType,
templatesimplifier::simplify_str_to_fqn,
types::{Conf, FunctionParameter, Type},
types::{Conf, FunctionParameter, NamedTypedVariable, Type, Variable, VariableSourceType},
};
use gimli::{DebuggingInformationEntry, Dwarf, Reader, Unit};
use gimli::{DebuggingInformationEntry, Dwarf, Unit};
use log::{error, warn};
use log::{debug, error, warn};
use std::{
cmp::Ordering,
collections::{hash_map::Values, HashMap},
hash::Hash,
};
@@ -46,6 +47,8 @@ pub(crate) struct FunctionInfoBuilder {
pub(crate) address: Option<u64>,
pub(crate) parameters: Vec<Option<(String, TypeUID)>>,
pub(crate) platform: Option<Ref<Platform>>,
pub(crate) variable_arguments: bool,
pub(crate) stack_variables: Vec<NamedTypedVariable>,
}
impl FunctionInfoBuilder {
@@ -55,7 +58,7 @@ impl FunctionInfoBuilder {
raw_name: Option<String>,
return_type: Option<TypeUID>,
address: Option<u64>,
parameters: Vec<Option<(String, TypeUID)>>,
parameters: &Vec<Option<(String, TypeUID)>>,
) {
if full_name.is_some() {
self.full_name = full_name;
@@ -75,13 +78,13 @@ impl FunctionInfoBuilder {
for (i, new_parameter) in parameters.into_iter().enumerate() {
match self.parameters.get(i) {
Some(None) => self.parameters[i] = new_parameter,
Some(None) => self.parameters[i] = new_parameter.clone(),
Some(Some(_)) => (),
// Some(Some((name, _))) if name.as_bytes().is_empty() => {
// self.parameters[i] = new_parameter
// }
// Some(Some((_, uid))) if *uid == 0 => self.parameters[i] = new_parameter, // TODO : This is a placebo....void types aren't actually UID 0
_ => self.parameters.push(new_parameter),
_ => self.parameters.push(new_parameter.clone()),
}
}
}
@@ -97,16 +100,27 @@ pub(crate) struct DebugType {
commit: bool,
}
pub(crate) struct DebugInfoBuilderContext<R: Reader<Offset = usize>> {
dwarf: Dwarf<R>,
impl DebugType {
pub fn get_name(&self) -> &String {
&self.name
}
pub fn get_type(&self) -> Ref<Type> {
self.t.clone()
}
}
pub(crate) struct DebugInfoBuilderContext<R: ReaderType> {
units: Vec<Unit<R>>,
sup_units: Vec<Unit<R>>,
names: HashMap<TypeUID, String>,
default_address_size: usize,
pub(crate) total_die_count: usize,
}
impl<R: Reader<Offset = usize>> DebugInfoBuilderContext<R> {
pub(crate) fn new(view: &BinaryView, dwarf: Dwarf<R>) -> Option<Self> {
impl<R: ReaderType> DebugInfoBuilderContext<R> {
pub(crate) fn new(view: &BinaryView, dwarf: &Dwarf<R>) -> Option<Self> {
let mut units = vec![];
let mut iter = dwarf.units();
while let Ok(Some(header)) = iter.next() {
@@ -118,40 +132,56 @@ impl<R: Reader<Offset = usize>> DebugInfoBuilderContext<R> {
}
}
let mut sup_units = vec![];
if let Some(sup_dwarf) = dwarf.sup() {
let mut sup_iter = sup_dwarf.units();
while let Ok(Some(header)) = sup_iter.next() {
if let Ok(unit) = sup_dwarf.unit(header) {
sup_units.push(unit);
} else {
error!("Unable to read supplementary DWARF information. File may be malformed or corrupted. Not applying debug info.");
return None;
}
}
}
Some(Self {
dwarf,
units,
sup_units,
names: HashMap::new(),
default_address_size: view.address_size(),
total_die_count: 0,
})
}
pub(crate) fn dwarf(&self) -> &Dwarf<R> {
&self.dwarf
}
pub(crate) fn units(&self) -> &[Unit<R>] {
&self.units
}
pub(crate) fn sup_units(&self) -> &[Unit<R>] {
&self.sup_units
}
pub(crate) fn default_address_size(&self) -> usize {
self.default_address_size
}
pub(crate) fn set_name(&mut self, die_uid: TypeUID, name: String) {
// die_uids need to be unique here
assert!(self.names.insert(die_uid, name).is_none());
}
pub(crate) fn get_name(
&self,
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
) -> Option<String> {
match resolve_specification(unit, entry, self) {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => self
match resolve_specification(dwarf, unit, entry, self) {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => self
.names
.get(&get_uid(
dwarf,
entry_unit,
&entry_unit.entry(entry_offset).unwrap(),
))
@@ -166,19 +196,29 @@ impl<R: Reader<Offset = usize>> DebugInfoBuilderContext<R> {
// info and types to one DIE's UID (T) before adding the completed info to BN's debug info
pub(crate) struct DebugInfoBuilder {
functions: Vec<FunctionInfoBuilder>,
raw_function_name_indices: HashMap<String, usize>,
full_function_name_indices: HashMap<String, usize>,
types: HashMap<TypeUID, DebugType>,
data_variables: HashMap<u64, (Option<String>, TypeUID)>,
range_data_offsets: iset::IntervalMap<u64, i64>
}
impl DebugInfoBuilder {
pub(crate) fn new() -> Self {
Self {
functions: vec![],
raw_function_name_indices: HashMap::new(),
full_function_name_indices: HashMap::new(),
types: HashMap::new(),
data_variables: HashMap::new(),
range_data_offsets: iset::IntervalMap::new(),
}
}
pub(crate) fn set_range_data_offsets(&mut self, offsets: iset::IntervalMap<u64, i64>) {
self.range_data_offsets = offsets
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn insert_function(
&mut self,
@@ -186,32 +226,87 @@ impl DebugInfoBuilder {
raw_name: Option<String>,
return_type: Option<TypeUID>,
address: Option<u64>,
parameters: Vec<Option<(String, TypeUID)>>,
) {
parameters: &Vec<Option<(String, TypeUID)>>,
variable_arguments: bool,
) -> Option<usize> {
// Returns the index of the function
// Raw names should be the primary key, but if they don't exist, use the full name
// TODO : Consider further falling back on address/architecture
if let Some(function) = self
.functions
.iter_mut()
.find(|func| func.raw_name.is_some() && func.raw_name == raw_name)
{
function.update(full_name, raw_name, return_type, address, parameters);
} else if let Some(function) = self.functions.iter_mut().find(|func| {
(func.raw_name.is_none() || raw_name.is_none())
&& func.full_name.is_some()
&& func.full_name == full_name
}) {
function.update(full_name, raw_name, return_type, address, parameters);
} else {
self.functions.push(FunctionInfoBuilder {
full_name,
raw_name,
return_type,
address,
parameters,
platform: None,
});
/*
If it has a raw_name and we know it, update it and return
Else if it has a full_name and we know it, update it and return
Else Add a new entry if we don't know the full_name or raw_name
*/
if let Some(ident) = &raw_name {
// check if we already know about this raw name's index
// if we do, and the full name will change, remove the known full index if it exists
// update the function
// if the full name exists, update the stored index for the full name
if let Some(idx) = self.raw_function_name_indices.get(ident) {
let function = self.functions.get_mut(*idx).unwrap();
if function.full_name.is_some() && function.full_name != full_name {
self.full_function_name_indices.remove(function.full_name.as_ref().unwrap());
}
function.update(full_name, raw_name, return_type, address, parameters);
if function.full_name.is_some() {
self.full_function_name_indices.insert(function.full_name.clone().unwrap(), *idx);
}
return Some(*idx);
}
}
else if let Some(ident) = &full_name {
// check if we already know about this full name's index
// if we do, and the raw name will change, remove the known raw index if it exists
// update the function
// if the raw name exists, update the stored index for the raw name
if let Some(idx) = self.full_function_name_indices.get(ident) {
let function = self.functions.get_mut(*idx).unwrap();
if function.raw_name.is_some() && function.raw_name != raw_name {
self.raw_function_name_indices.remove(function.raw_name.as_ref().unwrap());
}
function.update(full_name, raw_name, return_type, address, parameters);
if function.raw_name.is_some() {
self.raw_function_name_indices.insert(function.raw_name.clone().unwrap(), *idx);
}
return Some(*idx);
}
}
else {
debug!("Function entry in DWARF without full or raw name.");
return None;
}
let function = FunctionInfoBuilder {
full_name,
raw_name,
return_type,
address,
parameters: parameters.clone(),
platform: None,
variable_arguments,
stack_variables: vec![],
};
if let Some(n) = &function.full_name {
self.full_function_name_indices.insert(n.clone(), self.functions.len());
}
if let Some(n) = &function.raw_name {
self.raw_function_name_indices.insert(n.clone(), self.functions.len());
}
self.functions.push(function);
Some(self.functions.len()-1)
}
pub(crate) fn functions(&self) -> &[FunctionInfoBuilder] {
@@ -222,13 +317,7 @@ impl DebugInfoBuilder {
self.types.values()
}
pub(crate) fn add_type(
&mut self,
type_uid: TypeUID,
name: String,
t: Ref<Type>,
commit: bool,
) {
pub(crate) fn add_type(&mut self, type_uid: TypeUID, name: &String, t: Ref<Type>, commit: bool) {
if let Some(DebugType {
name: existing_name,
t: existing_type,
@@ -242,7 +331,7 @@ impl DebugInfoBuilder {
},
) {
if existing_type != t && commit {
error!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)",
warn!("DWARF info contains duplicate type definition. Overwriting type `{}` (named `{:?}`) with `{}` (named `{:?}`)",
existing_type,
existing_name,
t,
@@ -256,15 +345,76 @@ impl DebugInfoBuilder {
self.types.remove(&type_uid);
}
// TODO : Non-copy?
pub(crate) fn get_type(&self, type_uid: TypeUID) -> Option<(String, Ref<Type>)> {
self.types
.get(&type_uid)
.map(|type_ref_ref| (type_ref_ref.name.clone(), type_ref_ref.t.clone()))
pub(crate) fn get_type(&self, type_uid: TypeUID) -> Option<&DebugType> {
self.types.get(&type_uid)
}
pub(crate) fn contains_type(&self, type_uid: TypeUID) -> bool {
self.types.get(&type_uid).is_some()
self.types.contains_key(&type_uid)
}
pub(crate) fn add_stack_variable(
&mut self,
fn_idx: Option<usize>,
offset: i64,
name: Option<String>,
type_uid: Option<TypeUID>,
) {
let name = match name {
Some(x) => {
if x.len() == 1 && x.chars().next() == Some('\x00') {
// Anonymous variable, generate name
format!("debug_var_{}", offset)
}
else {
x
}
},
None => {
// Anonymous variable, generate name
format!("debug_var_{}", offset)
}
};
let Some(function_index) = fn_idx else {
// If we somehow lost track of what subprogram we're in or we're not actually in a subprogram
error!("Trying to add a local variable outside of a subprogram. Please report this issue.");
return;
};
// Either get the known type or use a 0 confidence void type so we at least get the name applied
let t = match type_uid {
Some(uid) => Conf::new(self.get_type(uid).unwrap().get_type(), 128),
None => Conf::new(Type::void(), 0)
};
let function = &mut self.functions[function_index];
// TODO: If we can't find a known offset can we try to guess somehow?
let Some(func_addr) = function.address else {
// If we somehow are processing a function's variables before the function is created
error!("Trying to add a local variable without a known function start. Please report this issue.");
return;
};
let Some(offset_adjustment) = self.range_data_offsets.values_overlap(func_addr).next() else {
// Unknown why, but this is happening with MachO + external dSYM
debug!("Refusing to add a local variable ({}@{}) to function at {} without a known CIE offset.", name, offset, func_addr);
return;
};
let adjusted_offset = offset - offset_adjustment;
if adjusted_offset > 0 {
// If we somehow end up with a positive sp offset
error!("Trying to add a local variable at positive storage offset {}. Please report this issue.", adjusted_offset);
return;
}
let var = Variable::new(VariableSourceType::StackVariableSourceType, 0, adjusted_offset);
function.stack_variables.push(NamedTypedVariable::new(var, name, t, false));
}
pub(crate) fn add_data_variable(
@@ -276,14 +426,14 @@ impl DebugInfoBuilder {
if let Some((_existing_name, existing_type_uid)) =
self.data_variables.insert(address, (name, type_uid))
{
let existing_type = self.get_type(existing_type_uid).unwrap().1;
let new_type = self.get_type(type_uid).unwrap().1;
let existing_type = self.get_type(existing_type_uid).unwrap().get_type();
let new_type = self.get_type(type_uid).unwrap().get_type();
if existing_type_uid != type_uid || existing_type != new_type {
error!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`",
warn!("DWARF info contains duplicate data variable definition. Overwriting data variable at 0x{:08x} (`{}`) with `{}`",
address,
self.get_type(existing_type_uid).unwrap().1,
self.get_type(type_uid).unwrap().1
existing_type,
new_type
);
}
}
@@ -303,7 +453,7 @@ impl DebugInfoBuilder {
for (&address, (name, type_uid)) in &self.data_variables {
assert!(debug_info.add_data_variable(
address,
&self.get_type(*type_uid).unwrap().1,
&self.get_type(*type_uid).unwrap().t,
name.clone(),
&[] // TODO : Components
));
@@ -312,17 +462,17 @@ impl DebugInfoBuilder {
fn get_function_type(&self, function: &FunctionInfoBuilder) -> Ref<Type> {
let return_type = match function.return_type {
Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().1.clone(), 0),
Some(return_type_id) => Conf::new(self.get_type(return_type_id).unwrap().get_type(), 128),
_ => Conf::new(binaryninja::types::Type::void(), 0),
};
let parameters: Vec<FunctionParameter<String>> = function
let parameters: Vec<FunctionParameter> = function
.parameters
.iter()
.filter_map(|parameter| match parameter {
Some((name, 0)) => Some(FunctionParameter::new(Type::void(), name.clone(), None)),
Some((name, uid)) => Some(FunctionParameter::new(
self.get_type(*uid).unwrap().1,
self.get_type(*uid).unwrap().get_type(),
name.clone(),
None,
)),
@@ -330,10 +480,7 @@ impl DebugInfoBuilder {
})
.collect();
// TODO : Handle
let variable_parameters = false;
binaryninja::types::Type::function(&return_type, &parameters, variable_parameters)
binaryninja::types::Type::function(&return_type, &parameters, function.variable_arguments)
}
fn commit_functions(&self, debug_info: &mut DebugInfo) {
@@ -348,12 +495,12 @@ impl DebugInfoBuilder {
function.address,
function.platform.clone(),
vec![], // TODO : Components
function.stack_variables.clone(), // TODO: local non-stack variables
));
}
}
pub(crate) fn post_process(&mut self, bv: &BinaryView, _debug_info: &mut DebugInfo) -> &Self {
// TODO : We don't need post-processing if we process correctly the first time....
// When originally resolving names, we need to check:
// If there's already a name from binja that's "more correct" than what we found (has more namespaces)
// If there's no name for the DIE, but there's a linkage name that's resolved in binja to a usable name
@@ -379,19 +526,22 @@ impl DebugInfoBuilder {
if simplify_str_to_fqn(func_full_name, true).len()
< simplify_str_to_fqn(symbol_full_name.clone(), true).len()
{
func.full_name =
Some(symbol_full_name.to_string());
func.full_name = Some(symbol_full_name.to_string());
}
}
}
}
if let Some(address) = func.address {
let existing_functions = bv.functions_at(address);
if existing_functions.len() > 1 {
warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary.");
} else if existing_functions.len() == 1 {
func.platform = Some(existing_functions.get(0).platform());
if let Some(address) = func.address.as_mut() {
let diff = bv.start() - bv.original_image_base();
*address += diff; // rebase the address
let existing_functions = bv.functions_at(*address);
match existing_functions.len().cmp(&1) {
Ordering::Greater => {
warn!("Multiple existing functions at address {address:08x}. One or more functions at this address may have the wrong platform information. Please report this binary.");
}
Ordering::Equal => func.platform = Some(existing_functions.get(0).platform()),
Ordering::Less => {}
}
}
}

View File

@@ -12,67 +12,119 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::OnceLock;
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
use crate::helpers::*;
use crate::{helpers::*, ReaderType};
use crate::types::get_type;
use gimli::{constants, DebuggingInformationEntry, Reader, Unit};
use binaryninja::templatesimplifier::simplify_str_to_str;
use cpp_demangle::DemangleOptions;
use gimli::{constants, DebuggingInformationEntry, Dwarf, Unit};
use log::debug;
use regex::Regex;
fn get_parameters<R: Reader<Offset = usize>>(
fn get_parameters<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
) -> Vec<Option<(String, TypeUID)>> {
) -> (Vec<Option<(String, TypeUID)>>, bool) {
if !entry.has_children() {
vec![]
} else {
// We make a new tree from the current entry to iterate over its children
let mut sub_die_tree = unit.entries_tree(Some(entry.offset())).unwrap();
let root = sub_die_tree.root().unwrap();
return (vec![], false);
}
let mut result = vec![];
let mut children = root.children();
while let Some(child) = children.next().unwrap() {
match child.entry().tag() {
constants::DW_TAG_formal_parameter => {
let name = debug_info_builder_context.get_name(unit, child.entry());
let type_ = get_type(
unit,
child.entry(),
debug_info_builder_context,
debug_info_builder,
);
if let Some(parameter_name) = name {
if let Some(parameter_type) = type_ {
result.push(Some((parameter_name, parameter_type)));
} else {
result.push(Some((parameter_name, 0)))
}
// We make a new tree from the current entry to iterate over its children
let mut sub_die_tree = unit.entries_tree(Some(entry.offset())).unwrap();
let root = sub_die_tree.root().unwrap();
let mut variable_arguments = false;
let mut result = vec![];
let mut children = root.children();
while let Some(child) = children.next().unwrap() {
match child.entry().tag() {
constants::DW_TAG_formal_parameter => {
//TODO: if the param type is a typedef to an anonymous struct (typedef struct {...} foo) then this is reoslved to an anonymous struct instead of foo
// We should still recurse to make sure we load all types this param type depends on, but
let name = debug_info_builder_context.get_name(dwarf, unit, child.entry());
let type_ = get_type(
dwarf,
unit,
child.entry(),
debug_info_builder_context,
debug_info_builder,
);
if let Some(parameter_name) = name {
if let Some(parameter_type) = type_ {
result.push(Some((parameter_name, parameter_type)));
} else {
result.push(None)
result.push(Some((parameter_name, 0)))
}
} else {
result.push(None)
}
}
constants::DW_TAG_unspecified_parameters => variable_arguments = true,
_ => (),
}
}
(result, variable_arguments)
}
pub(crate) fn parse_function_entry<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
) -> Option<usize> {
// Collect function properties (if they exist in this DIE)
let raw_name = get_raw_name(dwarf, unit, entry);
let return_type = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
let address = get_start_address(dwarf, unit, entry);
let (parameters, variable_arguments) = get_parameters(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
// If we have a raw name, it might be mangled, see if we can demangle it into full_name
// raw_name should contain a superset of the info we have in full_name
let mut full_name = None;
if let Some(possibly_mangled_name) = &raw_name {
if possibly_mangled_name.starts_with('_') {
static OPTIONS_MEM: OnceLock<DemangleOptions> = OnceLock::new();
let demangle_options = OPTIONS_MEM.get_or_init(|| {
DemangleOptions::new()
.no_return_type()
.hide_expression_literal_types()
.no_params()
});
static ABI_REGEX_MEM: OnceLock<Regex> = OnceLock::new();
let abi_regex = ABI_REGEX_MEM.get_or_init(|| {
Regex::new(r"\[abi:v\d+\]").unwrap()
});
if let Ok(sym) = cpp_demangle::Symbol::new(possibly_mangled_name) {
if let Ok(demangled) = sym.demangle(demangle_options) {
let cleaned = abi_regex.replace_all(&demangled, "");
let simplified = simplify_str_to_str(&cleaned);
full_name = Some(simplified.to_string());
}
constants::DW_TAG_unspecified_parameters => (),
_ => (),
}
}
result
}
}
pub(crate) fn parse_function_entry<R: Reader<Offset = usize>>(
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
) {
// Collect function properties (if they exist in this DIE)
let full_name = debug_info_builder_context.get_name(unit, entry);
let raw_name = get_raw_name(unit, entry, debug_info_builder_context);
let return_type = get_type(unit, entry, debug_info_builder_context, debug_info_builder);
let address = get_start_address(unit, entry, debug_info_builder_context);
let parameters = get_parameters(unit, entry, debug_info_builder_context, debug_info_builder);
// If we didn't demangle the raw name, fetch the name given
if full_name.is_none() {
full_name = debug_info_builder_context.get_name(dwarf, unit, entry)
}
debug_info_builder.insert_function(full_name, raw_name, return_type, address, parameters);
if raw_name.is_none() && full_name.is_none() {
debug!(
"Function entry in DWARF without full or raw name: .debug_info offset {:?}",
entry.offset().to_debug_info_offset(&unit.header)
);
return None;
}
debug_info_builder.insert_function(full_name, raw_name, return_type, address, &parameters, variable_arguments)
}

View File

@@ -12,124 +12,179 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::DebugInfoBuilderContext;
use std::path::PathBuf;
use std::{
collections::HashMap,
ops::Deref,
sync::mpsc,
str::FromStr
};
use crate::{DebugInfoBuilderContext, ReaderType};
use binaryninja::binaryview::BinaryViewBase;
use binaryninja::filemetadata::FileMetadata;
use binaryninja::Endianness;
use binaryninja::{binaryview::{BinaryView, BinaryViewExt}, downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider}, rc::Ref, settings::Settings};
use gimli::Dwarf;
use gimli::{
constants, Attribute, AttributeValue,
AttributeValue::{DebugInfoRef, UnitRef},
DebuggingInformationEntry, Operation, Reader, Unit, UnitOffset, UnitSectionOffset,
AttributeValue::{DebugInfoRef, DebugInfoRefSup, UnitRef},
DebuggingInformationEntry, Operation, Unit, UnitOffset, UnitSectionOffset,
};
use log::warn;
pub(crate) fn get_uid<R: Reader<Offset = usize>>(
pub(crate) fn get_uid<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
) -> usize {
match entry.offset().to_unit_section_offset(unit) {
// We set a large gap between supplementary and main entries
let adj = dwarf.sup().map_or(0, |_| 0x1000000000000000);
let entry_offset = match entry.offset().to_unit_section_offset(unit) {
UnitSectionOffset::DebugInfoOffset(o) => o.0,
UnitSectionOffset::DebugTypesOffset(o) => o.0,
}
};
entry_offset + adj
}
////////////////////////////////////
// DIE attr convenience functions
pub(crate) enum DieReference<'a, R: Reader<Offset = usize>> {
UnitAndOffset((&'a Unit<R>, UnitOffset)),
pub(crate) enum DieReference<'a, R: ReaderType> {
UnitAndOffset((&'a Dwarf<R>, &'a Unit<R>, UnitOffset)),
Err,
}
pub(crate) fn get_attr_die<'a, R: Reader<Offset = usize>>(
pub(crate) fn get_attr_die<'a, R: ReaderType>(
dwarf: &'a Dwarf<R>,
unit: &'a Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
attr: constants::DwAt,
) -> Option<DieReference<'a, R>> {
match entry.attr_value(attr) {
Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((unit, offset))),
Ok(Some(UnitRef(offset))) => Some(DieReference::UnitAndOffset((dwarf, unit, offset))),
Ok(Some(DebugInfoRef(offset))) => {
for source_unit in debug_info_builder_context.units() {
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
return Some(DieReference::UnitAndOffset((source_unit, new_offset)));
if dwarf.sup().is_some() {
for source_unit in debug_info_builder_context.units() {
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
}
}
}
warn!("Failed to fetch DIE. Debug information may be incomplete.");
else {
// This could either have no supplementary file because it is one or because it just doesn't have one
// operate on supplementary file if dwarf is a supplementary file, else self
// It's possible this is a reference in the supplementary file to itself
for source_unit in debug_info_builder_context.sup_units() {
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
}
}
// ... or it just doesn't have a supplementary file
for source_unit in debug_info_builder_context.units() {
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
return Some(DieReference::UnitAndOffset((dwarf, source_unit, new_offset)));
}
}
}
None
}
// Ok(Some(DebugInfoRefSup(offset))) TODO - dwarf 5 stuff
},
Ok(Some(DebugInfoRefSup(offset))) => {
for source_unit in debug_info_builder_context.sup_units() {
if let Some(new_offset) = offset.to_unit_offset(&source_unit.header) {
return Some(DieReference::UnitAndOffset((dwarf.sup().unwrap(), source_unit, new_offset)));
}
}
warn!("Failed to fetch DIE. Supplementary debug information may be incomplete.");
None
},
_ => None,
}
}
pub(crate) fn resolve_specification<'a, R: Reader<Offset = usize>>(
pub(crate) fn resolve_specification<'a, R: ReaderType>(
dwarf: &'a Dwarf<R>,
unit: &'a Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &'a DebugInfoBuilderContext<R>,
) -> DieReference<'a, R> {
if let Some(die_reference) = get_attr_die(
dwarf,
unit,
entry,
debug_info_builder_context,
constants::DW_AT_specification,
) {
match die_reference {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
if let Ok(entry) = entry_unit.entry(entry_offset) {
resolve_specification(entry_unit, &entry, debug_info_builder_context)
resolve_specification(dwarf, entry_unit, &entry, debug_info_builder_context)
} else {
warn!("Failed to fetch DIE. Debug information may be incomplete.");
warn!("Failed to fetch DIE for attr DW_AT_specification. Debug information may be incomplete.");
DieReference::Err
}
}
DieReference::Err => DieReference::Err,
}
} else if let Some(die_reference) = get_attr_die(
dwarf,
unit,
entry,
debug_info_builder_context,
constants::DW_AT_abstract_origin,
) {
match die_reference {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
if entry_offset == entry.offset() {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
if entry_offset == entry.offset() && unit.header.offset() == entry_unit.header.offset() {
warn!("DWARF information is invalid (infinite abstract origin reference cycle). Debug information may be incomplete.");
DieReference::Err
} else if let Ok(new_entry) = entry_unit.entry(entry_offset) {
resolve_specification(entry_unit, &new_entry, debug_info_builder_context)
resolve_specification(dwarf, entry_unit, &new_entry, debug_info_builder_context)
} else {
warn!("Failed to fetch DIE. Debug information may be incomplete.");
warn!("Failed to fetch DIE for attr DW_AT_abstract_origin. Debug information may be incomplete.");
DieReference::Err
}
}
DieReference::Err => DieReference::Err,
}
} else {
DieReference::UnitAndOffset((unit, entry.offset()))
DieReference::UnitAndOffset((dwarf, unit, entry.offset()))
}
}
// Get name from DIE, or referenced dependencies
pub(crate) fn get_name<R: Reader<Offset = usize>>(
pub(crate) fn get_name<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
) -> Option<String> {
match resolve_specification(unit, entry, debug_info_builder_context) {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
match resolve_specification(dwarf, unit, entry, debug_info_builder_context) {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
if let Ok(Some(attr_val)) = entry_unit
.entry(entry_offset)
.unwrap()
.attr_value(constants::DW_AT_name)
{
if let Ok(attr_string) = debug_info_builder_context
.dwarf()
.attr_string(entry_unit, attr_val)
if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val.clone())
{
if let Ok(attr_string) = attr_string.to_string() {
return Some(attr_string.to_string());
}
}
else if let Some(dwarf) = &dwarf.sup {
if let Ok(attr_string) = dwarf.attr_string(entry_unit, attr_val)
{
if let Ok(attr_string) = attr_string.to_string() {
return Some(attr_string.to_string());
}
}
}
}
// if let Some(raw_name) = get_raw_name(unit, entry, debug_info_builder_context) {
@@ -146,26 +201,32 @@ pub(crate) fn get_name<R: Reader<Offset = usize>>(
}
// Get raw name from DIE, or referenced dependencies
pub(crate) fn get_raw_name<R: Reader<Offset = usize>>(
pub(crate) fn get_raw_name<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
) -> Option<String> {
if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_linkage_name) {
if let Ok(attr_string) = debug_info_builder_context
.dwarf()
.attr_string(unit, attr_val)
if let Ok(attr_string) = dwarf.attr_string(unit, attr_val.clone())
{
if let Ok(attr_string) = attr_string.to_string() {
return Some(attr_string.to_string());
}
}
else if let Some(dwarf) = dwarf.sup() {
if let Ok(attr_string) = dwarf.attr_string(unit, attr_val)
{
if let Ok(attr_string) = attr_string.to_string() {
return Some(attr_string.to_string());
}
}
}
}
None
}
// Get the size of an object as a usize
pub(crate) fn get_size_as_usize<R: Reader<Offset = usize>>(
pub(crate) fn get_size_as_usize<R: ReaderType>(
entry: &DebuggingInformationEntry<R>,
) -> Option<usize> {
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
@@ -178,7 +239,7 @@ pub(crate) fn get_size_as_usize<R: Reader<Offset = usize>>(
}
// Get the size of an object as a u64
pub(crate) fn get_size_as_u64<R: Reader<Offset = usize>>(
pub(crate) fn get_size_as_u64<R: ReaderType>(
entry: &DebuggingInformationEntry<R>,
) -> Option<u64> {
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_byte_size) {
@@ -191,7 +252,7 @@ pub(crate) fn get_size_as_u64<R: Reader<Offset = usize>>(
}
// Get the size of a subrange as a u64
pub(crate) fn get_subrange_size<R: Reader<Offset = usize>>(
pub(crate) fn get_subrange_size<R: ReaderType>(
entry: &DebuggingInformationEntry<R>,
) -> u64 {
if let Ok(Some(attr)) = entry.attr(constants::DW_AT_upper_bound) {
@@ -206,35 +267,27 @@ pub(crate) fn get_subrange_size<R: Reader<Offset = usize>>(
}
// Get the start address of a function
pub(crate) fn get_start_address<R: Reader<Offset = usize>>(
pub(crate) fn get_start_address<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
) -> Option<u64> {
if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_low_pc) {
match debug_info_builder_context
.dwarf()
.attr_address(unit, attr_val)
match dwarf.attr_address(unit, attr_val)
{
Ok(Some(val)) => Some(val),
_ => None,
}
} else if let Ok(Some(attr_val)) = entry.attr_value(constants::DW_AT_entry_pc) {
match debug_info_builder_context
.dwarf()
.attr_address(unit, attr_val)
match dwarf.attr_address(unit, attr_val)
{
Ok(Some(val)) => Some(val),
_ => None,
}
} else if let Ok(Some(attr_value)) = entry.attr_value(constants::DW_AT_ranges) {
if let Ok(Some(ranges_offset)) = debug_info_builder_context
.dwarf()
.attr_ranges_offset(unit, attr_value)
if let Ok(Some(ranges_offset)) = dwarf.attr_ranges_offset(unit, attr_value)
{
if let Ok(mut ranges) = debug_info_builder_context
.dwarf()
.ranges(unit, ranges_offset)
if let Ok(mut ranges) = dwarf.ranges(unit, ranges_offset)
{
if let Ok(Some(range)) = ranges.next() {
return Some(range.begin);
@@ -248,20 +301,26 @@ pub(crate) fn get_start_address<R: Reader<Offset = usize>>(
}
// Get an attribute value as a u64 if it can be coerced
pub(crate) fn get_attr_as_u64<R: Reader<Offset = usize>>(attr: &Attribute<R>) -> Option<u64> {
if let Some(value) = attr.u8_value() {
Some(value.into())
} else if let Some(value) = attr.u16_value() {
Some(value.into())
} else if let Some(value) = attr.udata_value() {
pub(crate) fn get_attr_as_u64<R: ReaderType>(attr: &Attribute<R>) -> Option<u64> {
if let Some(value) = attr.udata_value() {
Some(value)
} else if let Some(value) = attr.sdata_value() {
Some(value as u64)
} else if let AttributeValue::Block(mut data) = attr.value() {
match data.len() {
1 => data.read_u8().map(u64::from).ok(),
2 => data.read_u16().map(u64::from).ok(),
4 => data.read_u32().map(u64::from).ok(),
8 => data.read_u64().ok(),
_ => None
}
} else {
attr.sdata_value().map(|value| value as u64)
None
}
}
// Get an attribute value as a usize if it can be coerced
pub(crate) fn get_attr_as_usize<R: Reader<Offset = usize>>(attr: Attribute<R>) -> Option<usize> {
pub(crate) fn get_attr_as_usize<R: ReaderType>(attr: Attribute<R>) -> Option<usize> {
if let Some(value) = attr.u8_value() {
Some(value.into())
} else if let Some(value) = attr.u16_value() {
@@ -275,7 +334,7 @@ pub(crate) fn get_attr_as_usize<R: Reader<Offset = usize>>(attr: Attribute<R>) -
// Get an attribute value as a usize if it can be coerced
// Parses DW_OP_address, DW_OP_const
pub(crate) fn get_expr_value<R: Reader<Offset = usize>>(
pub(crate) fn get_expr_value<R: ReaderType>(
unit: &Unit<R>,
attr: Attribute<R>,
) -> Option<u64> {
@@ -285,9 +344,252 @@ pub(crate) fn get_expr_value<R: Reader<Offset = usize>>(
Ok(Operation::UnsignedConstant { value }) => Some(value),
Ok(Operation::Address { address: 0 }) => None,
Ok(Operation::Address { address }) => Some(address),
_ => None,
_ => None
}
} else {
None
}
}
pub(crate) fn get_build_id(view: &BinaryView) -> Result<String, String> {
let mut build_id: Option<String> = None;
if let Ok(raw_view) = view.raw_view() {
if let Ok(build_id_section) = raw_view.section_by_name(".note.gnu.build-id") {
// Name size - 4 bytes
// Desc size - 4 bytes
// Type - 4 bytes
// Name - n bytes
// Desc - n bytes
let build_id_bytes = raw_view.read_vec(build_id_section.start(), build_id_section.len());
if build_id_bytes.len() < 12 {
return Err("Build id section must be at least 12 bytes".to_string());
}
let name_len: u32;
let desc_len: u32;
let note_type: u32;
match raw_view.default_endianness() {
Endianness::LittleEndian => {
name_len = u32::from_le_bytes(build_id_bytes[0..4].try_into().unwrap());
desc_len = u32::from_le_bytes(build_id_bytes[4..8].try_into().unwrap());
note_type = u32::from_le_bytes(build_id_bytes[8..12].try_into().unwrap());
},
Endianness::BigEndian => {
name_len = u32::from_be_bytes(build_id_bytes[0..4].try_into().unwrap());
desc_len = u32::from_be_bytes(build_id_bytes[4..8].try_into().unwrap());
note_type = u32::from_be_bytes(build_id_bytes[8..12].try_into().unwrap());
}
};
if note_type != 3 {
return Err(format!("Build id section has wrong type: {}", note_type));
}
let expected_len = (12 + name_len + desc_len) as usize;
if build_id_bytes.len() < expected_len {
return Err(format!("Build id section not expected length: expected {}, got {}", expected_len, build_id_bytes.len()));
}
let desc: &[u8] = &build_id_bytes[(12+name_len as usize)..expected_len];
build_id = Some(desc.iter().map(|b| format!("{:02x}", b)).collect());
}
}
if let Some(x) = build_id {
Ok(x)
}
else {
Err("Failed to get build id".to_string())
}
}
pub(crate) fn download_debug_info(build_id: &String, view: &BinaryView) -> Result<Ref<BinaryView>, String> {
let settings = Settings::new("");
let debug_server_urls = settings.get_string_list("network.debuginfodServers", Some(view), None);
for debug_server_url in debug_server_urls.iter() {
let artifact_url = format!("{}/buildid/{}/debuginfo", debug_server_url, build_id);
// Download from remote
let (tx, rx) = mpsc::channel();
let write = move |data: &[u8]| -> usize {
if let Ok(_) = tx.send(Vec::from(data)) {
data.len()
} else {
0
}
};
let dp = DownloadProvider::try_default().map_err(|_| "No default download provider")?;
let mut inst = dp
.create_instance()
.map_err(|_| "Couldn't create download instance")?;
let result = inst
.perform_custom_request(
"GET",
artifact_url,
HashMap::<String, String>::new(),
DownloadInstanceInputOutputCallbacks {
read: None,
write: Some(Box::new(write)),
progress: None,
},
)
.map_err(|e| e.to_string())?;
if result.status_code != 200 {
continue;
}
let mut expected_length = None;
for (k, v) in result.headers.iter() {
if k.to_lowercase() == "content-length" {
expected_length = Some(usize::from_str(v).map_err(|e| e.to_string())?);
}
}
let mut data = vec![];
while let Ok(packet) = rx.try_recv() {
data.extend(packet.into_iter());
}
if let Some(length) = expected_length {
if data.len() != length {
return Err(format!(
"Bad length: expected {} got {}",
length,
data.len()
));
}
}
let options = "{\"analysis.debugInfo.internal\": false}";
let bv = BinaryView::from_data(FileMetadata::new().deref(), &data)
.map_err(|_| "Unable to create binary view from downloaded data".to_string())?;
return binaryninja::load_view(bv.deref(), false, Some(options))
.ok_or("Unable to load binary view from downloaded data".to_string());
}
return Err("Could not find a server with debug info for this file".to_string());
}
pub(crate) fn find_local_debug_file_for_build_id(build_id: &String, view: &BinaryView) -> Option<String> {
let settings = Settings::new("");
let debug_dirs_enabled = settings.get_bool("analysis.debugInfo.enableDebugDirectories", Some(view), None);
if !debug_dirs_enabled {
return None;
}
let debug_info_paths = settings.get_string_list("analysis.debugInfo.debugDirectories", Some(view), None);
if debug_info_paths.is_empty() {
return None
}
for debug_info_path in debug_info_paths.into_iter() {
if let Ok(path) = PathBuf::from_str(&debug_info_path.to_string())
{
let elf_path = path
.join(&build_id[..2])
.join(&build_id[2..])
.join("elf");
let debug_ext_path = path
.join(&build_id[..2])
.join(format!("{}.debug", &build_id[2..]));
let final_path = if debug_ext_path.exists() {
debug_ext_path
}
else if elf_path.exists() {
elf_path
}
else {
// No paths exist in this dir, try the next one
continue;
};
return final_path
.to_str()
.and_then(|x| Some(x.to_string()));
}
}
None
}
pub(crate) fn load_debug_info_for_build_id(build_id: &String, view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) {
if let Some(debug_file_path) = find_local_debug_file_for_build_id(build_id, view) {
return
(
binaryninja::load_with_options(
debug_file_path,
false,
Some("{\"analysis.debugInfo.internal\": false}")
),
false
);
}
else if Settings::new("").get_bool("network.enableDebuginfod", Some(view), None) {
return (
download_debug_info(build_id, view).ok(),
true
);
}
(None, false)
}
pub(crate) fn find_sibling_debug_file(view: &BinaryView) -> Option<String> {
let settings = Settings::new("");
let load_sibling_debug = settings.get_bool("analysis.debugInfo.loadSiblingDebugFiles", Some(view), None);
if !load_sibling_debug {
return None;
}
let filename = view.file().filename().to_string();
let debug_file = PathBuf::from(format!("{}.debug", filename));
let dsym_folder = PathBuf::from(format!("{}.dSYM/", filename));
if debug_file.exists() && debug_file.is_file() {
return Some(debug_file.to_string_lossy().to_string());
}
if dsym_folder.exists() && dsym_folder.is_dir() {
let dsym_file = dsym_folder
.join("Contents/Resources/DWARF/")
.join(filename); // TODO: should this just pull any file out? Can there be multiple files?
if dsym_file.exists() {
return Some(dsym_file.to_string_lossy().to_string());
}
}
None
}
pub(crate) fn load_sibling_debug_file(view: &BinaryView) -> (Option<Ref<BinaryView>>, bool) {
let Some(debug_file) = find_sibling_debug_file(view) else {
return (None, false);
};
let load_settings = match view.default_platform() {
Some(plat) => format!("{{\"analysis.debugInfo.internal\": false, \"loader.platform\": \"{}\"}}", plat.name()),
None => "{\"analysis.debugInfo.internal\": false}".to_string()
};
(
binaryninja::load_with_options(
debug_file,
false,
Some(load_settings)
),
false
)
}

View File

@@ -18,32 +18,60 @@ mod functions;
mod helpers;
mod types;
use std::collections::HashMap;
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext};
use crate::functions::parse_function_entry;
use crate::helpers::{get_attr_die, get_name, get_uid, DieReference};
use crate::types::parse_data_variable;
use crate::types::parse_variable;
use binaryninja::binaryview::BinaryViewBase;
use binaryninja::{
binaryview::{BinaryView, BinaryViewExt},
debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
logger,
settings::Settings,
templatesimplifier::simplify_str_to_str,
};
use dwarfreader::{
create_section_reader, get_endian, is_dwo_dwarf, is_non_dwo_dwarf, is_raw_dwo_dwarf,
};
use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, SectionId, Unit};
use gimli::{constants, DebuggingInformationEntry, Dwarf, DwarfFileType, Reader, Section, SectionId, Unit, UnwindSection};
use helpers::{get_build_id, load_debug_info_for_build_id};
use log::{error, warn, LevelFilter};
fn recover_names<R: Reader<Offset = usize>>(
trait ReaderType: Reader<Offset = usize> {}
impl<T: Reader<Offset = usize>> ReaderType for T {}
fn recover_names<R: ReaderType>(
dwarf: &Dwarf<R>,
debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
progress: &dyn Fn(usize, usize) -> Result<(), ()>,
) -> bool {
let mut iter = debug_info_builder_context.dwarf().units();
let mut res = true;
if let Some(sup_dwarf) = dwarf.sup() {
res = recover_names_internal(sup_dwarf, debug_info_builder_context, progress);
}
if res {
res = recover_names_internal(dwarf, debug_info_builder_context, progress);
}
res
}
fn recover_names_internal<R: ReaderType>(
dwarf: &Dwarf<R>,
debug_info_builder_context: &mut DebugInfoBuilderContext<R>,
progress: &dyn Fn(usize, usize) -> Result<(), ()>,
) -> bool {
let mut iter = dwarf.units();
while let Ok(Some(header)) = iter.next() {
let unit = debug_info_builder_context.dwarf().unit(header).unwrap();
let unit = dwarf.unit(header).unwrap();
let mut namespace_qualifiers: Vec<(isize, String)> = vec![];
let mut entries = unit.entries();
let mut depth = 0;
@@ -72,7 +100,8 @@ fn recover_names<R: Reader<Offset = usize>>(
match entry.tag() {
constants::DW_TAG_namespace => {
fn resolve_namespace_name<R: Reader<Offset = usize>>(
fn resolve_namespace_name<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
@@ -80,18 +109,20 @@ fn recover_names<R: Reader<Offset = usize>>(
depth: isize,
) {
if let Some(namespace_qualifier) =
get_name(unit, entry, debug_info_builder_context)
get_name(dwarf, unit, entry, debug_info_builder_context)
{
namespace_qualifiers.push((depth, namespace_qualifier));
} else if let Some(die_reference) = get_attr_die(
dwarf,
unit,
entry,
debug_info_builder_context,
constants::DW_AT_extension,
) {
match die_reference {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
resolve_namespace_name(
dwarf,
entry_unit,
&entry_unit.entry(entry_offset).unwrap(),
debug_info_builder_context,
@@ -101,17 +132,17 @@ fn recover_names<R: Reader<Offset = usize>>(
}
DieReference::Err => {
warn!(
"Failed to fetch DIE. Debug information may be incomplete."
"Failed to fetch DIE when resolving namespace. Debug information may be incomplete."
);
}
}
} else {
namespace_qualifiers
.push((depth, "anonymous_namespace".to_string()));
namespace_qualifiers.push((depth, "anonymous_namespace".to_string()));
}
}
resolve_namespace_name(
dwarf,
&unit,
entry,
debug_info_builder_context,
@@ -122,54 +153,54 @@ fn recover_names<R: Reader<Offset = usize>>(
constants::DW_TAG_class_type
| constants::DW_TAG_structure_type
| constants::DW_TAG_union_type => {
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
namespace_qualifiers.push((depth, name))
} else {
namespace_qualifiers.push((
depth,
match entry.tag() {
constants::DW_TAG_class_type => "anonymous_class".to_string(),
constants::DW_TAG_structure_type => "anonymous_structure".to_string(),
constants::DW_TAG_structure_type => {
"anonymous_structure".to_string()
}
constants::DW_TAG_union_type => "anonymous_union".to_string(),
_ => unreachable!(),
}
},
))
}
debug_info_builder_context.set_name(
get_uid(&unit, entry),
simplify_str_to_str(
namespace_qualifiers
.iter()
.map(|(_, namespace)| namespace.to_owned())
.collect::<Vec<String>>()
.join("::"),
)
.to_string(),
get_uid(dwarf, &unit, entry),
simplify_str_to_str(
namespace_qualifiers
.iter()
.map(|(_, namespace)| namespace.to_owned())
.collect::<Vec<String>>()
.join("::"),
)
.to_string(),
);
}
constants::DW_TAG_typedef
| constants::DW_TAG_subprogram
| constants::DW_TAG_enumeration_type => {
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
debug_info_builder_context.set_name(
get_uid(&unit, entry),
simplify_str_to_str(
namespace_qualifiers
.iter()
.chain(vec![&(-1, name)].into_iter())
.map(|(_, namespace)| {
namespace.to_owned()
})
.collect::<Vec<String>>()
.join("::"),
)
.to_string(),
get_uid(dwarf, &unit, entry),
simplify_str_to_str(
namespace_qualifiers
.iter()
.chain(vec![&(-1, name)].into_iter())
.map(|(_, namespace)| namespace.to_owned())
.collect::<Vec<String>>()
.join("::"),
)
.to_string(),
);
}
}
_ => {
if let Some(name) = get_name(&unit, entry, debug_info_builder_context) {
debug_info_builder_context.set_name(get_uid(&unit, entry), name);
if let Some(name) = get_name(dwarf, &unit, entry, debug_info_builder_context) {
debug_info_builder_context.set_name(get_uid(dwarf, &unit, entry), name);
}
}
}
@@ -179,7 +210,8 @@ fn recover_names<R: Reader<Offset = usize>>(
true
}
fn parse_unit<R: Reader<Offset = usize>>(
fn parse_unit<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
@@ -188,9 +220,12 @@ fn parse_unit<R: Reader<Offset = usize>>(
) {
let mut entries = unit.entries();
let mut current_depth: isize = 0;
let mut functions_by_depth: Vec<(Option<usize>, isize)> = vec![];
// Really all we care about as we iterate the entries in a given unit is how they modify state (our perception of the file)
// There's a lot of junk we don't care about in DWARF info, so we choose a couple DIEs and mutate state (add functions (which adds the types it uses) and keep track of what namespace we're in)
while let Ok(Some((_, entry))) = entries.next_dfs() {
while let Ok(Some((depth_delta, entry))) = entries.next_dfs() {
*current_die_number += 1;
if (*progress)(
*current_die_number,
@@ -201,34 +236,147 @@ fn parse_unit<R: Reader<Offset = usize>>(
return; // Parsing canceled
}
current_depth = current_depth.saturating_add(depth_delta);
loop {
if let Some((_fn_idx, depth)) = functions_by_depth.last() {
if current_depth <= *depth {
functions_by_depth.pop();
}
else {
break
}
}
else {
break;
}
}
match entry.tag() {
constants::DW_TAG_subprogram => {
parse_function_entry(unit, entry, debug_info_builder_context, debug_info_builder)
}
let fn_idx = parse_function_entry(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
functions_by_depth.push((fn_idx, current_depth));
},
constants::DW_TAG_variable => {
parse_data_variable(unit, entry, debug_info_builder_context, debug_info_builder)
}
let current_fn_idx = functions_by_depth.last().and_then(|x| x.0);
parse_variable(dwarf, unit, entry, debug_info_builder_context, debug_info_builder, current_fn_idx)
},
constants::DW_TAG_class_type |
constants::DW_TAG_enumeration_type |
constants::DW_TAG_structure_type |
constants::DW_TAG_union_type |
constants::DW_TAG_typedef => {
// Ensure types are loaded even if they're unused
types::get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
},
_ => (),
}
}
}
fn parse_dwarf(
fn parse_eh_frame<R: Reader>(
view: &BinaryView,
mut eh_frame: gimli::EhFrame<R>,
) -> gimli::Result<iset::IntervalMap<u64, i64>> {
eh_frame.set_address_size(view.address_size() as u8);
let mut bases = gimli::BaseAddresses::default();
if let Ok(section) = view.section_by_name(".eh_frame_hdr").or(view.section_by_name("__eh_frame_hdr")) {
bases = bases.set_eh_frame_hdr(section.start());
}
if let Ok(section) = view.section_by_name(".eh_frame").or(view.section_by_name("__eh_frame")) {
bases = bases.set_eh_frame(section.start());
}
if let Ok(section) = view.section_by_name(".text").or(view.section_by_name("__text")) {
bases = bases.set_text(section.start());
}
if let Ok(section) = view.section_by_name(".got").or(view.section_by_name("__got")) {
bases = bases.set_got(section.start());
}
let mut cies = HashMap::new();
let mut cie_data_offsets = iset::IntervalMap::new();
let mut entries = eh_frame.entries(&bases);
loop {
match entries.next()? {
None => return Ok(cie_data_offsets),
Some(gimli::CieOrFde::Cie(_cie)) => {
// TODO: do we want to do anything with standalone CIEs?
}
Some(gimli::CieOrFde::Fde(partial)) => {
let fde = match partial.parse(|_, bases, o| {
cies.entry(o)
.or_insert_with(|| eh_frame.cie_from_offset(bases, o))
.clone()
}) {
Ok(fde) => fde,
Err(e) => {
error!("Failed to parse FDE: {}", e);
continue;
}
};
if fde.len() == 0 {
// This FDE is a terminator
return Ok(cie_data_offsets);
}
// Store CIE offset for FDE range
cie_data_offsets.insert(
fde.initial_address()..fde.initial_address()+fde.len(),
fde.cie().data_alignment_factor()
);
}
}
}
}
fn get_supplementary_build_id(bv: &BinaryView) -> Option<String> {
let raw_view = bv.raw_view().ok()?;
if let Ok(section) = raw_view.section_by_name(".gnu_debugaltlink") {
let start = section.start();
let len = section.len();
if len < 20 {
// Not large enough to hold a build id
return None;
}
raw_view
.read_vec(start, len)
.splitn(2, |x| *x == 0)
.last()
.map(|a| {
a.iter().map(|b| format!("{:02x}", b)).collect()
})
}
else {
None
}
}
fn parse_dwarf(
bv: &BinaryView,
debug_bv: &BinaryView,
supplementary_bv: Option<&BinaryView>,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> DebugInfoBuilder {
) -> Result<DebugInfoBuilder, ()> {
// TODO: warn if no supplementary file and .gnu_debugaltlink section present
// Determine if this is a DWO
// TODO : Make this more robust...some DWOs follow non-DWO conventions
let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
// Figure out if it's the given view or the raw view that has the dwarf info in it
let raw_view = &view.raw_view().unwrap();
let view = if is_dwo_dwarf(view) || is_non_dwo_dwarf(view) {
view
let raw_view = &debug_bv.raw_view().unwrap();
let view = if is_dwo_dwarf(debug_bv) || is_non_dwo_dwarf(debug_bv) {
debug_bv
} else {
raw_view
};
let dwo_file = is_dwo_dwarf(view) || is_raw_dwo_dwarf(view);
// gimli setup
let endian = get_endian(view);
let mut section_reader =
@@ -237,24 +385,60 @@ fn parse_dwarf(
if dwo_file {
dwarf.file_type = DwarfFileType::Dwo;
}
else {
dwarf.file_type = DwarfFileType::Main;
}
if let Some(sup_bv) = supplementary_bv {
let sup_endian = get_endian(sup_bv);
let sup_dwo_file = is_dwo_dwarf(sup_bv) || is_raw_dwo_dwarf(sup_bv);
let sup_section_reader =
|section_id: SectionId| -> _ { create_section_reader(section_id, sup_bv, sup_endian, sup_dwo_file) };
if let Err(e) = dwarf.load_sup(sup_section_reader) {
error!("Failed to load supplementary file: {}", e);
}
}
let eh_frame_endian = get_endian(bv);
let mut eh_frame_section_reader =
|section_id: SectionId| -> _ { create_section_reader(section_id, bv, eh_frame_endian, dwo_file) };
let eh_frame = gimli::EhFrame::load(&mut eh_frame_section_reader).unwrap();
let range_data_offsets = parse_eh_frame(bv, eh_frame)
.map_err(|e| error!("Error parsing .eh_frame: {}", e))?;
// Create debug info builder and recover name mapping first
// Since DWARF is stored as a tree with arbitrary implicit edges among leaves,
// it is not possible to correctly track namespaces while you're parsing "in order" without backtracking,
// so we just do it up front
let mut debug_info_builder = DebugInfoBuilder::new();
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, dwarf) {
if !recover_names(&mut debug_info_builder_context, &progress)
debug_info_builder.set_range_data_offsets(range_data_offsets);
if let Some(mut debug_info_builder_context) = DebugInfoBuilderContext::new(view, &dwarf) {
if !recover_names(&dwarf, &mut debug_info_builder_context, &progress)
|| debug_info_builder_context.total_die_count == 0
{
return debug_info_builder;
return Ok(debug_info_builder);
}
// Parse all the compilation units
let mut current_die_number = 0;
for unit in debug_info_builder_context.sup_units() {
parse_unit(
dwarf.sup().unwrap(),
&unit,
&debug_info_builder_context,
&mut debug_info_builder,
&progress,
&mut current_die_number,
);
}
for unit in debug_info_builder_context.units() {
parse_unit(
unit,
&dwarf,
&unit,
&debug_info_builder_context,
&mut debug_info_builder,
&progress,
@@ -262,14 +446,28 @@ fn parse_dwarf(
);
}
}
debug_info_builder
Ok(debug_info_builder)
}
struct DWARFParser;
impl CustomDebugInfoParser for DWARFParser {
fn is_valid(&self, view: &BinaryView) -> bool {
dwarfreader::is_valid(view)
if dwarfreader::is_valid(view) || dwarfreader::can_use_debuginfod(view) {
return true;
}
if dwarfreader::has_build_id_section(view) {
if let Ok(build_id) = get_build_id(view) {
if helpers::find_local_debug_file_for_build_id(&build_id, view).is_some() {
return true;
}
}
}
if helpers::find_sibling_debug_file(view).is_some() {
return true;
}
false
}
fn parse_info(
@@ -279,10 +477,51 @@ impl CustomDebugInfoParser for DWARFParser {
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> bool {
parse_dwarf(debug_file, progress)
.post_process(bv, debug_info)
.commit_info(debug_info);
true
let (external_file, close_external) = if !dwarfreader::is_valid(bv) {
if let (Some(debug_view), x) = helpers::load_sibling_debug_file(bv) {
(Some(debug_view), x)
}
else if let Ok(build_id) = get_build_id(bv) {
helpers::load_debug_info_for_build_id(&build_id, bv)
}
else {
(None, false)
}
}
else {
(None, false)
};
let sup_bv = get_supplementary_build_id(
external_file
.as_deref()
.unwrap_or(debug_file)
)
.and_then(|build_id| {
load_debug_info_for_build_id(&build_id, bv)
.0
.map(|x| x.raw_view().unwrap())
});
let result = match parse_dwarf(
bv,
external_file.as_deref().unwrap_or(debug_file),
sup_bv.as_deref(),
progress
)
{
Ok(mut builder) => {
builder.post_process(bv, debug_info).commit_info(debug_info);
true
}
Err(_) => false,
};
if let (Some(ext), true) = (external_file, close_external) {
ext.file().close();
}
result
}
}
@@ -290,6 +529,65 @@ impl CustomDebugInfoParser for DWARFParser {
pub extern "C" fn CorePluginInit() -> bool {
logger::init(LevelFilter::Debug).unwrap();
let settings = Settings::new("");
settings.register_setting_json(
"network.enableDebuginfod",
r#"{
"title" : "Enable Debuginfod Support",
"type" : "boolean",
"default" : false,
"description" : "Enable using Debuginfod servers to fetch DWARF debug info for files with a .note.gnu.build-id section.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"network.debuginfodServers",
r#"{
"title" : "Debuginfod Server URLs",
"type" : "array",
"elementType" : "string",
"default" : [],
"description" : "Servers to use for fetching DWARF debug info for files with a .note.gnu.build-id section.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"analysis.debugInfo.enableDebugDirectories",
r#"{
"title" : "Enable Debug File Directories",
"type" : "boolean",
"default" : true,
"description" : "Enable searching local debug directories for DWARF debug info.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"analysis.debugInfo.debugDirectories",
r#"{
"title" : "Debug File Directories",
"type" : "array",
"elementType" : "string",
"default" : [],
"description" : "Paths to folder containing DWARF debug info stored by build id.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"analysis.debugInfo.loadSiblingDebugFiles",
r#"{
"title" : "Enable Loading of Sibling Debug Files",
"type" : "boolean",
"default" : true,
"description" : "Enable automatic loading of X.debug and X.dSYM files next to a file named X.",
"ignore" : []
}"#,
);
DebugInfoParser::register("DWARF", DWARFParser {});
true
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::die_handlers::*;
use crate::{die_handlers::*, ReaderType};
use crate::dwarfdebuginfo::{DebugInfoBuilder, DebugInfoBuilderContext, TypeUID};
use crate::helpers::*;
@@ -23,31 +23,52 @@ use binaryninja::{
},
};
use gimli::{constants, DebuggingInformationEntry, Reader, Unit};
use gimli::{constants, AttributeValue, DebuggingInformationEntry, Dwarf, Operation, Unit};
use log::warn;
use log::{debug, error, warn};
pub(crate) fn parse_data_variable<R: Reader<Offset = usize>>(
pub(crate) fn parse_variable<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
function_index: Option<usize>,
) {
let full_name = debug_info_builder_context.get_name(unit, entry);
let type_uid = get_type(unit, entry, debug_info_builder_context, debug_info_builder);
let full_name = debug_info_builder_context.get_name(dwarf, unit, entry);
let type_uid = get_type(dwarf, unit, entry, debug_info_builder_context, debug_info_builder);
let address = if let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) {
get_expr_value(unit, attr)
} else {
None
let Ok(Some(attr)) = entry.attr(constants::DW_AT_location) else {
return
};
if let (Some(address), Some(type_uid)) = (address, type_uid) {
debug_info_builder.add_data_variable(address, full_name, type_uid);
let AttributeValue::Exprloc(mut expression) = attr.value() else {
return
};
match Operation::parse(&mut expression.0, unit.encoding()) {
Ok(Operation::FrameOffset { offset }) => {
debug_info_builder.add_stack_variable(function_index, offset, full_name, type_uid);
},
//Ok(Operation::RegisterOffset { register: _, offset: _, base_type: _ }) => {
// //TODO: look up register by index (binja register indexes don't match processor indexes?)
// //TODO: calculate absolute stack offset
// //TODO: add by absolute offset
//},
Ok(Operation::Address { address }) => {
if let Some(uid) = type_uid {
debug_info_builder.add_data_variable(address, full_name, uid)
}
},
Ok(op) => {
debug!("Unhandled operation type for variable: {:?}", op);
},
Err(e) => error!("Error parsing operation type for variable {:?}: {}", full_name, e)
}
}
fn do_structure_parse<R: Reader<Offset = usize>>(
fn do_structure_parse<R: ReaderType>(
dwarf: &Dwarf<R>,
structure_type: StructureType,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
@@ -91,8 +112,8 @@ fn do_structure_parse<R: Reader<Offset = usize>>(
return None;
}
let full_name = if get_name(unit, entry, debug_info_builder_context).is_some() {
debug_info_builder_context.get_name(unit, entry)
let full_name = if get_name(dwarf, unit, entry, debug_info_builder_context).is_some() {
debug_info_builder_context.get_name(dwarf, unit, entry)
} else {
None
};
@@ -109,8 +130,8 @@ fn do_structure_parse<R: Reader<Offset = usize>>(
// it will also be how any other types refer to this struct
if let Some(full_name) = &full_name {
debug_info_builder.add_type(
get_uid(unit, entry),
full_name.clone(),
get_uid(dwarf, unit, entry),
&full_name,
Type::named_type_from_type(
full_name.clone(),
&Type::structure(&structure_builder.finalize()),
@@ -121,11 +142,11 @@ fn do_structure_parse<R: Reader<Offset = usize>>(
// We _need_ to have initial typedefs or else we can enter infinite parsing loops
// These get overwritten in the last step with the actual type, however, so this
// is either perfectly fine or breaking a bunch of NTRs
let full_name = format!("anonymous_structure_{:x}", get_uid(unit, entry));
let full_name = format!("anonymous_structure_{:x}", get_uid(dwarf, unit, entry));
debug_info_builder.add_type(
get_uid(unit, entry),
full_name.clone(),
Type::named_type_from_type(full_name, &Type::structure(&structure_builder.finalize())),
get_uid(dwarf, unit, entry),
&full_name,
Type::named_type_from_type(&full_name, &Type::structure(&structure_builder.finalize())),
false,
);
}
@@ -136,14 +157,16 @@ fn do_structure_parse<R: Reader<Offset = usize>>(
while let Ok(Some(child)) = children.next() {
if child.entry().tag() == constants::DW_TAG_member {
if let Some(child_type_id) = get_type(
dwarf,
unit,
child.entry(),
debug_info_builder_context,
debug_info_builder,
) {
if let Some((_, child_type)) = debug_info_builder.get_type(child_type_id) {
if let Some(t) = debug_info_builder.get_type(child_type_id) {
let child_type = t.get_type();
if let Some(child_name) = debug_info_builder_context
.get_name(unit, child.entry())
.get_name(dwarf, unit, child.entry())
.map_or(
if child_type.type_class() == TypeClass::StructureTypeClass {
Some("".to_string())
@@ -188,32 +211,34 @@ fn do_structure_parse<R: Reader<Offset = usize>>(
let finalized_structure = Type::structure(&structure_builder.finalize());
if let Some(full_name) = full_name {
debug_info_builder.add_type(
get_uid(unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)
full_name,
get_uid(dwarf, unit, entry) + 1, // TODO : This is super broke (uid + 1 is not guaranteed to be unique)
&full_name,
finalized_structure,
true,
);
} else {
debug_info_builder.add_type(
get_uid(unit, entry),
format!("{}", finalized_structure),
get_uid(dwarf, unit, entry),
&format!("{}", finalized_structure),
finalized_structure,
false, // Don't commit anonymous unions (because I think it'll break things)
);
}
Some(get_uid(unit, entry))
Some(get_uid(dwarf, unit, entry))
}
// This function iterates up through the dependency references, adding all the types along the way until there are no more or stopping at the first one already tracked, then returns the UID of the type of the given DIE
pub(crate) fn get_type<R: Reader<Offset = usize>>(
pub(crate) fn get_type<R: ReaderType>(
dwarf: &Dwarf<R>,
unit: &Unit<R>,
entry: &DebuggingInformationEntry<R>,
debug_info_builder_context: &DebugInfoBuilderContext<R>,
debug_info_builder: &mut DebugInfoBuilder,
) -> Option<TypeUID> {
// If this node (and thus all its referenced nodes) has already been processed, just return the offset
if debug_info_builder.contains_type(get_uid(unit, entry)) {
return Some(get_uid(unit, entry));
let entry_uid = get_uid(dwarf, unit, entry);
if debug_info_builder.contains_type(entry_uid) {
return Some(entry_uid);
}
// Don't parse types that are just declarations and not definitions
@@ -222,6 +247,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
}
let entry_type = if let Some(die_reference) = get_attr_die(
dwarf,
unit,
entry,
debug_info_builder_context,
@@ -229,25 +255,29 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
) {
// This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
match die_reference {
DieReference::UnitAndOffset((entry_unit, entry_offset)) => get_type(
entry_unit,
&entry_unit.entry(entry_offset).unwrap(),
debug_info_builder_context,
debug_info_builder,
),
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset)) => {
get_type(
dwarf,
entry_unit,
&entry_unit.entry(entry_offset).unwrap(),
debug_info_builder_context,
debug_info_builder,
)
}
DieReference::Err => {
warn!("Failed to fetch DIE. Debug information may be incomplete.");
warn!("Failed to fetch DIE when getting type through DW_AT_type. Debug information may be incomplete.");
None
}
}
} else {
// This needs to recurse first (before the early return below) to ensure all sub-types have been parsed
match resolve_specification(unit, entry, debug_info_builder_context) {
DieReference::UnitAndOffset((entry_unit, entry_offset))
match resolve_specification(dwarf, unit, entry, debug_info_builder_context) {
DieReference::UnitAndOffset((dwarf, entry_unit, entry_offset))
if entry_unit.header.offset() != unit.header.offset()
&& entry_offset != entry.offset() =>
{
get_type(
dwarf,
entry_unit,
&entry_unit.entry(entry_offset).unwrap(),
debug_info_builder_context,
@@ -256,7 +286,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
}
DieReference::UnitAndOffset(_) => None,
DieReference::Err => {
warn!("Failed to fetch DIE. Debug information may be incomplete.");
warn!("Failed to fetch DIE when getting type. Debug information may be incomplete.");
None
}
}
@@ -264,20 +294,21 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
// If this node (and thus all its referenced nodes) has already been processed, just return the offset
// This check is not redundant because this type might have been processes in the recursive calls above
if debug_info_builder.contains_type(get_uid(unit, entry)) {
return Some(get_uid(unit, entry));
if debug_info_builder.contains_type(entry_uid) {
return Some(entry_uid);
}
// Collect the required information to create a type and add it to the type map. Also, add the dependencies of this type to the type's typeinfo
// Create the type, make a TypeInfo for it, and add it to the debug info
let (type_def, mut commit): (Option<Ref<Type>>, bool) = match entry.tag() {
constants::DW_TAG_base_type => (
handle_base_type(unit, entry, debug_info_builder_context),
handle_base_type(dwarf, unit, entry, debug_info_builder_context),
false,
),
constants::DW_TAG_structure_type => {
return do_structure_parse(
dwarf,
StructureType::StructStructureType,
unit,
entry,
@@ -287,6 +318,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
}
constants::DW_TAG_class_type => {
return do_structure_parse(
dwarf,
StructureType::ClassStructureType,
unit,
entry,
@@ -296,6 +328,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
}
constants::DW_TAG_union_type => {
return do_structure_parse(
dwarf,
StructureType::UnionStructureType,
unit,
entry,
@@ -306,13 +339,13 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
// Enum
constants::DW_TAG_enumeration_type => {
(handle_enum(unit, entry, debug_info_builder_context), true)
(handle_enum(dwarf, unit, entry, debug_info_builder_context), true)
}
// Basic types
constants::DW_TAG_typedef => {
if let Some(name) = debug_info_builder_context.get_name(unit, entry) {
handle_typedef(debug_info_builder, entry_type, name)
if let Some(name) = debug_info_builder_context.get_name(dwarf, unit, entry) {
handle_typedef(debug_info_builder, entry_type, &name)
} else {
(None, false)
}
@@ -356,6 +389,7 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
constants::DW_TAG_unspecified_type => (Some(Type::void()), false),
constants::DW_TAG_subroutine_type => (
handle_function(
dwarf,
unit,
entry,
debug_info_builder_context,
@@ -375,8 +409,8 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
// Wrap our resultant type in a TypeInfo so that the internal DebugInfo class can manage it
if let Some(type_def) = type_def {
let name = if get_name(unit, entry, debug_info_builder_context).is_some() {
debug_info_builder_context.get_name(unit, entry)
let name = if get_name(dwarf, unit, entry, debug_info_builder_context).is_some() {
debug_info_builder_context.get_name(dwarf, unit, entry)
} else {
None
}
@@ -385,8 +419,8 @@ pub(crate) fn get_type<R: Reader<Offset = usize>>(
format!("{}", type_def)
});
debug_info_builder.add_type(get_uid(unit, entry), name, type_def, commit);
Some(get_uid(unit, entry))
debug_info_builder.add_type(entry_uid, &name, type_def, commit);
Some(entry_uid)
} else {
None
}

View File

@@ -10,4 +10,4 @@ crate-type = ["cdylib"]
[dependencies]
dwarfreader = { path = "../shared/" }
binaryninja = {path="../../../"}
gimli = "0.28"
gimli = "0.31"

View File

@@ -17,7 +17,6 @@ use binaryninja::{
command::{register, Command},
disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption},
string::BnString,
};
use dwarfreader::is_valid;
@@ -34,7 +33,7 @@ use gimli::{
UnitSectionOffset,
};
static PADDING: [&'static str; 23] = [
static PADDING: [&str; 23] = [
"",
" ",
" ",
@@ -77,14 +76,14 @@ fn get_info_string<R: Reader>(
let label_string = format!("#0x{:08x}", label_value);
disassembly_lines.push(DisassemblyTextLine::from(vec![
InstructionTextToken::new(
BnString::new(label_string),
&label_string,
InstructionTextTokenContents::GotoLabel(label_value),
),
InstructionTextToken::new(BnString::new(":"), InstructionTextTokenContents::Text),
InstructionTextToken::new(":", InstructionTextTokenContents::Text),
]));
disassembly_lines.push(DisassemblyTextLine::from(vec![InstructionTextToken::new(
BnString::new(die_node.tag().static_string().unwrap()),
die_node.tag().static_string().unwrap(),
InstructionTextTokenContents::TypeName, // TODO : KeywordToken?
)]));
@@ -92,7 +91,7 @@ fn get_info_string<R: Reader>(
while let Some(attr) = attrs.next().unwrap() {
let mut attr_line: Vec<InstructionTextToken> = Vec::with_capacity(5);
attr_line.push(InstructionTextToken::new(
BnString::new(" "),
" ",
InstructionTextTokenContents::Indentation,
));
@@ -100,14 +99,14 @@ fn get_info_string<R: Reader>(
if let Some(n) = attr.name().static_string() {
len = n.len();
attr_line.push(InstructionTextToken::new(
BnString::new(n),
n,
InstructionTextTokenContents::FieldName,
));
} else {
// This is rather unlikely, I think
len = 1;
attr_line.push(InstructionTextToken::new(
BnString::new("?"),
"?",
InstructionTextTokenContents::FieldName,
));
}
@@ -115,25 +114,25 @@ fn get_info_string<R: Reader>(
// On command line the magic number that looks good is 22, but that's too much whitespace in a basic block, so I chose 18 (22 is the max with the current padding provided)
if len < 18 {
attr_line.push(InstructionTextToken::new(
BnString::new(PADDING[18 - len]),
PADDING[18 - len],
InstructionTextTokenContents::Text,
));
}
attr_line.push(InstructionTextToken::new(
BnString::new(" = "),
" = ",
InstructionTextTokenContents::Text,
));
if let Ok(Some(addr)) = dwarf.attr_address(unit, attr.value()) {
let addr_string = format!("0x{:08x}", addr);
attr_line.push(InstructionTextToken::new(
BnString::new(addr_string),
&addr_string,
InstructionTextTokenContents::Integer(addr),
));
} else if let Ok(attr_reader) = dwarf.attr_string(unit, attr.value()) {
if let Ok(attr_string) = attr_reader.to_string() {
attr_line.push(InstructionTextToken::new(
BnString::new(attr_string.as_ref()),
attr_string.as_ref(),
InstructionTextTokenContents::String({
let (_, id, offset) =
dwarf.lookup_offset_id(attr_reader.offset_id()).unwrap();
@@ -142,13 +141,13 @@ fn get_info_string<R: Reader>(
));
} else {
attr_line.push(InstructionTextToken::new(
BnString::new("??"),
"??",
InstructionTextTokenContents::Text,
));
}
} else if let Encoding(type_class) = attr.value() {
attr_line.push(InstructionTextToken::new(
BnString::new(type_class.static_string().unwrap()),
type_class.static_string().unwrap(),
InstructionTextTokenContents::TypeName,
));
} else if let UnitRef(offset) = attr.value() {
@@ -159,17 +158,17 @@ fn get_info_string<R: Reader>(
.into_u64();
let addr_string = format!("#0x{:08x}", addr);
attr_line.push(InstructionTextToken::new(
BnString::new(addr_string),
&addr_string,
InstructionTextTokenContents::GotoLabel(addr),
));
} else if let Flag(true) = attr.value() {
attr_line.push(InstructionTextToken::new(
BnString::new("true"),
"true",
InstructionTextTokenContents::Integer(1),
));
} else if let Flag(false) = attr.value() {
attr_line.push(InstructionTextToken::new(
BnString::new("false"),
"false",
InstructionTextTokenContents::Integer(1),
));
@@ -177,31 +176,31 @@ fn get_info_string<R: Reader>(
} else if let Some(value) = attr.u8_value() {
let value_string = format!("{}", value);
attr_line.push(InstructionTextToken::new(
BnString::new(value_string),
&value_string,
InstructionTextTokenContents::Integer(value.into()),
));
} else if let Some(value) = attr.u16_value() {
let value_string = format!("{}", value);
attr_line.push(InstructionTextToken::new(
BnString::new(value_string),
&value_string,
InstructionTextTokenContents::Integer(value.into()),
));
} else if let Some(value) = attr.udata_value() {
let value_string = format!("{}", value);
attr_line.push(InstructionTextToken::new(
BnString::new(value_string),
InstructionTextTokenContents::Integer(value.into()),
&value_string,
InstructionTextTokenContents::Integer(value),
));
} else if let Some(value) = attr.sdata_value() {
let value_string = format!("{}", value);
attr_line.push(InstructionTextToken::new(
BnString::new(value_string),
&value_string,
InstructionTextTokenContents::Integer(value as u64),
));
} else {
let attr_string = format!("{:?}", attr.value());
attr_line.push(InstructionTextToken::new(
BnString::new(attr_string),
&attr_string,
InstructionTextTokenContents::Text,
));
}
@@ -282,7 +281,7 @@ fn dump_dwarf(bv: &BinaryView) {
}
}
view.show_graph_report("DWARF", graph);
view.show_graph_report("DWARF", &graph);
}
struct DWARFDump;

View File

@@ -6,4 +6,4 @@ edition = "2021"
[dependencies]
binaryninja = {path="../../../"}
gimli = "0.28"
gimli = "0.31"

View File

@@ -20,6 +20,7 @@ use binaryninja::{
binaryview::{BinaryView, BinaryViewBase, BinaryViewExt},
databuffer::DataBuffer,
Endianness,
settings::Settings,
};
use std::{ffi::CString, rc::Rc};
@@ -52,6 +53,19 @@ pub fn is_raw_dwo_dwarf(view: &BinaryView) -> bool {
}
}
pub fn can_use_debuginfod(view: &BinaryView) -> bool {
has_build_id_section(view) &&
Settings::new("")
.get_bool("network.enableDebuginfod", Some(view), None)
}
pub fn has_build_id_section(view: &BinaryView) -> bool {
if let Ok(raw_view) = view.raw_view() {
return raw_view.section_by_name(".note.gnu.build-id").is_ok()
}
false
}
pub fn is_valid(view: &BinaryView) -> bool {
is_non_dwo_dwarf(view)
|| is_raw_non_dwo_dwarf(view)
@@ -88,23 +102,37 @@ pub fn create_section_reader<'a, Endian: 'a + Endianity>(
if let Some(data_var) = view
.data_variables()
.iter()
.find(|var| var.address == symbol.address())
.find(|var| var.address() == symbol.address())
{
// TODO : This should eventually be wrapped by some DataView sorta thingy thing, like how python does it
let data_type = data_var.type_with_confidence().contents;
let data = view.read_vec(data_var.address, data_type.width() as usize);
let data_type = data_var.t();
let data = view.read_vec(data_var.address(), data_type.width() as usize);
let element_type = data_type.element_type().unwrap().contents;
if let Some(current_section_header) = data
.chunks(element_type.width() as usize)
.find(|section_header| {
endian.read_u64(&section_header[24..32]) == section.start()
if view.address_size() == 4 {
endian.read_u32(&section_header[16..20]) as u64 == section.start()
}
else {
endian.read_u64(&section_header[24..32]) == section.start()
}
})
{
if (endian.read_u64(&current_section_header[8..16]) & 2048) != 0 {
let section_flags = if view.address_size() == 4 {
endian.read_u32(&current_section_header[8..12]) as u64
}
else {
endian.read_u64(&current_section_header[8..16])
};
// If the section has the compressed bit set
if (section_flags & 2048) != 0 {
// Get section, trim header, decompress, return
let offset = section.start() + 24;
let len = section.len() - 24;
let compressed_header_size = view.address_size()*3;
let offset = section.start() + compressed_header_size as u64;
let len = section.len() - compressed_header_size;
if let Ok(buffer) = view.read_buffer(offset, len) {
use std::ptr;

View File

@@ -3,16 +3,15 @@ use binaryninja::{
command::register,
disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenContents},
flowgraph::{BranchType, EdgePenStyle, EdgeStyle, FlowGraph, FlowGraphNode, ThemeColor},
string::BnString,
};
fn test_graph(view: &BinaryView) {
let graph = FlowGraph::new();
let disassembly_lines_a = vec![DisassemblyTextLine::from(vec![
InstructionTextToken::new(BnString::new("Li"), InstructionTextTokenContents::Text),
InstructionTextToken::new(BnString::new("ne"), InstructionTextTokenContents::Text),
InstructionTextToken::new(BnString::new(" 1"), InstructionTextTokenContents::Text),
InstructionTextToken::new("Li", InstructionTextTokenContents::Text),
InstructionTextToken::new("ne", InstructionTextTokenContents::Text),
InstructionTextToken::new(" 1", InstructionTextTokenContents::Text),
])];
let node_a = FlowGraphNode::new(&graph);
@@ -37,7 +36,7 @@ fn test_graph(view: &BinaryView) {
&EdgeStyle::default(),
);
view.show_graph_report("Rust Graph Title", graph);
view.show_graph_report("Rust Graph Title", &graph);
}
#[no_mangle]

View File

@@ -2,9 +2,7 @@ use std::env;
use binaryninja::binaryview::BinaryViewExt;
use binaryninja::hlil::HighLevelILLiftedOperand;
use binaryninja::hlil::{
HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind,
};
use binaryninja::hlil::{HighLevelILFunction, HighLevelILLiftedInstruction};
use binaryninja::types::Variable;
fn print_indent(indent: usize) {
@@ -12,131 +10,7 @@ fn print_indent(indent: usize) {
}
fn print_operation(operation: &HighLevelILLiftedInstruction) {
use HighLevelILLiftedInstructionKind::*;
match &operation.kind {
Adc(_) => print!("Adc"),
Sbb(_) => print!("Sbb"),
Rlc(_) => print!("Rlc"),
Rrc(_) => print!("Rrc"),
Add(_) => print!("Add"),
Sub(_) => print!("Sub"),
And(_) => print!("And"),
Or(_) => print!("Or"),
Xor(_) => print!("Xor"),
Lsl(_) => print!("Lsl"),
Lsr(_) => print!("Lsr"),
Asr(_) => print!("Asr"),
Rol(_) => print!("Rol"),
Ror(_) => print!("Ror"),
Mul(_) => print!("Mul"),
MuluDp(_) => print!("MuluDp"),
MulsDp(_) => print!("MulsDp"),
Divu(_) => print!("Divu"),
DivuDp(_) => print!("DivuDp"),
Divs(_) => print!("Divs"),
DivsDp(_) => print!("DivsDp"),
Modu(_) => print!("Modu"),
ModuDp(_) => print!("ModuDp"),
Mods(_) => print!("Mods"),
ModsDp(_) => print!("ModsDp"),
CmpE(_) => print!("CmpE"),
CmpNe(_) => print!("CmpNe"),
CmpSlt(_) => print!("CmpSlt"),
CmpUlt(_) => print!("CmpUlt"),
CmpSle(_) => print!("CmpSle"),
CmpUle(_) => print!("CmpUle"),
CmpSge(_) => print!("CmpSge"),
CmpUge(_) => print!("CmpUge"),
CmpSgt(_) => print!("CmpSgt"),
CmpUgt(_) => print!("CmpUgt"),
TestBit(_) => print!("TestBit"),
AddOverflow(_) => print!("AddOverflow"),
Fadd(_) => print!("Fadd"),
Fsub(_) => print!("Fsub"),
Fmul(_) => print!("Fmul"),
Fdiv(_) => print!("Fdiv"),
FcmpE(_) => print!("FcmpE"),
FcmpNe(_) => print!("FcmpNe"),
FcmpLt(_) => print!("FcmpLt"),
FcmpLe(_) => print!("FcmpLe"),
FcmpGe(_) => print!("FcmpGe"),
FcmpGt(_) => print!("FcmpGt"),
FcmpO(_) => print!("FcmpO"),
FcmpUo(_) => print!("FcmpUo"),
ArrayIndex(_) => print!("ArrayIndex"),
ArrayIndexSsa(_) => print!("ArrayIndexSsa"),
Assign(_) => print!("Assign"),
AssignMemSsa(_) => print!("AssignMemSsa"),
AssignUnpack(_) => print!("AssignUnpack"),
AssignUnpackMemSsa(_) => print!("AssignUnpackMemSsa"),
Block(_) => print!("Block"),
Call(_) => print!("Call"),
Tailcall(_) => print!("Tailcall"),
CallSsa(_) => print!("CallSsa"),
Case(_) => print!("Case"),
Const(_) => print!("Const"),
ConstPtr(_) => print!("ConstPtr"),
Import(_) => print!("Import"),
ConstData(_) => print!("ConstData"),
Deref(_) => print!("Deref"),
AddressOf(_) => print!("AddressOf"),
Neg(_) => print!("Neg"),
Not(_) => print!("Not"),
Sx(_) => print!("Sx"),
Zx(_) => print!("Zx"),
LowPart(_) => print!("LowPart"),
BoolToInt(_) => print!("BoolToInt"),
UnimplMem(_) => print!("UnimplMem"),
Fsqrt(_) => print!("Fsqrt"),
Fneg(_) => print!("Fneg"),
Fabs(_) => print!("Fabs"),
FloatToInt(_) => print!("FloatToInt"),
IntToFloat(_) => print!("IntToFloat"),
FloatConv(_) => print!("FloatConv"),
RoundToInt(_) => print!("RoundToInt"),
Floor(_) => print!("Floor"),
Ceil(_) => print!("Ceil"),
Ftrunc(_) => print!("Ftrunc"),
DerefFieldSsa(_) => print!("DerefFieldSsa"),
DerefSsa(_) => print!("DerefSsa"),
ExternPtr(_) => print!("ExternPtr"),
FloatConst(_) => print!("FloatConst"),
For(_) => print!("For"),
ForSsa(_) => print!("ForSsa"),
Goto(_) => print!("Goto"),
Label(_) => print!("Label"),
If(_) => print!("If"),
Intrinsic(_) => print!("Intrinsic"),
IntrinsicSsa(_) => print!("IntrinsicSsa"),
Jump(_) => print!("Jump"),
MemPhi(_) => print!("MemPhi"),
Nop => print!("Nop"),
Break => print!("Break"),
Continue => print!("Continue"),
Noret => print!("Noret"),
Unreachable => print!("Unreachable"),
Bp => print!("Bp"),
Undef => print!("Undef"),
Unimpl => print!("Unimpl"),
Ret(_) => print!("Ret"),
Split(_) => print!("Split"),
StructField(_) => print!("StructField"),
DerefField(_) => print!("DerefField"),
Switch(_) => print!("Switch"),
Syscall(_) => print!("Syscall"),
SyscallSsa(_) => print!("SyscallSsa"),
Trap(_) => print!("Trap"),
VarDeclare(_) => print!("VarDeclare"),
Var(_) => print!("Var"),
VarInit(_) => print!("VarInit"),
VarInitSsa(_) => print!("VarInitSsa"),
VarPhi(_) => print!("VarPhi"),
VarSsa(_) => print!("VarSsa"),
While(_) => print!("While"),
DoWhile(_) => print!("DoWhile"),
WhileSsa(_) => print!("WhileSsa"),
DoWhileSsa(_) => print!("DoWhileSsa"),
}
print!("{}", operation.name());
}
fn print_variable(func: &HighLevelILFunction, var: &Variable) {
@@ -146,7 +20,7 @@ fn print_variable(func: &HighLevelILFunction, var: &Variable) {
fn print_il_expr(instr: &HighLevelILLiftedInstruction, mut indent: usize) {
print_indent(indent);
print_operation(instr);
println!("");
println!();
indent += 1;

View File

@@ -5,14 +5,11 @@ use minidump::{Minidump, MinidumpMemoryInfoList};
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
use crate::view::DataBufferWrapper;
pub fn print_memory_information(bv: &BinaryView) {
debug!("Printing memory information");
if let Ok(minidump_bv) = bv.parent_view() {
if let Ok(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len()) {
let read_buffer = DataBufferWrapper::new(read_buffer);
if let Ok(minidump_obj) = Minidump::read(read_buffer) {
if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) {
if let Ok(memory_info_list) = minidump_obj.get_stream::<MinidumpMemoryInfoList>() {
let mut memory_info_list_writer = Vec::new();
match memory_info_list.print(&mut memory_info_list_writer) {

View File

@@ -1,6 +1,5 @@
use std::collections::HashMap;
use std::ops::{Deref, Range};
use std::sync::Arc;
use std::ops::Range;
use binaryninja::section::Section;
use binaryninja::segment::Segment;
@@ -16,37 +15,11 @@ use binaryninja::custombinaryview::{
BinaryViewType, BinaryViewTypeBase, CustomBinaryView, CustomBinaryViewType, CustomView,
CustomViewBuilder,
};
use binaryninja::databuffer::DataBuffer;
use binaryninja::platform::Platform;
use binaryninja::Endianness;
type BinaryViewResult<R> = binaryninja::binaryview::Result<R>;
/// A wrapper around a `binaryninja::databuffer::DataBuffer`, from which a `[u8]` buffer can be obtained
/// to pass to `minidump::Minidump::read`.
///
/// This code is taken from [`dwarfdump`](https://github.com/Vector35/binaryninja-api/blob/9d8bc846bd213407fb1a7a19af2a96f17501ac3b/rust/examples/dwarfdump/src/lib.rs#L81)
/// in the Rust API examples.
#[derive(Clone)]
pub struct DataBufferWrapper {
inner: Arc<DataBuffer>,
}
impl DataBufferWrapper {
pub fn new(buf: DataBuffer) -> Self {
DataBufferWrapper {
inner: Arc::new(buf),
}
}
}
impl Deref for DataBufferWrapper {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.inner.get_data()
}
}
/// The _Minidump_ binary view type, which the Rust plugin registers with the Binary Ninja core
/// (via `binaryninja::custombinaryview::register_view_type`) as a possible binary view
/// that can be applied to opened binaries.
@@ -141,9 +114,8 @@ impl MinidumpBinaryView {
fn init(&self) -> BinaryViewResult<()> {
let parent_view = self.parent_view()?;
let read_buffer = parent_view.read_buffer(0, parent_view.len())?;
let read_buffer = DataBufferWrapper::new(read_buffer);
if let Ok(minidump_obj) = Minidump::read(read_buffer) {
if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) {
// Architecture, platform information
if let Ok(minidump_system_info) = minidump_obj.get_stream::<MinidumpSystemInfo>() {
if let Some(platform) = MinidumpBinaryView::translate_minidump_platform(

View File

@@ -1,10 +1,9 @@
use std::env;
use binaryninja::architecture::Intrinsic;
use binaryninja::binaryview::BinaryViewExt;
use binaryninja::mlil::MediumLevelILLiftedOperand;
use binaryninja::mlil::{
MediumLevelILFunction, MediumLevelILLiftedInstruction, MediumLevelILLiftedInstructionKind,
};
use binaryninja::mlil::{MediumLevelILFunction, MediumLevelILLiftedInstruction};
use binaryninja::types::Variable;
fn print_indent(indent: usize) {
@@ -12,139 +11,7 @@ fn print_indent(indent: usize) {
}
fn print_operation(operation: &MediumLevelILLiftedInstruction) {
use MediumLevelILLiftedInstructionKind::*;
match operation.kind {
Nop => print!("Nop"),
Noret => print!("Noret"),
Bp => print!("Bp"),
Undef => print!("Undef"),
Unimpl => print!("Unimpl"),
If(_) => print!("If"),
FloatConst(_) => print!("FloatConst"),
Const(_) => print!("Const"),
ConstPtr(_) => print!("ConstPtr"),
Import(_) => print!("Import"),
ExternPtr(_) => print!("ExternPtr"),
ConstData(_) => print!("ConstData"),
Jump(_) => print!("Jump"),
RetHint(_) => print!("RetHint"),
StoreSsa(_) => print!("StoreSsa"),
StoreStructSsa(_) => print!("StoreStructSsa"),
StoreStruct(_) => print!("StoreStruct"),
Store(_) => print!("Store"),
JumpTo(_) => print!("JumpTo"),
Goto(_) => print!("Goto"),
FreeVarSlot(_) => print!("FreeVarSlot"),
SetVarField(_) => print!("SetVarField"),
SetVar(_) => print!("SetVar"),
FreeVarSlotSsa(_) => print!("FreeVarSlotSsa"),
SetVarSsaField(_) => print!("SetVarSsaField"),
SetVarAliasedField(_) => print!("SetVarAliasedField"),
SetVarAliased(_) => print!("SetVarAliased"),
SetVarSsa(_) => print!("SetVarSsa"),
VarPhi(_) => print!("VarPhi"),
MemPhi(_) => print!("MemPhi"),
VarSplit(_) => print!("VarSplit"),
SetVarSplit(_) => print!("SetVarSplit"),
VarSplitSsa(_) => print!("VarSplitSsa"),
SetVarSplitSsa(_) => print!("SetVarSplitSsa"),
Add(_) => print!("Add"),
Sub(_) => print!("Sub"),
And(_) => print!("And"),
Or(_) => print!("Or"),
Xor(_) => print!("Xor"),
Lsl(_) => print!("Lsl"),
Lsr(_) => print!("Lsr"),
Asr(_) => print!("Asr"),
Rol(_) => print!("Rol"),
Ror(_) => print!("Ror"),
Mul(_) => print!("Mul"),
MuluDp(_) => print!("MuluDp"),
MulsDp(_) => print!("MulsDp"),
Divu(_) => print!("Divu"),
DivuDp(_) => print!("DivuDp"),
Divs(_) => print!("Divs"),
DivsDp(_) => print!("DivsDp"),
Modu(_) => print!("Modu"),
ModuDp(_) => print!("ModuDp"),
Mods(_) => print!("Mods"),
ModsDp(_) => print!("ModsDp"),
CmpE(_) => print!("CmpE"),
CmpNe(_) => print!("CmpNe"),
CmpSlt(_) => print!("CmpSlt"),
CmpUlt(_) => print!("CmpUlt"),
CmpSle(_) => print!("CmpSle"),
CmpUle(_) => print!("CmpUle"),
CmpSge(_) => print!("CmpSge"),
CmpUge(_) => print!("CmpUge"),
CmpSgt(_) => print!("CmpSgt"),
CmpUgt(_) => print!("CmpUgt"),
TestBit(_) => print!("TestBit"),
AddOverflow(_) => print!("AddOverflow"),
FcmpE(_) => print!("FcmpE"),
FcmpNe(_) => print!("FcmpNe"),
FcmpLt(_) => print!("FcmpLt"),
FcmpLe(_) => print!("FcmpLe"),
FcmpGe(_) => print!("FcmpGe"),
FcmpGt(_) => print!("FcmpGt"),
FcmpO(_) => print!("FcmpO"),
FcmpUo(_) => print!("FcmpUo"),
Fadd(_) => print!("Fadd"),
Fsub(_) => print!("Fsub"),
Fmul(_) => print!("Fmul"),
Fdiv(_) => print!("Fdiv"),
Adc(_) => print!("Adc"),
Sbb(_) => print!("Sbb"),
Rlc(_) => print!("Rlc"),
Rrc(_) => print!("Rrc"),
Call(_) => print!("Call"),
Tailcall(_) => print!("Tailcall"),
Syscall(_) => print!("Syscall"),
Intrinsic(_) => print!("Intrinsic"),
IntrinsicSsa(_) => print!("IntrinsicSsa"),
CallSsa(_) => print!("CallSsa"),
TailcallSsa(_) => print!("TailcallSsa"),
CallUntypedSsa(_) => print!("CallUntypedSsa"),
TailcallUntypedSsa(_) => print!("TailcallUntypedSsa"),
SyscallSsa(_) => print!("SyscallSsa"),
SyscallUntypedSsa(_) => print!("SyscallUntypedSsa"),
CallUntyped(_) => print!("CallUntyped"),
TailcallUntyped(_) => print!("TailcallUntyped"),
SyscallUntyped(_) => print!("SyscallUntyped"),
SeparateParamList(_) => print!("SeparateParamList"),
SharedParamSlot(_) => print!("SharedParamSlot"),
Neg(_) => print!("Neg"),
Not(_) => print!("Not"),
Sx(_) => print!("Sx"),
Zx(_) => print!("Zx"),
LowPart(_) => print!("LowPart"),
BoolToInt(_) => print!("BoolToInt"),
UnimplMem(_) => print!("UnimplMem"),
Fsqrt(_) => print!("Fsqrt"),
Fneg(_) => print!("Fneg"),
Fabs(_) => print!("Fabs"),
FloatToInt(_) => print!("FloatToInt"),
IntToFloat(_) => print!("IntToFloat"),
FloatConv(_) => print!("FloatConv"),
RoundToInt(_) => print!("RoundToInt"),
Floor(_) => print!("Floor"),
Ceil(_) => print!("Ceil"),
Ftrunc(_) => print!("Ftrunc"),
Load(_) => print!("Load"),
LoadStruct(_) => print!("LoadStruct"),
LoadStructSsa(_) => print!("LoadStructSsa"),
LoadSsa(_) => print!("LoadSsa"),
Ret(_) => print!("Ret"),
Var(_) => print!("Var"),
AddressOf(_) => print!("AddressOf"),
VarField(_) => print!("VarField"),
AddressOfField(_) => print!("AddressOfField"),
VarSsa(_) => print!("VarSsa"),
VarAliased(_) => print!("VarAliased"),
VarSsaField(_) => print!("VarSsaField"),
VarAliasedField(_) => print!("VarAliasedField"),
Trap(_) => print!("Trap"),
}
print!("{}", operation.name());
}
fn print_variable(func: &MediumLevelILFunction, var: &Variable) {
@@ -154,7 +21,7 @@ fn print_variable(func: &MediumLevelILFunction, var: &Variable) {
fn print_il_expr(instr: &MediumLevelILLiftedInstruction, mut indent: usize) {
print_indent(indent);
print_operation(instr);
println!("");
println!();
indent += 1;

1
examples/pdb-ng/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
target

View File

@@ -0,0 +1,138 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
project(pdb_import_plugin)
file(GLOB PLUGIN_SOURCES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/Cargo.toml
${PROJECT_SOURCE_DIR}/src/*.rs)
file(GLOB_RECURSE API_SOURCES CONFIGURE_DEPENDS
${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/*
${PROJECT_SOURCE_DIR}/../../Cargo.toml
${PROJECT_SOURCE_DIR}/../../src/*.rs)
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target)
else()
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release)
endif()
if(FORCE_COLORED_OUTPUT)
set(CARGO_OPTS ${CARGO_OPTS} --color always)
endif()
if(DEMO)
set(CARGO_FEATURES --features demo --manifest-path ${PROJECT_SOURCE_DIR}/demo/Cargo.toml)
set(OUTPUT_FILE_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}_static${CMAKE_STATIC_LIBRARY_SUFFIX})
set(OUTPUT_PDB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
set(OUTPUT_FILE_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_FILE_NAME})
set(OUTPUT_PDB_PATH ${CMAKE_BINARY_DIR}/${OUTPUT_PDB_NAME})
set(BINJA_LIB_DIR $<TARGET_FILE_DIR:binaryninjacore>)
else()
set(CARGO_FEATURES "")
set(OUTPUT_FILE_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX})
set(OUTPUT_PDB_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PROJECT_NAME}.pdb)
set(OUTPUT_FILE_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_FILE_NAME})
set(OUTPUT_PDB_PATH ${BN_CORE_PLUGIN_DIR}/${OUTPUT_PDB_NAME})
set(BINJA_LIB_DIR ${BN_INSTALL_BIN_DIR})
endif()
add_custom_target(${PROJECT_NAME} ALL DEPENDS ${OUTPUT_FILE_PATH})
add_dependencies(${PROJECT_NAME} binaryninjaapi)
get_target_property(BN_API_SOURCE_DIR binaryninjaapi SOURCE_DIR)
list(APPEND CMAKE_MODULE_PATH "${BN_API_SOURCE_DIR}/cmake")
find_package(BinaryNinjaCore REQUIRED)
set_property(TARGET ${PROJECT_NAME} PROPERTY OUTPUT_FILE_PATH ${OUTPUT_FILE_PATH})
find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
set(RUSTUP_COMMAND ${RUSTUP_PATH} run ${CARGO_STABLE_VERSION} cargo)
if(APPLE)
if(UNIVERSAL)
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE_NAME})
else()
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE_NAME})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE_NAME})
endif()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean --target=aarch64-apple-darwin ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean --target=x86_64-apple-darwin ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build --target=aarch64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build --target=x86_64-apple-darwin ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND lipo -create ${AARCH64_LIB_PATH} ${X86_64_LIB_PATH} -output ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
else()
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/debug/${OUTPUT_FILE_NAME})
else()
set(LIB_PATH ${PROJECT_BINARY_DIR}/target/release/${OUTPUT_FILE_NAME})
endif()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} clean ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 BINARYNINJADIR=${BINJA_LIB_DIR}
${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${LIB_PATH} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()
elseif(WIN32)
if(DEMO)
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
else()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_PDB_NAME} ${OUTPUT_PDB_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()
else()
add_custom_command(
OUTPUT ${OUTPUT_FILE_PATH}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} clean ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env BINARYNINJADIR=${BINJA_LIB_DIR} ${RUSTUP_COMMAND} build ${CARGO_OPTS} ${CARGO_FEATURES}
COMMAND ${CMAKE_COMMAND} -E copy ${TARGET_DIR}/${OUTPUT_FILE_NAME} ${OUTPUT_FILE_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES}
)
endif()

540
examples/pdb-ng/Cargo.lock generated Normal file
View File

@@ -0,0 +1,540 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247"
[[package]]
name = "binaryninja"
version = "0.1.0"
dependencies = [
"binaryninjacore-sys",
"lazy_static",
"libc",
"log",
]
[[package]]
name = "binaryninjacore-sys"
version = "0.1.0"
dependencies = [
"bindgen",
]
[[package]]
name = "bindgen"
version = "0.68.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
"which",
]
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cab"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae6b4de23c7d39c0631fd3cc952d87951c86c75a13812d7247cb7a896e7b3551"
dependencies = [
"byteorder",
"flate2",
"lzxd",
"time",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "crc32fast"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa"
dependencies = [
"cfg-if",
]
[[package]]
name = "deranged"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
dependencies = [
"powerfmt",
]
[[package]]
name = "either"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "errno"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "home"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
dependencies = [
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libloading"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
dependencies = [
"cfg-if",
"windows-targets",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lzxd"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784462f20dddd9dfdb45de963fa4ad4a288cb10a7889ac5d2c34fb6481c6b213"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pdb"
version = "0.8.0"
dependencies = [
"fallible-iterator",
"scroll",
"uuid",
]
[[package]]
name = "pdb-import-plugin"
version = "0.1.0"
dependencies = [
"anyhow",
"binaryninja",
"cab",
"home",
"itertools",
"log",
"pdb",
"regex",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
[[package]]
name = "serde"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "uuid"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a"
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
[[package]]
name = "windows_i686_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
[[package]]
name = "windows_i686_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"

View File

@@ -0,0 +1,20 @@
[package]
name = "pdb-import-plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
anyhow = "^1.0"
binaryninja = {path = "../../"}
home = "^0.5.5"
itertools = "^0.11"
log = "^0.4"
pdb = "^0.8"
cab = "^0.4"
regex = "1"
[features]
demo = []

555
examples/pdb-ng/demo/Cargo.lock generated Normal file
View File

@@ -0,0 +1,555 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anyhow"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
[[package]]
name = "binaryninja"
version = "0.1.0"
dependencies = [
"binaryninjacore-sys",
"lazy_static",
"libc",
"log",
]
[[package]]
name = "binaryninjacore-sys"
version = "0.1.0"
dependencies = [
"bindgen",
]
[[package]]
name = "bindgen"
version = "0.68.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"prettyplease",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn",
"which",
]
[[package]]
name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cab"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae6b4de23c7d39c0631fd3cc952d87951c86c75a13812d7247cb7a896e7b3551"
dependencies = [
"byteorder",
"flate2",
"lzxd",
"time",
]
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "deranged"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
dependencies = [
"powerfmt",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "errno"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "flate2"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "home"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
dependencies = [
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "linux-raw-sys"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "lzxd"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784462f20dddd9dfdb45de963fa4ad4a288cb10a7889ac5d2c34fb6481c6b213"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "pdb"
version = "0.8.0"
dependencies = [
"fallible-iterator",
"scroll",
"uuid",
]
[[package]]
name = "pdb-import-plugin"
version = "0.1.0"
dependencies = [
"anyhow",
"binaryninja",
"cab",
"home",
"itertools",
"log",
"pdb",
"regex",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "prettyplease"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.38.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "scroll"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da"
[[package]]
name = "serde"
version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380"
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "time"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
dependencies = [
"deranged",
"powerfmt",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "uuid"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
[[package]]
name = "which"
version = "4.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
dependencies = [
"either",
"home",
"once_cell",
"rustix",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"

View File

@@ -0,0 +1,21 @@
[package]
name = "pdb-import-plugin-static"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["staticlib"]
path = "../src/lib.rs"
[dependencies]
anyhow = "^1.0"
binaryninja = {path = "../../../"}
home = "^0.5.5"
itertools = "^0.11"
log = "^0.4"
pdb = "^0.8"
cab = "^0.4"
regex = "1"
[features]
demo = []

937
examples/pdb-ng/src/lib.rs Normal file
View File

@@ -0,0 +1,937 @@
// Copyright 2022-2024 Vector 35 Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::HashMap;
use std::env::{current_dir, current_exe, temp_dir};
use std::io::Cursor;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::mpsc;
use std::{env, fs};
use anyhow::{anyhow, Result};
use log::{debug, error, info, LevelFilter};
use pdb::PDB;
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
use binaryninja::debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser};
use binaryninja::downloadprovider::{DownloadInstanceInputOutputCallbacks, DownloadProvider};
use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet};
use binaryninja::settings::Settings;
use binaryninja::string::BnString;
use binaryninja::{add_optional_plugin_dependency, interaction, logger, user_directory};
use parser::PDBParserInstance;
/// PDB Parser!!
///
/// General project structure:
/// - lib.rs: Interaction with DebugInfoParser and plugin actions
/// - parser.rs: PDB Parser base functionality, puts the internal structures into the DebugInfo
/// - type_parser.rs: Parses all the TPI type stream information into both named and indexed types
/// - symbol_parser.rs: Parses, one module at a time, symbol information into named symbols
/// - struct_grouper.rs: Ugly algorithm for handling union and structure members
mod parser;
mod struct_grouper;
mod symbol_parser;
mod type_parser;
// struct PDBLoad;
// struct PDBLoadFile;
// struct PDBSetSymbolPath;
#[allow(dead_code)]
struct PDBInfo {
path: String,
file_name: String,
age: u32,
guid: Vec<u8>,
guid_age_string: String,
}
fn is_pdb(view: &BinaryView) -> bool {
let pdb_magic_bytes = "Microsoft C/C++ MSF 7.00\r\n\x1A\x44\x53\x00\x00\x00";
if let Ok(raw_view) = view.raw_view() {
raw_view.read_vec(0, pdb_magic_bytes.len()) == pdb_magic_bytes.as_bytes()
} else {
false
}
}
fn default_local_cache() -> Result<String> {
// The default value is a directory named "sym" immediately below the program directory
// of the calling application. This is sometimes referred to as the default local cache.
let current_path = current_exe()?;
let parent_path = current_path
.parent()
.ok_or_else(|| anyhow!("No parent to current exe"))?;
let mut cache_path = PathBuf::from(parent_path);
cache_path.push("sym");
return Ok(cache_path
.to_str()
.ok_or_else(|| anyhow!("Could not convert cache path to string"))?
.to_string());
}
fn active_local_cache(view: Option<&BinaryView>) -> Result<String> {
// Check the local symbol store
let mut local_store_path = Settings::new("")
.get_string("pdb.files.localStoreAbsolute", view, None)
.to_string();
if local_store_path.is_empty() {
local_store_path = match user_directory() {
Ok(mut dir) => {
dir.push(
Settings::new("")
.get_string("pdb.files.localStoreRelative", view, None)
.to_string(),
);
match dir.to_str() {
Some(s) => s.to_string(),
_ => "".to_string(),
}
}
_ => "".to_string(),
};
}
if !local_store_path.is_empty() {
Ok(local_store_path)
} else if let Ok(default_cache) = default_local_cache() {
Ok(default_cache)
} else if let Ok(current) = current_dir().map(|d| {
d.to_str()
.expect("Expected current dir to be a valid string")
.to_string()
}) {
Ok(current)
} else {
Ok(temp_dir()
.to_str()
.expect("Expected temp dir to be a valid string")
.to_string())
}
}
fn parse_sym_srv(
symbol_path: &String,
default_store: &String,
) -> Result<Box<dyn Iterator<Item = String>>> {
// https://docs.microsoft.com/en-us/windows/win32/debug/using-symsrv
// Why
// ... the symbol path (_NT_SYMBOL_PATH environment variable) can be made up of several path
// elements separated by semicolons. If any one or more of these path elements begins with
// the text "srv*", then the element is a symbol server and will use SymSrv to locate
// symbol files.
// If the "srv*" text is not specified but the actual path element is a symbol server store,
// then the symbol handler will act as if "srv*" were specified. The symbol handler makes
// this determination by searching for the existence of a file called "pingme.txt" in
// the root directory of the specified path.
// ... symbol servers are made up of symbol store elements separated by asterisks. There can
// be up to 10 symbol stores after the "srv*" prefix.
let mut sym_srv_results = vec![];
// 'path elements separated by semicolons'
for path_element in symbol_path.split(';') {
// 'begins with the text "srv*"'
if path_element.to_lowercase().starts_with("srv*") {
// 'symbol store elements separated by asterisks'
for store_element in path_element[4..].split('*') {
if store_element.is_empty() {
sym_srv_results.push(default_store.clone());
} else {
sym_srv_results.push(store_element.to_string());
}
}
} else if PathBuf::from(path_element).exists() {
// 'searching for the existence of a file called "pingme.txt" in the root directory'
let pingme_txt = path_element.to_string() + "/" + "pingme.txt";
if PathBuf::from(pingme_txt).exists() {
sym_srv_results.push(path_element.to_string());
}
}
}
Ok(Box::new(sym_srv_results.into_iter()))
}
fn read_from_sym_store(path: &String) -> Result<(bool, Vec<u8>)> {
info!("Read file: {}", path);
if !path.contains("://") {
// Local file
let conts = fs::read(path)?;
return Ok((false, conts));
}
if !Settings::new("").get_bool("network.pdbAutoDownload", None, None) {
return Err(anyhow!("Auto download disabled"));
}
// Download from remote
let (tx, rx) = mpsc::channel();
let write = move |data: &[u8]| -> usize {
if let Ok(_) = tx.send(Vec::from(data)) {
data.len()
} else {
0
}
};
info!("GET: {}", path);
let dp =
DownloadProvider::try_default().map_err(|_| anyhow!("No default download provider"))?;
let mut inst = dp
.create_instance()
.map_err(|_| anyhow!("Couldn't create download instance"))?;
let result = inst
.perform_custom_request(
"GET",
path.clone(),
HashMap::<BnString, BnString>::new(),
DownloadInstanceInputOutputCallbacks {
read: None,
write: Some(Box::new(write)),
progress: None,
},
)
.map_err(|e| anyhow!(e.to_string()))?;
if result.status_code != 200 {
return Err(anyhow!("Path does not exist"));
}
let mut expected_length = None;
for (k, v) in result.headers.iter() {
if k.to_lowercase() == "content-length" {
expected_length = Some(usize::from_str(v)?);
}
}
let mut data = vec![];
while let Ok(packet) = rx.try_recv() {
data.extend(packet.into_iter());
}
if let Some(length) = expected_length {
if data.len() != length {
return Err(anyhow!(format!(
"Bad length: expected {} got {}",
length,
data.len()
)));
}
}
Ok((true, data))
}
fn search_sym_store(store_path: &String, pdb_info: &PDBInfo) -> Result<Option<Vec<u8>>> {
// https://www.technlg.net/windows/symbol-server-path-windbg-debugging/
// For symbol servers, to identify the files path easily, Windbg uses the format
// binaryname.pdb/GUID
// Doesn't actually say what the format is, just gives an example:
// https://docs.microsoft.com/en-us/windows/win32/debug/using-symstore
// In this example, the lookup path for the acpi.dbg symbol file might look something
// like this: \\mybuilds\symsrv\acpi.dbg\37cdb03962040.
let base_path =
store_path.clone() + "/" + &pdb_info.file_name + "/" + &pdb_info.guid_age_string;
// Three files may exist inside the lookup directory:
// 1. If the file was stored, then acpi.dbg will exist there.
// 2. If a pointer was stored, then a file called file.ptr will exist and contain the path
// to the actual symbol file.
// 3. A file called refs.ptr, which contains a list of all the current locations for
// acpi.dbg with this timestamp and image size that are currently added to the
// symbol store.
// We don't care about #3 because it says we don't
let direct_path = base_path.clone() + "/" + &pdb_info.file_name;
if let Ok((_remote, conts)) = read_from_sym_store(&direct_path) {
return Ok(Some(conts));
}
let file_ptr = base_path.clone() + "/" + "file.ptr";
if let Ok((_remote, conts)) = read_from_sym_store(&file_ptr) {
let path = String::from_utf8(conts)?;
// PATH:https://full/path
if path.starts_with("PATH:") {
if let Ok((_remote, conts)) = read_from_sym_store(&path[5..].to_string()) {
return Ok(Some(conts));
}
}
}
return Ok(None);
}
fn parse_pdb_info(view: &BinaryView) -> Option<PDBInfo> {
match view.get_metadata::<u64, _>("DEBUG_INFO_TYPE") {
Some(Ok(0x53445352 /* 'SDSR' */)) => {}
_ => return None,
}
// This is stored in the BV by the PE loader
let file_path = match view.get_metadata::<String, _>("PDB_FILENAME") {
Some(Ok(md)) => md,
_ => return None,
};
let mut guid = match view.get_metadata::<Vec<u8>, _>("PDB_GUID") {
Some(Ok(md)) => md,
_ => return None,
};
let age = match view.get_metadata::<u64, _>("PDB_AGE") {
Some(Ok(md)) => md as u32,
_ => return None,
};
if guid.len() != 16 {
return None;
}
// struct _GUID {
// uint32_t Data1;
// uint16_t Data2;
// uint16_t Data3;
// uint8_t Data4[8];
// };
// Endian swap
// Data1
guid.swap(0, 3);
guid.swap(1, 2);
// Data2
guid.swap(4, 5);
// Data3
guid.swap(6, 7);
let guid_age_string = guid
.iter()
.take(16)
.map(|ch| format!("{:02X}", ch))
.collect::<Vec<_>>()
.join("")
+ &format!("{:X}", age);
// Just assume all the paths are /
let file_path = if cfg!(windows) {
file_path
} else {
file_path.replace("\\", "/")
};
let path = file_path;
let file_name = if let Some(idx) = path.rfind("\\") {
path[(idx + 1)..].to_string()
} else if let Some(idx) = path.rfind("/") {
path[(idx + 1)..].to_string()
} else {
path.clone()
};
Some(PDBInfo {
path,
file_name,
age,
guid,
guid_age_string,
})
}
struct PDBParser;
impl PDBParser {
fn load_from_file(
&self,
conts: &Vec<u8>,
debug_info: &mut DebugInfo,
view: &BinaryView,
progress: &Box<dyn Fn(usize, usize) -> Result<(), ()>>,
check_guid: bool,
did_download: bool,
) -> Result<()> {
let mut pdb = PDB::open(Cursor::new(&conts))?;
let settings = Settings::new("");
if let Some(info) = parse_pdb_info(view) {
let pdb_info = &pdb.pdb_information()?;
if info.guid.as_slice() != pdb_info.guid.as_ref() {
if check_guid {
return Err(anyhow!("PDB GUID does not match"));
} else {
let ask = settings.get_string(
"pdb.features.loadMismatchedPDB",
Some(view),
None,
);
match ask.as_str() {
"true" => {},
"ask" => {
if interaction::show_message_box(
"Mismatched PDB",
"This PDB does not look like it matches your binary. Do you want to load it anyway?",
MessageBoxButtonSet::YesNoButtonSet,
binaryninja::interaction::MessageBoxIcon::QuestionIcon
) == MessageBoxButtonResult::NoButton {
return Err(anyhow!("User cancelled mismatched load"));
}
}
_ => {
return Err(anyhow!("PDB GUID does not match"));
}
}
}
}
// Microsoft's symbol server sometimes gives us a different version of the PDB
// than what we ask for. It's weird, but if they're doing it, I trust it will work.
if info.age != pdb_info.age {
if info.age > pdb_info.age {
// Have not seen this case, so I'm not sure if this is fatal
info!("PDB age is older than our binary! Loading it anyway, but there may be missing information.");
} else {
info!("PDB age is newer than our binary! Loading it anyway, there probably shouldn't be any issues.");
}
}
if did_download && settings.get_bool("pdb.files.localStoreCache", None, None) {
match active_local_cache(Some(view)) {
Ok(cache) => {
let mut cab_path = PathBuf::from(&cache);
cab_path.push(&info.file_name);
cab_path.push(
pdb_info
.guid
.as_ref()
.iter()
.map(|ch| format!("{:02X}", ch))
.collect::<Vec<_>>()
.join("")
+ &format!("{:X}", pdb_info.age),
);
let has_dir = if cab_path.is_dir() {
true
} else {
match fs::create_dir_all(&cab_path) {
Ok(_) => true,
Err(e) => {
error!("Could not create PDB cache dir: {}", e);
false
}
}
};
if has_dir {
cab_path.push(&info.file_name);
match fs::write(&cab_path, &conts) {
Ok(_) => {
info!("Downloaded to: {}", cab_path.to_string_lossy());
}
Err(e) => error!("Could not write PDB to cache: {}", e),
}
}
// Also write with the age we expect in our binary view
if info.age < pdb_info.age {
let mut cab_path = PathBuf::from(&cache);
cab_path.push(&info.file_name);
cab_path.push(
pdb_info
.guid
.as_ref()
.iter()
.map(|ch| format!("{:02X}", ch))
.collect::<Vec<_>>()
.join("")
+ &format!("{:X}", info.age), // XXX: BV's pdb age
);
let has_dir = if cab_path.is_dir() {
true
} else {
match fs::create_dir_all(&cab_path) {
Ok(_) => true,
Err(e) => {
error!("Could not create PDB cache dir: {}", e);
false
}
}
};
if has_dir {
cab_path.push(&info.file_name);
match fs::write(&cab_path, &conts) {
Ok(_) => {
info!("Downloaded to: {}", cab_path.to_string_lossy());
}
Err(e) => error!("Could not write PDB to cache: {}", e),
}
}
}
}
Err(e) => error!("Could not get local cache for writing: {}", e),
}
}
} else {
if check_guid {
return Err(anyhow!("File not compiled with PDB information"));
} else {
let ask = settings.get_string(
"pdb.features.loadMismatchedPDB",
Some(view),
None,
);
match ask.as_str() {
"true" => {},
"ask" => {
if interaction::show_message_box(
"No PDB Information",
"This file does not look like it was compiled with a PDB, so your PDB might not correctly apply to the analysis. Do you want to load it anyway?",
MessageBoxButtonSet::YesNoButtonSet,
binaryninja::interaction::MessageBoxIcon::QuestionIcon
) == MessageBoxButtonResult::NoButton {
return Err(anyhow!("User cancelled missing info load"));
}
}
_ => {
return Err(anyhow!("File not compiled with PDB information"));
}
}
}
}
let mut inst = match PDBParserInstance::new(debug_info, view, pdb) {
Ok(inst) => {
info!("Loaded PDB, parsing...");
inst
}
Err(e) => {
error!("Could not open PDB: {}", e);
return Err(e);
}
};
match inst.try_parse_info(Box::new(|cur, max| {
(*progress)(cur, max).map_err(|_| anyhow!("Cancelled"))
})) {
Ok(()) => {
info!("Parsed pdb");
Ok(())
}
Err(e) => {
error!("Could not parse PDB: {}", e);
if e.to_string() == "Todo" {
Ok(())
} else {
Err(e)
}
}
}
}
}
impl CustomDebugInfoParser for PDBParser {
fn is_valid(&self, view: &BinaryView) -> bool {
view.type_name().to_string() == "PE" || is_pdb(view)
}
fn parse_info(
&self,
debug_info: &mut DebugInfo,
view: &BinaryView,
debug_file: &BinaryView,
progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
) -> bool {
if is_pdb(debug_file) {
match self.load_from_file(
&debug_file.read_vec(0, debug_file.len()),
debug_info,
view,
&progress,
false,
false,
) {
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(_) => {
error!("Chosen PDB file failed to load");
return false;
}
}
}
// See if we can get pdb info from the view
if let Some(info) = parse_pdb_info(view) {
// First, check _NT_SYMBOL_PATH
if let Ok(sym_path) = env::var("_NT_SYMBOL_PATH") {
let stores = if let Ok(default_cache) = active_local_cache(Some(view)) {
parse_sym_srv(&sym_path, &default_cache)
} else {
Err(anyhow!("No local cache found"))
};
if let Ok(stores) = stores {
for store in stores {
match search_sym_store(&store, &info) {
Ok(Some(conts)) => {
match self
.load_from_file(&conts, debug_info, view, &progress, true, true)
{
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Skipping, {}", e.to_string()),
}
}
Ok(None) => {}
e => error!("Error searching symbol store {}: {:?}", store, e),
}
}
}
}
// Does the raw path just exist?
if PathBuf::from(&info.path).exists() {
match fs::read(&info.path) {
Ok(conts) => match self
.load_from_file(&conts, debug_info, view, &progress, true, false)
{
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Skipping, {}", e.to_string()),
},
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Could not read pdb: {}", e.to_string()),
}
}
// Try in the same directory as the file
let mut potential_path = PathBuf::from(view.file().filename().to_string());
potential_path.pop();
potential_path.push(&info.file_name);
if potential_path.exists() {
match fs::read(
&potential_path
.to_str()
.expect("Potential path is a real string")
.to_string(),
) {
Ok(conts) => match self
.load_from_file(&conts, debug_info, view, &progress, true, false)
{
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Skipping, {}", e.to_string()),
},
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Could not read pdb: {}", e.to_string()),
}
}
// Check the local symbol store
if let Ok(local_store_path) = active_local_cache(Some(view)) {
match search_sym_store(&local_store_path, &info) {
Ok(Some(conts)) => {
match self.load_from_file(&conts, debug_info, view, &progress, true, false)
{
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Skipping, {}", e.to_string()),
}
}
Ok(None) => {}
e => error!(
"Error searching local symbol store {}: {:?}",
local_store_path, e
),
}
}
// Next, try downloading from all symbol servers in the server list
let server_list =
Settings::new("").get_string_list("pdb.files.symbolServerList", Some(view), None);
for server in server_list.iter() {
match search_sym_store(&server.to_string(), &info) {
Ok(Some(conts)) => {
match self.load_from_file(&conts, debug_info, view, &progress, true, true) {
Ok(_) => return true,
Err(e) if e.to_string() == "Cancelled" => return false,
Err(e) => debug!("Skipping, {}", e.to_string()),
}
}
Ok(None) => {}
e => error!("Error searching remote symbol server {}: {:?}", server, e),
}
}
}
false
}
}
#[cfg(not(feature = "demo"))]
#[no_mangle]
pub extern "C" fn CorePluginDependencies() {
add_optional_plugin_dependency("view_pe");
}
#[cfg(not(feature = "demo"))]
#[no_mangle]
pub extern "C" fn CorePluginInit() -> bool {
init_plugin()
}
#[cfg(feature = "demo")]
#[no_mangle]
pub extern "C" fn PDBPluginInit() -> bool {
init_plugin()
}
fn init_plugin() -> bool {
let _ = logger::init(LevelFilter::Debug);
DebugInfoParser::register("PDB", PDBParser {});
let settings = Settings::new("");
settings.register_group("pdb", "PDB Loader");
settings.register_setting_json(
"pdb.files.localStoreAbsolute",
r#"{
"title" : "Local Symbol Store Absolute Path",
"type" : "string",
"default" : "",
"aliases" : ["pdb.local-store-absolute", "pdb.localStoreAbsolute"],
"description" : "Absolute path specifying where the PDB symbol store exists on this machine, overrides relative path.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.files.localStoreRelative",
r#"{
"title" : "Local Symbol Store Relative Path",
"type" : "string",
"default" : "symbols",
"aliases" : ["pdb.local-store-relative", "pdb.localStoreRelative"],
"description" : "Path *relative* to the binaryninja _user_ directory, specifying the pdb symbol store. If the Local Symbol Store Absolute Path is specified, this is ignored.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.files.localStoreCache",
r#"{
"title" : "Cache Downloaded PDBs in Local Store",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.localStoreCache"],
"description" : "Store PDBs downloaded from Symbol Servers in the local Symbol Store Path.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"network.pdbAutoDownload",
r#"{
"title" : "Enable Auto Downloading PDBs",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.autoDownload", "pdb.auto-download-pdb"],
"description" : "Automatically search for and download pdb files from specified symbol servers.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.files.symbolServerList",
r#"{
"title" : "Symbol Server List",
"type" : "array",
"elementType" : "string",
"sorted" : false,
"default" : ["https://msdl.microsoft.com/download/symbols"],
"aliases" : ["pdb.symbol-server-list", "pdb.symbolServerList"],
"description" : "List of servers to query for pdb symbols.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.expandRTTIStructures",
r#"{
"title" : "Expand RTTI Structures",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.expandRTTIStructures"],
"description" : "Create structures for RTTI symbols with variable-sized names and arrays.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.generateVTables",
r#"{
"title" : "Generate Virtual Table Structures",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.generateVTables"],
"description" : "Create Virtual Table (VTable) structures for C++ classes found when parsing.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.loadGlobalSymbols",
r#"{
"title" : "Load Global Module Symbols",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.loadGlobalSymbols"],
"description" : "Load symbols in the Global module of the PDB. These symbols have generally lower quality types due to relying on the demangler.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.allowUnnamedVoidSymbols",
r#"{
"title" : "Allow Unnamed Untyped Symbols",
"type" : "boolean",
"default" : false,
"aliases" : ["pdb.allowUnnamedVoidSymbols"],
"description" : "Allow creation of symbols with no name and void types, often used as static local variables. Generally, these are just noisy and not relevant.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.allowVoidGlobals",
r#"{
"title" : "Allow Untyped Symbols",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.allowVoidGlobals"],
"description" : "Allow creation of symbols that have no type, and will be created as void-typed symbols. Generally, this happens in a stripped PDB when a Global symbol's mangled name does not contain type information.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.createMissingNamedTypes",
r#"{
"title" : "Create Missing Named Types",
"type" : "boolean",
"default" : true,
"aliases" : ["pdb.createMissingNamedTypes"],
"description" : "Allow creation of types named by function signatures which are not found in the PDB's types list or the Binary View. These types are usually found in stripped PDBs that have no type information but function signatures reference the stripped types.",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.loadMismatchedPDB",
r#"{
"title" : "Load Mismatched PDB",
"type" : "string",
"default" : "ask",
"enum" : ["true", "ask", "false"],
"enumDescriptions" : [
"Always load the PDB",
"Use the Interaction system to ask if the PDB should be loaded",
"Never load the PDB"
],
"aliases" : [],
"description" : "If a manually loaded PDB has a mismatched GUID, should it be loaded?",
"ignore" : []
}"#,
);
settings.register_setting_json(
"pdb.features.parseSymbols",
r#"{
"title" : "Parse PDB Symbols",
"type" : "boolean",
"default" : true,
"aliases" : [],
"description" : "Parse Symbol names and types. If you turn this off, you will only load Types.",
"ignore" : []
}"#,
);
true
}
#[test]
fn test_default_cache_path() {
println!("{:?}", default_local_cache());
}
#[test]
fn test_sym_srv() {
assert_eq!(
parse_sym_srv(
&r"srv*\\mybuilds\mysymbols".to_string(),
&r"DEFAULT_STORE".to_string()
)
.expect("parse success")
.collect::<Vec<_>>(),
vec![r"\\mybuilds\mysymbols".to_string()]
);
assert_eq!(
parse_sym_srv(
&r"srv*c:\localsymbols*\\mybuilds\mysymbols".to_string(),
&r"DEFAULT_STORE".to_string()
)
.expect("parse success")
.collect::<Vec<_>>(),
vec![
r"c:\localsymbols".to_string(),
r"\\mybuilds\mysymbols".to_string()
]
);
assert_eq!(
parse_sym_srv(
&r"srv**\\mybuilds\mysymbols".to_string(),
&r"DEFAULT_STORE".to_string()
)
.expect("parse success")
.collect::<Vec<_>>(),
vec![
r"DEFAULT_STORE".to_string(),
r"\\mybuilds\mysymbols".to_string()
]
);
assert_eq!(
parse_sym_srv(
&r"srv*c:\localsymbols*\\NearbyServer\store*https://DistantServer".to_string(),
&r"DEFAULT_STORE".to_string()
)
.expect("parse success")
.collect::<Vec<_>>(),
vec![
r"c:\localsymbols".to_string(),
r"\\NearbyServer\store".to_string(),
r"https://DistantServer".to_string()
]
);
assert_eq!(
parse_sym_srv(
&r"srv*c:\DownstreamStore*https://msdl.microsoft.com/download/symbols".to_string(),
&r"DEFAULT_STORE".to_string()
)
.expect("parse success")
.collect::<Vec<_>>(),
vec![
r"c:\DownstreamStore".to_string(),
r"https://msdl.microsoft.com/download/symbols".to_string()
]
);
}

View File

@@ -0,0 +1,508 @@
// Copyright 2022-2024 Vector 35 Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::{BTreeMap, HashMap, HashSet};
use std::env;
use std::fmt::Display;
use std::sync::OnceLock;
use anyhow::{anyhow, Result};
use log::{debug, info};
use pdb::*;
use binaryninja::architecture::{Architecture, CoreArchitecture};
use binaryninja::binaryview::{BinaryView, BinaryViewExt};
use binaryninja::callingconvention::CallingConvention;
use binaryninja::debuginfo::{DebugFunctionInfo, DebugInfo};
use binaryninja::platform::Platform;
use binaryninja::rc::Ref;
use binaryninja::settings::Settings;
use binaryninja::types::{
min_confidence, Conf, DataVariableAndName, EnumerationBuilder, NamedTypeReference,
NamedTypeReferenceClass, StructureBuilder, StructureType, Type, TypeClass,
};
use crate::symbol_parser::{ParsedDataSymbol, ParsedProcedure, ParsedSymbol};
use crate::type_parser::ParsedType;
/// Megastruct for all the parsing
/// Certain fields are only used by specific files, as marked below.
/// Why not make new structs for them? Because vvvv this garbage
pub struct PDBParserInstance<'a, S: Source<'a> + 'a> {
/// DebugInfo where types/functions will be stored eventually
pub(crate) debug_info: &'a mut DebugInfo,
/// Parent binary view (usually during BinaryView::Finalize)
pub(crate) bv: &'a BinaryView,
/// Default arch of self.bv
pub(crate) arch: CoreArchitecture,
/// Default calling convention for self.arch
pub(crate) default_cc: Ref<CallingConvention<CoreArchitecture>>,
/// Thiscall calling convention for self.bv, or default_cc if we can't find one
pub(crate) thiscall_cc: Ref<CallingConvention<CoreArchitecture>>,
/// Cdecl calling convention for self.bv, or default_cc if we can't find one
pub(crate) cdecl_cc: Ref<CallingConvention<CoreArchitecture>>,
/// Default platform of self.bv
pub(crate) platform: Ref<Platform>,
/// pdb-rs structure for making lifetime hell a real place
pub(crate) pdb: PDB<'a, S>,
/// pdb-rs Mapping of modules to addresses for resolving RVAs
pub(crate) address_map: AddressMap<'a>,
/// Binja Settings instance (for optimization)
pub(crate) settings: Ref<Settings>,
/// type_parser.rs
/// TypeIndex -> ParsedType enum used during parsing
pub(crate) indexed_types: BTreeMap<TypeIndex, ParsedType>,
/// QName -> Binja Type for finished types
pub(crate) named_types: BTreeMap<String, Ref<Type>>,
/// Raw (mangled) name -> TypeIndex for resolving forward references
pub(crate) full_type_indices: BTreeMap<String, TypeIndex>,
/// Stack of types we're currently parsing
pub(crate) type_stack: Vec<TypeIndex>,
/// Stack of parent types we're parsing nested types inside of
pub(crate) namespace_stack: Vec<String>,
/// Type Index -> Does it return on the stack
pub(crate) type_default_returnable: BTreeMap<TypeIndex, bool>,
/// symbol_parser.rs
/// List of fully parsed symbols from all modules
pub(crate) parsed_symbols: Vec<ParsedSymbol>,
/// Raw name -> index in parsed_symbols
pub(crate) parsed_symbols_by_name: BTreeMap<String, usize>,
/// Raw name -> Symbol index for looking up symbols for the currently parsing module (mostly for thunks)
pub(crate) named_symbols: BTreeMap<String, SymbolIndex>,
/// Parent -> Children symbol index tree for the currently parsing module
pub(crate) symbol_tree: BTreeMap<SymbolIndex, Vec<SymbolIndex>>,
/// Child -> Parent symbol index mapping, inverse of symbol_tree
pub(crate) symbol_parents: BTreeMap<SymbolIndex, SymbolIndex>,
/// Stack of (start, end) indices for the current symbols being parsed while constructing the tree
pub(crate) symbol_stack: Vec<(SymbolIndex, SymbolIndex)>,
/// Index -> parsed symbol for the currently parsing module
pub(crate) indexed_symbols: BTreeMap<SymbolIndex, ParsedSymbol>,
/// Symbol address -> Symbol for looking up by address
pub(crate) addressed_symbols: BTreeMap<u64, Vec<ParsedSymbol>>,
/// CPU type of the currently parsing module
pub(crate) module_cpu_type: Option<CPUType>,
}
impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> {
/// Try to create a new parser instance from a given bv/pdb
pub fn new(
debug_info: &'a mut DebugInfo,
bv: &'a BinaryView,
mut pdb: PDB<'a, S>,
) -> Result<Self> {
let arch = if let Some(arch) = bv.default_arch() {
arch
} else {
return Err(anyhow!("Cannot parse to view with no architecture"));
};
let platform = bv
.default_platform()
.expect("Expected bv to have a platform");
let address_map = pdb.address_map()?;
let default_cc = platform
.get_default_calling_convention()
.expect("Expected default calling convention");
let thiscall_cc = Self::find_calling_convention(platform.as_ref(), "thiscall")
.unwrap_or(default_cc.clone());
let cdecl_cc = platform
.get_cdecl_calling_convention()
.unwrap_or(default_cc.clone());
Ok(Self {
debug_info,
bv,
arch,
default_cc,
thiscall_cc,
cdecl_cc,
platform,
pdb,
address_map,
settings: Settings::new(""),
indexed_types: Default::default(),
named_types: Default::default(),
full_type_indices: Default::default(),
type_stack: Default::default(),
namespace_stack: Default::default(),
type_default_returnable: Default::default(),
parsed_symbols: Default::default(),
parsed_symbols_by_name: Default::default(),
named_symbols: Default::default(),
symbol_tree: Default::default(),
symbol_parents: Default::default(),
symbol_stack: Default::default(),
indexed_symbols: Default::default(),
addressed_symbols: Default::default(),
module_cpu_type: None,
})
}
/// Try to parse the pdb into the DebugInfo
pub fn try_parse_info(
&mut self,
progress: Box<dyn Fn(usize, usize) -> Result<()> + 'a>,
) -> Result<()> {
self.parse_types(Self::split_progress(&progress, 0, &[1.0, 3.0, 0.5, 0.5]))?;
for (name, ty) in self.named_types.iter() {
self.debug_info.add_type(name, ty.as_ref(), &[]); // TODO : Components
}
info!(
"PDB found {} types (before resolving NTRs)",
self.named_types.len()
);
if self
.settings
.get_bool("pdb.features.parseSymbols", Some(self.bv), None)
{
let (symbols, functions) =
self.parse_symbols(Self::split_progress(&progress, 1, &[1.0, 3.0, 0.5, 0.5]))?;
if self
.settings
.get_bool("pdb.features.createMissingNamedTypes", Some(self.bv), None)
{
self.resolve_missing_ntrs(
&symbols,
Self::split_progress(&progress, 2, &[1.0, 3.0, 0.5, 0.5]),
)?;
self.resolve_missing_ntrs(
&functions,
Self::split_progress(&progress, 3, &[1.0, 3.0, 0.5, 0.5]),
)?;
}
info!("PDB found {} types", self.named_types.len());
info!("PDB found {} data variables", symbols.len());
info!("PDB found {} functions", functions.len());
let allow_void =
self.settings
.get_bool("pdb.features.allowVoidGlobals", Some(self.bv), None);
let min_confidence_type = Conf::new(Type::void(), min_confidence());
for sym in symbols.iter() {
match sym {
ParsedSymbol::Data(ParsedDataSymbol {
address,
name,
type_,
..
}) => {
let real_type =
type_.as_ref().unwrap_or(&min_confidence_type);
if real_type.contents.type_class() == TypeClass::VoidTypeClass {
if !allow_void {
self.log(|| {
format!("Not adding void-typed symbol {:?}@{:x}", name, address)
});
continue;
}
}
self.log(|| {
format!(
"Adding data variable: 0x{:x}: {} {:?}",
address, &name.raw_name, real_type
)
});
self.debug_info
.add_data_variable_info(DataVariableAndName::new(
*address,
real_type.clone(),
true,
name.full_name.as_ref().unwrap_or(&name.raw_name),
));
}
s => {
self.log(|| format!("Not adding non-data symbol {:?}", s));
}
}
}
for sym in functions {
match sym {
ParsedSymbol::Procedure(ParsedProcedure {
address,
name,
type_,
locals: _,
..
}) => {
self.log(|| {
format!(
"Adding function: 0x{:x}: {} {:?}",
address, &name.raw_name, type_
)
});
self.debug_info.add_function(DebugFunctionInfo::new(
Some(name.short_name.unwrap_or(name.raw_name.clone())),
Some(name.full_name.unwrap_or(name.raw_name.clone())),
Some(name.raw_name),
type_.clone().and_then(|conf| {
// TODO: When DebugInfo support confidence on function types, remove this
if conf.confidence == 0 {
None
} else {
Some(conf.contents)
}
}),
Some(address),
Some(self.platform.clone()),
vec![], // TODO : Components
vec![], //TODO: local variables
));
}
_ => {}
}
}
}
Ok(())
}
fn collect_name(
&self,
name: &NamedTypeReference,
unknown_names: &mut HashMap<String, NamedTypeReferenceClass>,
) {
let used_name = name.name().to_string();
if let Some(&found) =
unknown_names.get(&used_name)
{
if found != name.class() {
// Interesting case, not sure we care
self.log(|| {
format!(
"Mismatch unknown NTR class for {}: {} ?",
&used_name,
name.class() as u32
)
});
}
} else {
self.log(|| format!("Found new unused name: {}", &used_name));
unknown_names.insert(used_name, name.class());
}
}
fn collect_names(
&self,
ty: &Type,
unknown_names: &mut HashMap<String, NamedTypeReferenceClass>,
) {
match ty.type_class() {
TypeClass::StructureTypeClass => {
if let Ok(structure) = ty.get_structure() {
if let Ok(members) = structure.members() {
for member in members {
self.collect_names(member.ty.contents.as_ref(), unknown_names);
}
}
if let Ok(bases) = structure.base_structures() {
for base in bases {
self.collect_name(base.ty.as_ref(), unknown_names);
}
}
}
}
TypeClass::PointerTypeClass => {
if let Ok(target) = ty.target() {
self.collect_names(target.contents.as_ref(), unknown_names);
}
}
TypeClass::ArrayTypeClass => {
if let Ok(element_type) = ty.element_type() {
self.collect_names(element_type.contents.as_ref(), unknown_names);
}
}
TypeClass::FunctionTypeClass => {
if let Ok(return_value) = ty.return_value() {
self.collect_names(return_value.contents.as_ref(), unknown_names);
}
if let Ok(params) = ty.parameters() {
for param in params {
self.collect_names(param.t.contents.as_ref(), unknown_names);
}
}
}
TypeClass::NamedTypeReferenceClass => {
if let Ok(ntr) = ty.get_named_type_reference() {
self.collect_name(ntr.as_ref(), unknown_names);
}
}
_ => {}
}
}
fn resolve_missing_ntrs(
&mut self,
symbols: &Vec<ParsedSymbol>,
progress: Box<dyn Fn(usize, usize) -> Result<()> + '_>,
) -> Result<()> {
let mut unknown_names = HashMap::new();
let mut known_names = self
.bv
.types()
.iter()
.map(|qnat| qnat.name().string())
.collect::<HashSet<_>>();
for ty in &self.named_types {
known_names.insert(ty.0.clone());
}
let count = symbols.len();
for (i, sym) in symbols.into_iter().enumerate() {
match sym {
ParsedSymbol::Data(ParsedDataSymbol {
type_: Some(type_), ..
}) => {
self.collect_names(type_.contents.as_ref(), &mut unknown_names);
}
ParsedSymbol::Procedure(ParsedProcedure {
type_: Some(type_),
locals,
..
}) => {
self.collect_names(type_.contents.as_ref(), &mut unknown_names);
for l in locals {
if let Some(ltype) = &l.type_ {
self.collect_names(ltype.contents.as_ref(), &mut unknown_names);
}
}
}
_ => {}
}
(progress)(i, count)?;
}
for (name, class) in unknown_names.into_iter() {
if known_names.contains(&name) {
self.log(|| format!("Found referenced name and ignoring: {}", &name));
continue;
}
self.log(|| format!("Adding referenced but unknown type {} (likely due to demangled name and stripped type)", &name));
match class {
NamedTypeReferenceClass::UnknownNamedTypeClass
| NamedTypeReferenceClass::TypedefNamedTypeClass => {
self.debug_info.add_type(name, Type::void().as_ref(), &[]); // TODO : Components
}
NamedTypeReferenceClass::ClassNamedTypeClass
| NamedTypeReferenceClass::StructNamedTypeClass
| NamedTypeReferenceClass::UnionNamedTypeClass => {
let structure = StructureBuilder::new();
match class {
NamedTypeReferenceClass::ClassNamedTypeClass => {
structure.set_structure_type(StructureType::ClassStructureType);
}
NamedTypeReferenceClass::StructNamedTypeClass => {
structure.set_structure_type(StructureType::StructStructureType);
}
NamedTypeReferenceClass::UnionNamedTypeClass => {
structure.set_structure_type(StructureType::UnionStructureType);
}
_ => {}
}
structure.set_width(1);
structure.set_alignment(1);
self.debug_info.add_type(
name,
Type::structure(structure.finalize().as_ref()).as_ref(),
&[], // TODO : Components
);
}
NamedTypeReferenceClass::EnumNamedTypeClass => {
let enumeration = EnumerationBuilder::new();
self.debug_info.add_type(
name,
Type::enumeration(
enumeration.finalize().as_ref(),
self.arch.default_integer_size(),
false,
)
.as_ref(),
&[], // TODO : Components
);
}
}
}
Ok(())
}
/// Lazy logging function that prints like 20MB of messages
pub(crate) fn log<F: FnOnce() -> D, D: Display>(&self, msg: F) {
static MEM: OnceLock<bool> = OnceLock::new();
let debug_pdb = MEM.get_or_init(|| {
env::var("BN_DEBUG_PDB").is_ok()
});
if *debug_pdb {
let space = "\t".repeat(self.type_stack.len()) + &"\t".repeat(self.symbol_stack.len());
let msg = format!("{}", msg());
debug!(
"{}{}",
space,
msg.replace("\n", &*("\n".to_string() + &space))
);
}
}
pub(crate) fn split_progress<'b, F: Fn(usize, usize) -> Result<()> + 'b>(
original_fn: F,
subpart: usize,
subpart_weights: &[f64],
) -> Box<dyn Fn(usize, usize) -> Result<()> + 'b> {
// Normalize weights
let weight_sum: f64 = subpart_weights.iter().sum();
if weight_sum < 0.0001 {
return Box::new(|_, _| Ok(()));
}
// Keep a running count of weights for the start
let mut subpart_starts = vec![];
let mut start = 0f64;
for w in subpart_weights {
subpart_starts.push(start);
start += *w;
}
let subpart_start = subpart_starts[subpart] / weight_sum;
let weight = subpart_weights[subpart] / weight_sum;
Box::new(move |cur: usize, max: usize| {
// Just use a large number for easy divisibility
let steps = 1000000f64;
let subpart_size = steps * weight;
let subpart_progress = ((cur as f64) / (max as f64)) * subpart_size;
original_fn(
(subpart_start * steps + subpart_progress) as usize,
steps as usize,
)
})
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -31,9 +31,7 @@ fn main() {
.get_data(),
addr,
) {
tokens
.iter()
.for_each(|token| print!("{}", token.text().as_str()));
tokens.iter().for_each(|token| print!("{}", token.text()));
println!();
}
}