feat(protossl-scan): accept module+offset targets for disasm/xref
Add `module+0xoffset` addressing (e.g. `anadius64.dll+0x2BB90`) to the disasm and xref commands. Offsets are stable across launches while the base is ASLR'd, so this resolves the module's current base in the running process instead of requiring a stale absolute address each session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -124,17 +124,25 @@ fn main() {
|
|||||||
// loads and call targets. Use this on a code anchor (e.g. the lea that
|
// loads and call targets. Use this on a code anchor (e.g. the lea that
|
||||||
// loads the "_ProtoSSLSendPacket" string) to read the real code.
|
// loads the "_ProtoSSLSendPacket" string) to read the real code.
|
||||||
Some("disasm") => {
|
Some("disasm") => {
|
||||||
let target = match args.get(1).and_then(|s| parse_hex(s)) {
|
let arg = match args.get(1) {
|
||||||
Some(t) => t,
|
Some(a) => a.clone(),
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Usage: protossl-scan disasm <hex-addr> [pid|name]");
|
eprintln!("Usage: protossl-scan disasm <hex-addr | module+0xoffset> [pid|name]");
|
||||||
eprintln!("Example: protossl-scan disasm 0x140EFA631");
|
eprintln!("Examples: protossl-scan disasm 0x140EFA631");
|
||||||
|
eprintln!(" protossl-scan disasm anadius64.dll+0x2BB90");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let pid = resolve_pid(args.get(2).map(|s| s.as_str()));
|
let pid = resolve_pid(args.get(2).map(|s| s.as_str()));
|
||||||
let process = open_for_read(pid);
|
let process = open_for_read(pid);
|
||||||
let modules = enumerate_modules(pid);
|
let modules = enumerate_modules(pid);
|
||||||
|
let target = match resolve_target(&arg, &modules) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
eprintln!("Could not resolve '{arg}' (unknown module or bad address)");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
run_disasm(process, &modules, target);
|
run_disasm(process, &modules, target);
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = CloseHandle(process);
|
let _ = CloseHandle(process);
|
||||||
@@ -142,12 +150,11 @@ fn main() {
|
|||||||
}
|
}
|
||||||
// xref <hex-addr> [pid|name] (power-user form, exact absolute address)
|
// xref <hex-addr> [pid|name] (power-user form, exact absolute address)
|
||||||
Some("xref") => {
|
Some("xref") => {
|
||||||
let target = match args.get(1).and_then(|s| parse_hex(s)) {
|
let arg = match args.get(1) {
|
||||||
Some(t) => t,
|
Some(a) => a.clone(),
|
||||||
None => {
|
None => {
|
||||||
eprintln!("Usage: protossl-scan xref <hex-addr> [pid|name]");
|
eprintln!("Usage: protossl-scan xref <hex-addr | module+0xoffset> [pid|name]");
|
||||||
eprintln!("Example: protossl-scan xref 0x147D198B9");
|
eprintln!("Example: protossl-scan xref 0x147D198B9");
|
||||||
eprintln!("Tip: pass the FULL absolute address, not the +offset.");
|
|
||||||
eprintln!("Or just use: protossl-scan xref-str ProtoSSLSend");
|
eprintln!("Or just use: protossl-scan xref-str ProtoSSLSend");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@@ -155,6 +162,13 @@ fn main() {
|
|||||||
let pid = resolve_pid(args.get(2).map(|s| s.as_str()));
|
let pid = resolve_pid(args.get(2).map(|s| s.as_str()));
|
||||||
let process = open_for_read(pid);
|
let process = open_for_read(pid);
|
||||||
let modules = enumerate_modules(pid);
|
let modules = enumerate_modules(pid);
|
||||||
|
let target = match resolve_target(&arg, &modules) {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
eprintln!("Could not resolve '{arg}' (unknown module or bad address)");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
run_xref(process, &modules, target);
|
run_xref(process, &modules, target);
|
||||||
unsafe {
|
unsafe {
|
||||||
let _ = CloseHandle(process);
|
let _ = CloseHandle(process);
|
||||||
@@ -525,6 +539,26 @@ fn parse_hex(s: &str) -> Option<usize> {
|
|||||||
usize::from_str_radix(trimmed, 16).ok()
|
usize::from_str_radix(trimmed, 16).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resolve a target that is either a raw hex address or a `module+0xoffset`
|
||||||
|
/// form (e.g. `anadius64.dll+0x2BB90`). The module form is ASLR-robust: it adds
|
||||||
|
/// the offset to the module's CURRENT base in the running process.
|
||||||
|
fn resolve_target(s: &str, modules: &[ModuleInfo]) -> Option<usize> {
|
||||||
|
if let Some(idx) = s.find('+') {
|
||||||
|
let name = s[..idx].trim().to_ascii_lowercase();
|
||||||
|
let want = name.strip_suffix(".dll").unwrap_or(&name);
|
||||||
|
let off = parse_hex(s[idx + 1..].trim())?;
|
||||||
|
for m in modules {
|
||||||
|
let mn = m.name.to_ascii_lowercase();
|
||||||
|
let mn = mn.strip_suffix(".dll").unwrap_or(&mn);
|
||||||
|
if mn == want {
|
||||||
|
return Some(m.base + off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
parse_hex(s)
|
||||||
|
}
|
||||||
|
|
||||||
/// Read a single u64 from the target process at `addr`. Returns None if the
|
/// Read a single u64 from the target process at `addr`. Returns None if the
|
||||||
/// memory can't be read (e.g. unmapped).
|
/// memory can't be read (e.g. unmapped).
|
||||||
fn read_u64(process: HANDLE, addr: usize) -> Option<u64> {
|
fn read_u64(process: HANDLE, addr: usize) -> Option<u64> {
|
||||||
|
|||||||
Reference in New Issue
Block a user