Added extra tool and update cargo deps

This commit is contained in:
xavo95 2025-03-27 00:19:21 +01:00
parent 631721ffe6
commit aa5b2906d0
Signed by: xavo95
GPG key ID: CBF8ADED6DEBB783
6 changed files with 449 additions and 39 deletions

128
Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "aes-key-finder"
@ -10,12 +10,24 @@ dependencies = [
"offset-finder",
]
[[package]]
name = "anyhow"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
[[package]]
name = "crc"
version = "3.2.1"
@ -60,21 +72,30 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
name = "cursor"
version = "0.1.0"
dependencies = [
"thiserror 2.0.9",
"thiserror 2.0.12",
"widestring",
]
[[package]]
name = "descriptor-to-proto-rs"
version = "0.1.0"
dependencies = [
"prost",
"prost-types",
"thiserror 2.0.12",
]
[[package]]
name = "either"
version = "1.13.0"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "goblin"
version = "0.9.2"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53ab3f32d1d77146981dea5d6b1e8fe31eedcb7013e5e00d6ccd1259a4b4d923"
checksum = "daa0a64d21a7eb230583b4c5f4e23b7e4e57974f96620f42a7e75e08ae66d745"
dependencies = [
"log",
"plain",
@ -82,10 +103,19 @@ dependencies = [
]
[[package]]
name = "log"
version = "0.4.22"
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lzma-rs"
@ -106,7 +136,7 @@ dependencies = [
"patternscanner",
"pe-utils",
"serde",
"thiserror 2.0.9",
"thiserror 2.0.12",
]
[[package]]
@ -124,7 +154,7 @@ name = "pe-utils"
version = "0.1.0"
dependencies = [
"goblin",
"thiserror 2.0.9",
"thiserror 2.0.12",
]
[[package]]
@ -135,18 +165,50 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
[[package]]
name = "proc-macro2"
version = "1.0.92"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
name = "prost"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "prost-types"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16"
dependencies = [
"prost",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
@ -178,7 +240,7 @@ dependencies = [
"goblin",
"log",
"pe-utils",
"thiserror 2.0.9",
"thiserror 2.0.12",
]
[[package]]
@ -203,18 +265,18 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.217"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
@ -223,9 +285,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.94"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "987bc0be1cdea8b10216bd06e2ca407d40b9543468fafd3ddfb02f36e77f71f3"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
@ -243,11 +305,11 @@ dependencies = [
[[package]]
name = "thiserror"
version = "2.0.9"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl 2.0.9",
"thiserror-impl 2.0.12",
]
[[package]]
@ -263,9 +325,9 @@ dependencies = [
[[package]]
name = "thiserror-impl"
version = "2.0.9"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
@ -277,17 +339,17 @@ name = "tlzma"
version = "0.1.0"
dependencies = [
"lzma-rs",
"thiserror 2.0.9",
"thiserror 2.0.12",
]
[[package]]
name = "unicode-ident"
version = "1.0.14"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "widestring"
version = "1.1.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d"

View file

@ -3,6 +3,7 @@ resolver = "2"
members = [
"aes-key-finder",
"cursor",
"descriptor-to-proto-rs",
"offset-finder",
"pe-utils",
"restorer",
@ -11,16 +12,18 @@ members = [
[workspace.package]
version = "0.1.0"
edition = "2021"
edition = "2024"
[workspace.dependencies]
goblin = "0.9.2"
log = "0.4.22"
goblin = "0.9.3"
log = "0.4.27"
lzma-rs = { version = "0.3.0", features = ["raw_decoder"] }
patternscanner = "0.5.0"
serde = { version = "1.0.217", features = ["derive"] }
thiserror = "2.0.9"
widestring = "1.1.0"
prost = "0.13.5"
prost-types = "0.13.5"
serde = { version = "1.0.219", features = ["derive"] }
thiserror = "2.0.12"
widestring = "1.2.0"
offset-finder = { path = "offset-finder" }
pe-utils = { path = "pe-utils" }

View file

@ -15,6 +15,8 @@ dump? So I hope this helps people wanting to learn to
- Cursor
- An implementation for a cursor Read + Write. Rust already has one, but I needed something like string and
wide string parsing, so I created my own
- Descriptor to Proto
- Tool to convert protocol descriptors back to protocol buffer files
- Offset Finder
- This library allows to find patterns in executables
- Allows to find either exact or partial matches by leveraging wildcards(??)

View file

@ -0,0 +1,9 @@
[package]
name = "descriptor-to-proto-rs"
version.workspace = true
edition.workspace = true
[dependencies]
prost.workspace = true
prost-types.workspace = true
thiserror.workspace = true

View file

@ -0,0 +1,62 @@
use prost_types::field_descriptor_proto::Type;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ExtendError {
#[error("Type group should be deprecated")]
GroupTypeDeprecated,
#[error("Explicit type name required when type is message or enum")]
ExplicitTypeRequired,
}
pub(crate) trait AsStrType {
fn as_str_type<'a>(&self, explicit_type: Option<&'a String>) -> Result<&'a str, ExtendError>;
#[allow(unused)]
fn as_string_type(&self, package: Option<&String>, explicit_type: Option<String>) -> Result<String, ExtendError>;
}
impl AsStrType for Type {
fn as_str_type<'a>(&self, explicit_type: Option<&'a String>) -> Result<&'a str, ExtendError> {
match self {
Type::Double => Ok("double"),
Type::Float => Ok("float"),
Type::Int64 => Ok("int64"),
Type::Uint64 => Ok("uint64"),
Type::Int32 => Ok("int32"),
Type::Fixed64 => Ok("fixed64"),
Type::Fixed32 => Ok("fixed32"),
Type::Bool => Ok("bool"),
Type::String => Ok("string"),
Type::Group => Err(ExtendError::GroupTypeDeprecated),
Type::Message | Type::Enum => Ok(explicit_type.unwrap().as_str()),
Type::Bytes => Ok("bytes"),
Type::Uint32 => Ok("uint32"),
Type::Sfixed32 => Ok("sfixed64"),
Type::Sfixed64 => Ok("sfixed64"),
Type::Sint32 => Ok("sint32"),
Type::Sint64 => Ok("sint32"),
}
}
fn as_string_type(&self, package: Option<&String>, explicit_type: Option<String>) -> Result<String, ExtendError> {
match self {
Type::Group => Err(ExtendError::GroupTypeDeprecated),
Type::Message | Type::Enum => {
match explicit_type {
None => Err(ExtendError::ExplicitTypeRequired),
Some(explicit_type) => {
let new_type = explicit_type.clone();
let mut new_type_str = new_type.as_str();
new_type_str = explicit_type.strip_prefix(".").unwrap_or(new_type_str);
if let Some(package) = package {
new_type_str = new_type_str.strip_prefix(package).unwrap_or(new_type_str);
new_type_str = new_type_str.strip_prefix(".").unwrap_or(new_type_str);
}
Ok(new_type_str.to_string())
}
}
}
_ => Ok(self.as_str_type(None)?.to_string()),
}
}
}

View file

@ -0,0 +1,272 @@
mod extend;
use crate::extend::AsStrType;
use prost::Message;
use prost_types::field_descriptor_proto::Label;
use prost_types::{
DescriptorProto, EnumDescriptorProto, FieldDescriptorProto, FileDescriptorProto, FileOptions,
ServiceDescriptorProto, SourceCodeInfo,
};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConverterError {
#[error("Proto decoding error")]
DecodeError(#[from] prost::DecodeError),
#[error("Internal extend error")]
ExtendError(#[from] extend::ExtendError),
#[error("{0}")]
GenericError(String),
}
enum Syntax {
Proto2,
Proto3,
}
pub fn process_file_descriptor_set(
file_descriptor_set_data: &[u8],
) -> Result<HashMap<String, String>, ConverterError> {
let mut descriptors = HashMap::new();
let file_descriptor_set = prost_types::FileDescriptorSet::decode(file_descriptor_set_data)?;
for descriptor_proto in &file_descriptor_set.file {
let (file_name, content) = process_file_descriptor(descriptor_proto)?;
descriptors.insert(file_name, content);
}
Ok(descriptors)
}
fn process_file_descriptor(
descriptor: &FileDescriptorProto,
) -> Result<(String, String), ConverterError> {
let mut buffer = String::with_capacity(1024 * 1024); // 1MB
let syntax = process_version(&mut buffer, descriptor.syntax.as_ref());
let name = descriptor.name().to_string();
process_package(&mut buffer, descriptor.package.as_ref());
if let Some(options) = &descriptor.options {
process_options(&mut buffer, options)?;
}
for dependency in &descriptor.dependency {
buffer.push_str(format!("import \"{dependency}\";\n").as_str());
}
buffer.push('\n');
// TODO: Hande public_dependency and weak_dependency
process_enum(&mut buffer, &descriptor.enum_type)?;
process_message(
&mut buffer,
&syntax,
&descriptor.message_type,
descriptor.package.as_ref(),
)?;
process_service(&mut buffer, &descriptor.service)?;
process_extension(&mut buffer, &descriptor.extension)?;
if let Some(source_code_info) = &descriptor.source_code_info {
process_source_code_info(&mut buffer, source_code_info)?;
}
Ok((name, buffer))
}
fn process_version(buffer: &mut String, syntax: Option<&String>) -> Syntax {
// According to google, if no edition or syntax is specified, one shall default to proto2
let syntax = syntax.cloned().unwrap_or("proto2".to_string());
buffer.push_str(format!("syntax = \"{syntax}\";\n\n").as_str());
match syntax.as_str() {
"proto3" => Syntax::Proto3,
_ => Syntax::Proto2,
}
}
fn process_package(buffer: &mut String, package: Option<&String>) {
if let Some(package) = package {
buffer.push_str(format!("package {package};\n\n").as_str());
}
}
fn process_message(
buffer: &mut String,
syntax: &Syntax,
messages: &[DescriptorProto],
package: Option<&String>,
) -> Result<(), ConverterError> {
for message in messages {
buffer.push_str(format!("message {} {{\n", message.name()).as_str());
for field in &message.field {
buffer.push('\t');
// TODO: use label to know if optional shall be used
match field.label() {
Label::Optional => buffer.push_str("optional "),
Label::Required => match syntax {
Syntax::Proto2 => buffer.push_str("required "),
Syntax::Proto3 => {}
},
Label::Repeated => buffer.push_str("repeated "),
}
buffer.push_str(field.r#type().as_str_type(field.type_name.as_ref())?);
buffer.push_str(format!(" {} = {}", field.name(), field.number()).as_str());
if let Some(extendee) = &field.extendee {
// TODO: Handle extendee
return Err(ConverterError::GenericError(format!(
"Descriptor extendee is not supported {extendee:?}"
)));
}
if let Some(default_value) = &field.default_value {
// TODO: Handle default_value
return Err(ConverterError::GenericError(format!(
"Descriptor default_value is not supported {default_value:?}"
)));
}
if let Some(oneof_index) = &field.oneof_index {
// TODO: Handle oneof_index
return Err(ConverterError::GenericError(format!(
"Descriptor oneof_index is not supported {oneof_index:?}"
)));
}
if let Some(json_name) = &field.json_name {
buffer.push_str(format!(" [json_name=\"{json_name}\"]").as_str());
}
buffer.push_str(";\n");
}
process_extension(buffer, &message.extension)?;
// TODO: We might require indentation without external tool
process_message(buffer, syntax, &message.nested_type, package)?;
// TODO: We might require indentation without external tool
process_enum(buffer, &message.enum_type)?;
if !message.extension_range.is_empty() {
// TODO: Handle extension_range
return Err(ConverterError::GenericError(format!(
"Descriptor extension_range is not supported {:?}",
message.extension_range
)));
}
if !message.oneof_decl.is_empty() {
// TODO: Handle oneof_decl
return Err(ConverterError::GenericError(format!(
"Descriptor oneof_decl is not supported {:?}",
message.oneof_decl
)));
}
if let Some(options) = &message.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
if !message.reserved_range.is_empty() {
// TODO: Handle reserved_range
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_range is not supported {:?}",
message.reserved_range
)));
}
if !message.reserved_name.is_empty() {
// TODO: Handle reserved_name
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_name is not supported {:?}",
message.reserved_name
)));
}
buffer.push_str("}\n\n");
}
Ok(())
}
fn process_enum(buffer: &mut String, enums: &[EnumDescriptorProto]) -> Result<(), ConverterError> {
for enum_descriptor in enums {
buffer.push_str(format!("enum {} {{\n", enum_descriptor.name()).as_str());
for variant in &enum_descriptor.value {
buffer.push_str(format!("\t{} = {};\n", variant.name(), variant.number()).as_str());
if let Some(options) = &variant.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
}
if let Some(options) = &enum_descriptor.options {
// TODO: Handle options
return Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)));
}
if !enum_descriptor.reserved_range.is_empty() {
// TODO: Handle reserved_range
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_range is not supported {:?}",
enum_descriptor.reserved_range
)));
}
if !enum_descriptor.reserved_name.is_empty() {
// TODO: Handle reserved_name
return Err(ConverterError::GenericError(format!(
"Descriptor reserved_name is not supported {:?}",
enum_descriptor.reserved_name
)));
}
buffer.push_str("}\n\n");
}
Ok(())
}
fn process_service(
_buffer: &mut String,
services: &[ServiceDescriptorProto],
) -> Result<(), ConverterError> {
if !services.is_empty() {
// TODO: Handle service
return Err(ConverterError::GenericError(format!(
"Descriptor service is not supported {services:?}"
)));
}
Ok(())
}
fn process_options(_buffer: &mut String, options: &FileOptions) -> Result<(), ConverterError> {
// TODO: Handle options
Err(ConverterError::GenericError(format!(
"Descriptor options is not supported {options:?}"
)))
}
fn process_extension(
_buffer: &mut String,
extension: &[FieldDescriptorProto],
) -> Result<(), ConverterError> {
if !extension.is_empty() {
// TODO: Handle extension
return Err(ConverterError::GenericError(format!(
"Descriptor extension is not supported {extension:?}"
)));
}
Ok(())
}
fn process_source_code_info(
_buffer: &mut String,
source_code_info: &SourceCodeInfo,
) -> Result<(), ConverterError> {
// TODO: Handle source_code_info
Err(ConverterError::GenericError(format!(
"Descriptor source_code_info is not supported {source_code_info:?}"
)))
}