update rust crate
This commit is contained in:
1
examples/pdb-ng/.gitignore
vendored
Normal file
1
examples/pdb-ng/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
target
|
||||
138
examples/pdb-ng/CMakeLists.txt
Normal file
138
examples/pdb-ng/CMakeLists.txt
Normal 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
540
examples/pdb-ng/Cargo.lock
generated
Normal 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"
|
||||
20
examples/pdb-ng/Cargo.toml
Normal file
20
examples/pdb-ng/Cargo.toml
Normal 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
555
examples/pdb-ng/demo/Cargo.lock
generated
Normal 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"
|
||||
21
examples/pdb-ng/demo/Cargo.toml
Normal file
21
examples/pdb-ng/demo/Cargo.toml
Normal 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
937
examples/pdb-ng/src/lib.rs
Normal 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()
|
||||
]
|
||||
);
|
||||
}
|
||||
508
examples/pdb-ng/src/parser.rs
Normal file
508
examples/pdb-ng/src/parser.rs
Normal 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,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
1164
examples/pdb-ng/src/struct_grouper.rs
Normal file
1164
examples/pdb-ng/src/struct_grouper.rs
Normal file
File diff suppressed because it is too large
Load Diff
2061
examples/pdb-ng/src/symbol_parser.rs
Normal file
2061
examples/pdb-ng/src/symbol_parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
2477
examples/pdb-ng/src/type_parser.rs
Normal file
2477
examples/pdb-ng/src/type_parser.rs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user