diff --git a/idalib-sys/src/kernwin_extras.h b/idalib-sys/src/kernwin_extras.h index 8ec60b6ca..8eaac5309 100644 --- a/idalib-sys/src/kernwin_extras.h +++ b/idalib-sys/src/kernwin_extras.h @@ -142,3 +142,13 @@ int idalib_open_database_quiet(const char *name, bool auto_analysis) { return result; } + +rust::String idalib_ea2str(ea_t ea) { + auto out = qstring(); + + if (ea2str(&out, ea)) { + return rust::String(out.c_str()); + } else { + return rust::String(); + } +} diff --git a/idalib-sys/src/lib.rs b/idalib-sys/src/lib.rs index 68d7b60b2..96cb67293 100644 --- a/idalib-sys/src/lib.rs +++ b/idalib-sys/src/lib.rs @@ -59,6 +59,7 @@ include_cpp! { #include "moves.hpp" #include "pro.h" #include "segment.hpp" + #include "strlist.hpp" #include "ua.hpp" #include "xref.hpp" @@ -330,6 +331,11 @@ include_cpp! { generate!("set_cmt") generate!("append_cmt") + // strings + generate!("build_strlist") + generate!("clear_strlist") + generate!("get_strlist_qty") + // loader generate!("plugin_t") generate!("find_plugin") @@ -695,6 +701,7 @@ mod ffix { include!("ph_extras.h"); include!("segm_extras.h"); include!("search_extras.h"); + include!("strings_extras.h"); type c_short = autocxx::c_short; type c_int = autocxx::c_int; @@ -970,6 +977,11 @@ mod ffix { unsafe fn idalib_find_imm(ea: c_ulonglong, imm: c_uint) -> c_ulonglong; unsafe fn idalib_find_defined(ea: c_ulonglong) -> c_ulonglong; + unsafe fn idalib_get_strlist_item_addr(index: usize) -> c_ulonglong; + unsafe fn idalib_get_strlist_item_length(index: usize) -> usize; + + unsafe fn idalib_ea2str(ea: c_ulonglong) -> String; + unsafe fn idalib_get_byte(ea: c_ulonglong) -> u8; unsafe fn idalib_get_word(ea: c_ulonglong) -> u16; unsafe fn idalib_get_dword(ea: c_ulonglong) -> u32; @@ -1126,6 +1138,10 @@ pub mod comments { pub use super::ffix::idalib_get_cmt; } +pub mod conversions { + pub use super::ffix::idalib_ea2str; +} + pub mod bookmarks { pub use super::ffix::{ idalib_bookmarks_t_erase, idalib_bookmarks_t_find_index, idalib_bookmarks_t_get, @@ -1137,6 +1153,11 @@ pub mod search { pub use super::ffix::{idalib_find_defined, idalib_find_imm, idalib_find_text}; } +pub mod strings { + pub use super::ffi::{build_strlist, clear_strlist, get_strlist_qty}; + pub use super::ffix::{idalib_get_strlist_item_addr, idalib_get_strlist_item_length}; +} + pub mod loader { pub use super::ffi::{find_plugin, plugin_t, run_plugin}; pub use super::ffix::{idalib_plugin_flags, idalib_plugin_version}; diff --git a/idalib-sys/src/strings_extras.h b/idalib-sys/src/strings_extras.h new file mode 100644 index 000000000..d05e14d33 --- /dev/null +++ b/idalib-sys/src/strings_extras.h @@ -0,0 +1,17 @@ +#pragma once + +#include "strlist.hpp" + +#include "cxx.h" + +ea_t idalib_get_strlist_item_addr(size_t n) { + string_info_t si; + get_strlist_item(&si, n); + return si.ea; +} + +size_t idalib_get_strlist_item_length(size_t n) { + string_info_t si; + get_strlist_item(&si, n); + return (size_t)si.length; +} diff --git a/idalib/examples/strings_ls.rs b/idalib/examples/strings_ls.rs new file mode 100644 index 000000000..bb894ac5d --- /dev/null +++ b/idalib/examples/strings_ls.rs @@ -0,0 +1,34 @@ +use idalib::idb::IDB; + +fn main() -> anyhow::Result<()> { + println!("Trying to open IDA database..."); + + // Open IDA database + let idb = IDB::open("./tests/ls")?; + + println!("Testing len(), get_by_index(), and get_address_by_index() (valid indexes)"); + // len() + for i in 0..idb.strings().len() { + /* + println!( + "\t{:#x}\t{:?}", + idb.strings().get_address_by_index(i).unwrap(), + idb.strings().get_by_index(i).unwrap() + ); + */ + // get_by_index() + assert!(idb.strings().get_by_index(i).is_some()); + // get_address_by_index() + assert!(idb.strings().get_address_by_index(i).is_some()); + } + + println!("Testing len(), get_by_index(), and get_address_by_index() (invalid index)"); + // len() + let len = idb.strings().len(); + // get_by_index() + assert!(idb.strings().get_by_index(len).is_none()); + // get_address_by_index() + assert!(idb.strings().get_address_by_index(len).is_none()); + + Ok(()) +} diff --git a/idalib/src/idb.rs b/idalib/src/idb.rs index d196327ae..c9505bf1e 100644 --- a/idalib/src/idb.rs +++ b/idalib/src/idb.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use crate::ffi::bytes::*; use crate::ffi::comments::{append_cmt, idalib_get_cmt, set_cmt}; +use crate::ffi::conversions::idalib_ea2str; use crate::ffi::entry::{get_entry, get_entry_ordinal, get_entry_qty}; use crate::ffi::func::{get_func, get_func_qty, getn_func}; use crate::ffi::hexrays::{decompile_func, init_hexrays_plugin, term_hexrays_plugin}; @@ -28,6 +29,7 @@ use crate::meta::{Metadata, MetadataMut}; use crate::plugin::Plugin; use crate::processor::Processor; use crate::segment::{Segment, SegmentId}; +use crate::strings::StringList; use crate::xref::{XRef, XRefQuery}; use crate::{prepare_library, Address, IDAError, IDARuntimeHandle}; @@ -382,6 +384,20 @@ impl IDB { } } + pub fn strings(&self) -> StringList { + StringList::new(self) + } + + pub fn address_to_string(&self, ea: Address) -> Option { + let s = unsafe { idalib_ea2str(ea.into()) }; + + if s.is_empty() { + None + } else { + Some(s) + } + } + pub fn get_byte(&self, ea: Address) -> u8 { unsafe { idalib_get_byte(ea.into()) } } diff --git a/idalib/src/lib.rs b/idalib/src/lib.rs index 54b6ffddc..06a5ad3bc 100644 --- a/idalib/src/lib.rs +++ b/idalib/src/lib.rs @@ -13,6 +13,7 @@ pub mod meta; pub mod plugin; pub mod processor; pub mod segment; +pub mod strings; pub mod xref; pub use idalib_sys as ffi; diff --git a/idalib/src/strings.rs b/idalib/src/strings.rs new file mode 100644 index 000000000..798c5f2d0 --- /dev/null +++ b/idalib/src/strings.rs @@ -0,0 +1,71 @@ +use std::marker::PhantomData; + +use crate::ffi::bytes::idalib_get_bytes; +use crate::ffi::strings::{ + build_strlist, clear_strlist, get_strlist_qty, idalib_get_strlist_item_addr, + idalib_get_strlist_item_length, +}; +use crate::ffi::BADADDR; + +use crate::idb::IDB; +use crate::Address; + +pub type StringIndex = usize; + +pub struct StringList<'a> { + _marker: PhantomData<&'a IDB>, +} + +impl<'a> StringList<'a> { + pub(crate) fn new(_: &'a IDB) -> Self { + Self { + _marker: PhantomData, + } + } + + pub fn rebuild(&self) { + unsafe { build_strlist() } + } + + pub fn clear(&self) { + unsafe { clear_strlist() } + } + + pub fn get_by_index(&self, index: StringIndex) -> Option { + let addr = self.get_address_by_index(index)?; + let size = self.get_length_by_index(index); + + // See also `IDB::get_bytes` + let mut buf = Vec::with_capacity(size); + let Ok(new_len) = (unsafe { idalib_get_bytes(addr.into(), &mut buf) }) else { + return None; + }; + unsafe { + buf.set_len(new_len); + } + + // TODO: switch to `String::from_utf8_lossy_owned` once it's stable + Some(String::from_utf8_lossy(&buf).into_owned()) + } + + pub fn get_address_by_index(&self, index: StringIndex) -> Option
{ + let addr = unsafe { idalib_get_strlist_item_addr(index) }; + if addr == BADADDR { + None + } else { + Some(addr.into()) + } + } + + fn get_length_by_index(&self, index: StringIndex) -> usize { + unsafe { idalib_get_strlist_item_length(index) } + } + + pub fn len(&self) -> StringIndex { + unsafe { get_strlist_qty() } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +}