commit
185e0b5509
@ -1,16 +1,9 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
cd memflow_lib/memflow-win32-ffi/
|
cd memflow_lib/memflow-ffi/
|
||||||
if cargo build --release ; then
|
if cargo build --release ; then
|
||||||
cd ../memflow-qemu-procfs
|
|
||||||
|
|
||||||
if cargo build --release --all-features ; then
|
|
||||||
cd ../../
|
cd ../../
|
||||||
make
|
make
|
||||||
else
|
|
||||||
echo "Error while building memflow-qemu-procfs"
|
|
||||||
fi
|
|
||||||
|
|
||||||
else
|
else
|
||||||
echo "Error while building memflow-win32-ffi"
|
echo "Error while building memflow-ffi"
|
||||||
fi
|
fi
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
[target.'cfg(unix)']
|
||||||
|
runner = "./runner.sh"
|
@ -0,0 +1,176 @@
|
|||||||
|
name: Build and test
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
flags: [--all-features, --no-default-features]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --workspace ${{ matrix.flags }} --verbose
|
||||||
|
|
||||||
|
- name: Build examples
|
||||||
|
run: cargo build --workspace ${{ matrix.flags }} --examples --verbose
|
||||||
|
|
||||||
|
build-cross-targets:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [aarch64-unknown-linux-gnu, armv7-unknown-linux-gnueabihf, i686-unknown-linux-gnu]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --target ${{ matrix.target }} --workspace --all-features --verbose
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Pre-build binaries (for inventory integration tests)
|
||||||
|
run: cargo build --workspace --all-features --verbose
|
||||||
|
|
||||||
|
- name: Run all tests
|
||||||
|
run: cargo test --workspace --all-features --verbose
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
|
- name: Run all tests
|
||||||
|
run: cargo test --workspace --exclude memflow-derive --all-features --verbose
|
||||||
|
if: runner.os != 'Linux'
|
||||||
|
|
||||||
|
test-cross:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [aarch64-unknown-linux-gnu, i686-unknown-linux-gnu]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly # currently required due to -Zdoctest-xcompile
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
- name: Pre-build binaries (for inventory integration tests)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --target ${{ matrix.target }} --workspace --all-features --verbose --release
|
||||||
|
- name: Run all tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: test
|
||||||
|
args: -Zdoctest-xcompile --target ${{ matrix.target }} --workspace --all-features --verbose --release
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- name: Check formatting
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-targets --all-features --workspace -- -D clippy::all
|
||||||
|
|
||||||
|
build-ffi:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly # required by cglue codegen
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Validate c and c++ headers
|
||||||
|
run: cd memflow-ffi; ./verify_headers.sh
|
||||||
|
|
||||||
|
- name: Build memflow
|
||||||
|
run: cargo build --workspace --release --verbose
|
||||||
|
|
||||||
|
- name: Build c examples
|
||||||
|
run: cd memflow-ffi/examples/c; make all
|
||||||
|
- name: Build c++ examples
|
||||||
|
run: cd memflow-ffi/examples/cpp; make all
|
||||||
|
|
||||||
|
build-nostd:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Rust nightly
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly-2023-12-15
|
||||||
|
override: true
|
||||||
|
- run: rustup toolchain install nightly-2023-12-15
|
||||||
|
- run: rustup +nightly-2023-12-15 component add rust-src
|
||||||
|
- name: Build no_std crate
|
||||||
|
run: cd nostd-test; cargo +nightly-2023-12-15 build --all-features --verbose
|
||||||
|
|
||||||
|
build-coverage:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: 'memflow-repo'
|
||||||
|
- name: Set up Rust nightly
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
- run: cargo install grcov
|
||||||
|
- name: Run tests with coverage
|
||||||
|
run: |
|
||||||
|
cd memflow-repo
|
||||||
|
export CARGO_INCREMENTAL=0
|
||||||
|
export RUSTFLAGS="-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
||||||
|
export RUSTDOCFLAGS="-Cpanic=abort"
|
||||||
|
cargo build --workspace --exclude memflow-derive --all-features
|
||||||
|
cargo test --workspace --exclude memflow-derive --all-features
|
||||||
|
grcov ./target/debug/ -s . -t lcov --llvm --branch --ignore-not-existing -o ./target/debug/coverage
|
||||||
|
bash <(curl -s https://codecov.io/bash) -f ./target/debug/coverage -t ${{ secrets.CODECOV_TOKEN }};
|
@ -0,0 +1,13 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
*.swp
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
.vscode
|
||||||
|
nostd-test/target
|
||||||
|
nostd-test/Cargo.lock
|
||||||
|
*.so
|
||||||
|
.vagrant
|
||||||
|
TODO.md
|
||||||
|
TODO.txt
|
@ -1,24 +1,21 @@
|
|||||||
|
|
||||||
[profile.bench]
|
[profile.bench]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"memflow",
|
"memflow",
|
||||||
"memflow-win32",
|
|
||||||
"memflow-ffi",
|
"memflow-ffi",
|
||||||
"memflow-win32-ffi",
|
|
||||||
"memflow-bench",
|
"memflow-bench",
|
||||||
]
|
]
|
||||||
default-members = [
|
default-members = [
|
||||||
"memflow",
|
"memflow",
|
||||||
"memflow-win32",
|
|
||||||
"memflow-ffi",
|
"memflow-ffi",
|
||||||
"memflow-win32-ffi",
|
|
||||||
"memflow-bench",
|
"memflow-bench",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"nostd-test",
|
"nostd-test"
|
||||||
"memflow-qemu-procfs"
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
goblin = { git = "https://github.com/h33p/goblin", branch = "lossy-macho" }
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
# Checklist:
|
||||||
|
- memflow
|
||||||
|
- memflow-ffi
|
||||||
|
|
||||||
|
# Plugins:
|
||||||
|
- memflow-win32
|
||||||
|
- memflow-pcileech
|
||||||
|
- memflow-reclass-plugin
|
||||||
|
- memflow-native
|
||||||
|
- memflow-qemu
|
||||||
|
- memflow-kcore
|
||||||
|
- memflow-cmake-example
|
||||||
|
- memflow-kvm
|
||||||
|
- memflow-coredump
|
||||||
|
- memflow-linux
|
||||||
|
- memflow-microvmi
|
||||||
|
|
||||||
|
# Tools:
|
||||||
|
- cloudflow
|
||||||
|
- scanflow
|
||||||
|
- reflow
|
@ -1,3 +1,4 @@
|
|||||||
pub mod phys;
|
pub mod phys;
|
||||||
|
pub mod util;
|
||||||
pub mod vat;
|
pub mod vat;
|
||||||
pub mod virt;
|
pub mod virt;
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
use memflow::prelude::v1::*;
|
||||||
|
|
||||||
|
pub fn build_os(
|
||||||
|
conn_name: &str,
|
||||||
|
cache_size: usize,
|
||||||
|
os_name: &str,
|
||||||
|
use_tlb: bool,
|
||||||
|
) -> Result<OsInstanceArcBox<'static>> {
|
||||||
|
// this workaround is to prevent loaded libraries
|
||||||
|
// from spitting out to much log information and skewing benchmarks
|
||||||
|
let filter = log::max_level();
|
||||||
|
log::set_max_level(log::Level::Debug.to_level_filter());
|
||||||
|
|
||||||
|
let inventory = Inventory::scan();
|
||||||
|
|
||||||
|
log::set_max_level(log::Level::Error.to_level_filter());
|
||||||
|
|
||||||
|
let mut args = Args::new();
|
||||||
|
|
||||||
|
if !use_tlb {
|
||||||
|
args = args.insert("vatcache", "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
let page_cache_params = if cache_size > 0 {
|
||||||
|
Some(ConnectorMiddlewareArgs::new().cache_size(cache_size))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let conn_args = ConnectorArgs::new(None, Default::default(), page_cache_params);
|
||||||
|
let args = OsArgs::new(None, args);
|
||||||
|
|
||||||
|
let ret = if conn_name.is_empty() {
|
||||||
|
inventory.builder().os(os_name).args(args).build()
|
||||||
|
} else {
|
||||||
|
inventory
|
||||||
|
.builder()
|
||||||
|
.connector(conn_name)
|
||||||
|
.args(conn_args)
|
||||||
|
.os(os_name)
|
||||||
|
.args(args)
|
||||||
|
.build()
|
||||||
|
}?;
|
||||||
|
|
||||||
|
log::set_max_level(filter);
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_proc<T: Os>(os: &mut T) -> Result<(<T as Os>::ProcessType<'_>, ModuleInfo)> {
|
||||||
|
let infos = os.process_info_list()?;
|
||||||
|
|
||||||
|
let mut data = None;
|
||||||
|
|
||||||
|
for info in infos {
|
||||||
|
if let Ok(mut proc) = os.process_by_info(info.clone()) {
|
||||||
|
let mut module = None;
|
||||||
|
|
||||||
|
proc.module_list_callback(
|
||||||
|
None,
|
||||||
|
(&mut |info: ModuleInfo| {
|
||||||
|
if info.size > 0x1000 {
|
||||||
|
module = Some(info);
|
||||||
|
}
|
||||||
|
module.is_none()
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if let Some(module) = module {
|
||||||
|
data = Some((info, module));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.and_then(move |(info, module)| Some((os.process_by_info(info).ok()?, module)))
|
||||||
|
.ok_or_else(|| ErrorKind::NotFound.into())
|
||||||
|
}
|
@ -1,218 +1,129 @@
|
|||||||
use criterion::*;
|
use criterion::*;
|
||||||
|
|
||||||
use memflow::mem::{CachedMemoryAccess, CachedVirtualTranslate, PhysicalMemory, VirtualTranslate};
|
use memflow::cglue::as_mut;
|
||||||
|
use memflow::mem::virt_translate::*;
|
||||||
use memflow::architecture::ScopedVirtualTranslate;
|
use memflow::prelude::v1::*;
|
||||||
|
|
||||||
use memflow::error::Result;
|
|
||||||
use memflow::iter::FnExtend;
|
|
||||||
use memflow::process::*;
|
|
||||||
use memflow::types::*;
|
|
||||||
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand::{Rng, SeedableRng};
|
use rand::{Rng, SeedableRng};
|
||||||
use rand_xorshift::XorShiftRng as CurRng;
|
use rand_xorshift::XorShiftRng as CurRng;
|
||||||
|
|
||||||
fn vat_test_with_mem<
|
use std::convert::TryInto;
|
||||||
T: PhysicalMemory,
|
|
||||||
V: VirtualTranslate,
|
fn vat_test_with_mem(
|
||||||
S: ScopedVirtualTranslate,
|
|
||||||
M: OsProcessModuleInfo,
|
|
||||||
>(
|
|
||||||
bench: &mut Bencher,
|
bench: &mut Bencher,
|
||||||
phys_mem: &mut T,
|
vat: &mut impl VirtualTranslate,
|
||||||
vat: &mut V,
|
|
||||||
chunk_count: usize,
|
chunk_count: usize,
|
||||||
translations: usize,
|
translations: usize,
|
||||||
translator: S,
|
module: ModuleInfo,
|
||||||
module: M,
|
) {
|
||||||
) -> usize {
|
|
||||||
let mut rng = CurRng::from_rng(thread_rng()).unwrap();
|
let mut rng = CurRng::from_rng(thread_rng()).unwrap();
|
||||||
|
|
||||||
let mut bufs = vec![Address::null(); chunk_count];
|
let mut bufs = vec![CTup2(Address::null(), 1); translations];
|
||||||
let mut done_size = 0;
|
|
||||||
|
|
||||||
let mut out = Vec::new();
|
let base_addr = rng.gen_range(module.base.to_umem()..(module.base.to_umem() + module.size));
|
||||||
|
|
||||||
while done_size < translations {
|
for CTup2(address, _) in bufs.iter_mut() {
|
||||||
let base_addr = rng.gen_range(
|
*address = (base_addr + rng.gen_range(0..0x2000)).into();
|
||||||
module.base().as_u64(),
|
|
||||||
module.base().as_u64() + module.size() as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
for addr in bufs.iter_mut() {
|
|
||||||
*addr = (base_addr + rng.gen_range(0, 0x2000)).into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut out = vec![];
|
||||||
|
|
||||||
bench.iter(|| {
|
bench.iter(|| {
|
||||||
|
for chunk in bufs.chunks_mut(chunk_count) {
|
||||||
out.clear();
|
out.clear();
|
||||||
vat.virt_to_phys_iter(
|
vat.virt_to_phys_list(chunk, (&mut out).into(), (&mut |_| true).into());
|
||||||
phys_mem,
|
|
||||||
&translator,
|
|
||||||
bufs.iter_mut().map(|x| (*x, 1)),
|
|
||||||
&mut out,
|
|
||||||
&mut FnExtend::new(|_| {}),
|
|
||||||
);
|
|
||||||
black_box(&out);
|
black_box(&out);
|
||||||
});
|
|
||||||
|
|
||||||
done_size += chunk_count;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
done_size
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vat_test_with_ctx<
|
fn vat_test_with_os(
|
||||||
T: PhysicalMemory,
|
|
||||||
V: VirtualTranslate,
|
|
||||||
P: OsProcessInfo,
|
|
||||||
S: ScopedVirtualTranslate,
|
|
||||||
M: OsProcessModuleInfo,
|
|
||||||
>(
|
|
||||||
bench: &mut Bencher,
|
bench: &mut Bencher,
|
||||||
cache_size: u64,
|
|
||||||
chunks: usize,
|
chunks: usize,
|
||||||
translations: usize,
|
translations: usize,
|
||||||
use_tlb: bool,
|
os: &mut OsInstanceArcBox<'static>,
|
||||||
(mut mem, mut vat, prc, translator, tmod): (T, V, P, S, M),
|
|
||||||
) {
|
) {
|
||||||
if cache_size > 0 {
|
let (mut process, module) = crate::util::find_proc(os).unwrap();
|
||||||
let cache = CachedMemoryAccess::builder(&mut mem)
|
|
||||||
.arch(prc.sys_arch())
|
|
||||||
.cache_size(size::mb(cache_size as usize))
|
|
||||||
.page_type_mask(PageType::PAGE_TABLE | PageType::READ_ONLY | PageType::WRITEABLE);
|
|
||||||
|
|
||||||
if use_tlb {
|
|
||||||
let mut mem = cache.build().unwrap();
|
|
||||||
let mut vat = CachedVirtualTranslate::builder(vat)
|
|
||||||
.arch(prc.sys_arch())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
vat_test_with_mem(
|
|
||||||
bench,
|
|
||||||
&mut mem,
|
|
||||||
&mut vat,
|
|
||||||
chunks,
|
|
||||||
translations,
|
|
||||||
translator,
|
|
||||||
tmod,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let mut mem = cache.build().unwrap();
|
|
||||||
vat_test_with_mem(
|
|
||||||
bench,
|
|
||||||
&mut mem,
|
|
||||||
&mut vat,
|
|
||||||
chunks,
|
|
||||||
translations,
|
|
||||||
translator,
|
|
||||||
tmod,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if use_tlb {
|
|
||||||
let mut vat = CachedVirtualTranslate::builder(vat)
|
|
||||||
.arch(prc.sys_arch())
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
vat_test_with_mem(
|
|
||||||
bench,
|
|
||||||
&mut mem,
|
|
||||||
&mut vat,
|
|
||||||
chunks,
|
|
||||||
translations,
|
|
||||||
translator,
|
|
||||||
tmod,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
vat_test_with_mem(
|
vat_test_with_mem(
|
||||||
bench,
|
bench,
|
||||||
&mut mem,
|
as_mut!(process impl VirtualTranslate).unwrap(),
|
||||||
&mut vat,
|
|
||||||
chunks,
|
chunks,
|
||||||
translations,
|
translations,
|
||||||
translator,
|
module,
|
||||||
tmod,
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn chunk_vat_params<
|
fn chunk_vat_params(
|
||||||
T: PhysicalMemory,
|
|
||||||
V: VirtualTranslate,
|
|
||||||
P: OsProcessInfo,
|
|
||||||
S: ScopedVirtualTranslate,
|
|
||||||
M: OsProcessModuleInfo,
|
|
||||||
>(
|
|
||||||
group: &mut BenchmarkGroup<'_, measurement::WallTime>,
|
group: &mut BenchmarkGroup<'_, measurement::WallTime>,
|
||||||
func_name: String,
|
func_name: String,
|
||||||
cache_size: u64,
|
cache_size: usize,
|
||||||
use_tlb: bool,
|
use_tlb: bool,
|
||||||
initialize_ctx: &dyn Fn() -> Result<(T, V, P, S, M)>,
|
initialize_ctx: &dyn Fn(usize, bool) -> Result<OsInstanceArcBox<'static>>,
|
||||||
) {
|
) {
|
||||||
let size = 0x10;
|
let size = 0x10;
|
||||||
|
|
||||||
|
let mut os = initialize_ctx(cache_size, use_tlb).unwrap();
|
||||||
|
|
||||||
for &chunk_size in [1, 4, 16, 64].iter() {
|
for &chunk_size in [1, 4, 16, 64].iter() {
|
||||||
group.throughput(Throughput::Elements(chunk_size * size));
|
group.throughput(Throughput::Elements(chunk_size * size));
|
||||||
group.bench_with_input(
|
group.bench_with_input(
|
||||||
BenchmarkId::new(func_name.clone(), chunk_size),
|
BenchmarkId::new(func_name.clone(), chunk_size),
|
||||||
&size,
|
&size,
|
||||||
|b, &size| {
|
|b, &size| {
|
||||||
vat_test_with_ctx(
|
vat_test_with_os(
|
||||||
b,
|
b,
|
||||||
black_box(cache_size),
|
black_box(chunk_size.try_into().unwrap()),
|
||||||
black_box(chunk_size as usize),
|
black_box((size * chunk_size).try_into().unwrap()),
|
||||||
black_box((size * chunk_size) as usize),
|
&mut os,
|
||||||
black_box(use_tlb),
|
|
||||||
initialize_ctx().unwrap(),
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn chunk_vat<
|
pub fn chunk_vat(
|
||||||
T: PhysicalMemory,
|
|
||||||
V: VirtualTranslate,
|
|
||||||
P: OsProcessInfo,
|
|
||||||
S: ScopedVirtualTranslate,
|
|
||||||
M: OsProcessModuleInfo,
|
|
||||||
>(
|
|
||||||
c: &mut Criterion,
|
c: &mut Criterion,
|
||||||
backend_name: &str,
|
backend_name: &str,
|
||||||
initialize_ctx: &dyn Fn() -> Result<(T, V, P, S, M)>,
|
initialize_ctx: &dyn Fn(usize, bool) -> Result<OsInstanceArcBox<'static>>,
|
||||||
|
use_caches: bool,
|
||||||
) {
|
) {
|
||||||
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
|
let plot_config = PlotConfiguration::default().summary_scale(AxisScale::Logarithmic);
|
||||||
|
|
||||||
let group_name = format!("{}_chunk_vat", backend_name);
|
let group_name = format!("{backend_name}_chunk_vat");
|
||||||
|
|
||||||
let mut group = c.benchmark_group(group_name.clone());
|
let mut group = c.benchmark_group(group_name.clone());
|
||||||
group.plot_config(plot_config);
|
group.plot_config(plot_config);
|
||||||
|
|
||||||
chunk_vat_params(
|
chunk_vat_params(
|
||||||
&mut group,
|
&mut group,
|
||||||
format!("{}_nocache", group_name),
|
format!("{group_name}_nocache"),
|
||||||
0,
|
0,
|
||||||
false,
|
false,
|
||||||
initialize_ctx,
|
initialize_ctx,
|
||||||
);
|
);
|
||||||
|
if use_caches {
|
||||||
chunk_vat_params(
|
chunk_vat_params(
|
||||||
&mut group,
|
&mut group,
|
||||||
format!("{}_tlb_nocache", group_name),
|
format!("{group_name}_tlb_nocache"),
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
initialize_ctx,
|
initialize_ctx,
|
||||||
);
|
);
|
||||||
chunk_vat_params(
|
chunk_vat_params(
|
||||||
&mut group,
|
&mut group,
|
||||||
format!("{}_cache", group_name),
|
format!("{group_name}_cache"),
|
||||||
2,
|
2,
|
||||||
false,
|
false,
|
||||||
initialize_ctx,
|
initialize_ctx,
|
||||||
);
|
);
|
||||||
chunk_vat_params(
|
chunk_vat_params(
|
||||||
&mut group,
|
&mut group,
|
||||||
format!("{}_tlb_cache", group_name),
|
format!("{group_name}_tlb_cache"),
|
||||||
2,
|
2,
|
||||||
true,
|
true,
|
||||||
initialize_ctx,
|
initialize_ctx,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
# -*- mode: ruby -*-
|
||||||
|
# vi: set ft=ruby :
|
||||||
|
|
||||||
|
Vagrant.configure("2") do |config|
|
||||||
|
config.vm.provider :virtualbox do |v|
|
||||||
|
v.memory = 2048
|
||||||
|
v.cpus = 4
|
||||||
|
config.vm.synced_folder "../..", "/memflow", create: true, disabled: false
|
||||||
|
end
|
||||||
|
config.vm.provider :libvirt do |v|
|
||||||
|
v.memory = 2048
|
||||||
|
v.cpus = 4
|
||||||
|
v.qemu_use_session = false
|
||||||
|
config.vm.synced_folder "../..", "/memflow", type: 'nfs', nfs_udp: false, create: true, disabled: false
|
||||||
|
end
|
||||||
|
|
||||||
|
config.vm.box = "generic/ubuntu2004"
|
||||||
|
config.vm.provision :shell, privileged: true, inline: $install_updates
|
||||||
|
config.vm.provision :shell, privileged: false, inline: $install_memflow
|
||||||
|
end
|
||||||
|
|
||||||
|
$install_updates = <<-SCRIPT
|
||||||
|
echo "installing updates"
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get -qq update
|
||||||
|
apt-get -qq install git build-essential
|
||||||
|
SCRIPT
|
||||||
|
|
||||||
|
$install_memflow = <<-SCRIPT
|
||||||
|
echo "installing rust"
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||||
|
source $HOME/.cargo/env
|
||||||
|
|
||||||
|
echo "building memflow"
|
||||||
|
cd /memflow
|
||||||
|
cargo build --release --workspace --all-features
|
||||||
|
|
||||||
|
# TODO: install memflow-win32, memflow-coredump, etc
|
||||||
|
SCRIPT
|
@ -1,38 +0,0 @@
|
|||||||
use memflow::types::byte_swap::ByteSwap;
|
|
||||||
use memflow_derive::*;
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDerive {
|
|
||||||
pub val: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDeriveGeneric<T: ByteSwap> {
|
|
||||||
pub val: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDeriveWhere<T>
|
|
||||||
where
|
|
||||||
T: ByteSwap,
|
|
||||||
{
|
|
||||||
pub val: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDeriveSlice {
|
|
||||||
pub slice: [u8; 32],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDeriveStructSlice {
|
|
||||||
pub slice: [ByteSwapDeriveSlice; 128],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ByteSwap)]
|
|
||||||
struct ByteSwapDeriveStructGenericSlice<T: ByteSwap> {
|
|
||||||
pub slice: [ByteSwapDeriveGeneric<T>; 128],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
pub fn compiles() {}
|
|
@ -1,3 +1,23 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
cargo build --release --workspace
|
|
||||||
cbindgen --config cbindgen.toml --crate memflow-ffi --output memflow.h
|
run_twice() {
|
||||||
|
echo $@
|
||||||
|
|
||||||
|
v=$(exec $@)
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Error occured! Maybe a fluke. Retrying..."
|
||||||
|
v=$(exec $@)
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove any RUSTC_WRAPPER like sccache which might cause issues with cglue-bindgen
|
||||||
|
export RUSTC_WRAPPER=""
|
||||||
|
|
||||||
|
# update cglue-bindgen
|
||||||
|
cargo +nightly install cbindgen
|
||||||
|
cargo +nightly install cglue-bindgen
|
||||||
|
|
||||||
|
# generate c and cpp bindings
|
||||||
|
run_twice rustup run nightly cglue-bindgen +nightly -c cglue.toml -- --config cbindgen.toml --crate memflow-ffi --output memflow.h -l C
|
||||||
|
run_twice rustup run nightly cglue-bindgen +nightly -c cglue.toml -- --config cbindgen.toml --crate memflow-ffi --output memflow.hpp -l C++
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
default_container = "Box"
|
||||||
|
default_context = "Arc"
|
||||||
|
function_prefix = "mf"
|
@ -1,19 +0,0 @@
|
|||||||
CC =g++
|
|
||||||
CFLAGS =-I../ -I../../memflow-ffi/ -L../../target/release
|
|
||||||
LIBS=-lm -Wl,--no-as-needed -ldl -lpthread -l:libmemflow_win32_ffi.a
|
|
||||||
|
|
||||||
ODIR=./
|
|
||||||
|
|
||||||
%.o: %.c $(DEPS)
|
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
|
||||||
|
|
||||||
phys_mem.out: phys_mem.o
|
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: phys_mem.out
|
|
||||||
|
|
||||||
.DEFAULT_GOAL := all
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(ODIR)/*.o
|
|
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: LLVM
|
@ -0,0 +1,31 @@
|
|||||||
|
CC = clang
|
||||||
|
CFLAGS = -g -O0 -I../../../memflow-ffi/ -L../../../target/release
|
||||||
|
LIBS=-lm -ldl -lpthread -l:libmemflow_ffi.a
|
||||||
|
|
||||||
|
ODIR=./
|
||||||
|
|
||||||
|
%.o: %.c $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
phys_mem.out: phys_mem.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
process_list.out: process_list.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
module_list.out: module_list.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
module_dump.out: module_dump.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
find_process.out: find_process.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: phys_mem.out process_list.out module_list.out module_dump.out find_process.out
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(ODIR)/*.o
|
@ -0,0 +1,210 @@
|
|||||||
|
#include "memflow.h"
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
ProcessInstance target_process;
|
||||||
|
ProcessInstance target_process2;
|
||||||
|
|
||||||
|
struct FindProcessContext {
|
||||||
|
OsInstance *os;
|
||||||
|
const char *name;
|
||||||
|
ProcessInstance *target_process;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool find_process(struct FindProcessContext *find_context, Address addr) {
|
||||||
|
|
||||||
|
if (find_context->found) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mf_osinstance_process_by_address(find_context->os, addr,
|
||||||
|
find_context->target_process)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct ProcessInfo *info =
|
||||||
|
mf_processinstance_info(find_context->target_process);
|
||||||
|
|
||||||
|
if (!strcmp(info->name, find_context->name)) {
|
||||||
|
// abort iteration
|
||||||
|
find_context->found = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mf_processinstance_drop(*find_context->target_process);
|
||||||
|
|
||||||
|
// continue iteration
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
|
||||||
|
void *readmem(void *vargp) {
|
||||||
|
// Store the value argument passed to this thread
|
||||||
|
int *myid = (int *)vargp;
|
||||||
|
|
||||||
|
uint8_t buffer[0x8];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
pthread_mutex_lock(&lock);
|
||||||
|
mf_processinstance_read_raw_into(&target_process, 0x7FF72AF30000,
|
||||||
|
MUT_SLICE(u8, buffer, sizeof(buffer)));
|
||||||
|
|
||||||
|
printf("TID: %d, Read: %lx\n", *myid, *(uint64_t *)buffer);
|
||||||
|
pthread_mutex_unlock(&lock);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// enable info level logging
|
||||||
|
log_init(3);
|
||||||
|
|
||||||
|
// load all available plugins
|
||||||
|
Inventory *inventory = inventory_scan();
|
||||||
|
printf("inventory initialized: %p\n", inventory);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "kvm";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
const char *os_name = argc > 3 ? argv[3] : "win32";
|
||||||
|
const char *os_arg = argc > 4 ? argv[4] : "";
|
||||||
|
const char *target_proc = argc > 5 ? argv[5] : "notepad.exe";
|
||||||
|
|
||||||
|
ConnectorInstance connector, *conn = conn_name[0] ? &connector : NULL;
|
||||||
|
|
||||||
|
// initialize the connector plugin
|
||||||
|
if (conn) {
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, conn)) {
|
||||||
|
log_error("unable to initialize connector");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connector initialized: %p\n",
|
||||||
|
connector.container.instance.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the OS plugin
|
||||||
|
OsInstance os;
|
||||||
|
if (inventory_create_os(inventory, os_name, os_arg, conn, &os)) {
|
||||||
|
log_error("unable to initialize os plugin");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("os plugin initialized: %p\n", os.container.instance.instance);
|
||||||
|
|
||||||
|
// find a specific process based on it's name.
|
||||||
|
// this can easily be replaced by process_by_name but
|
||||||
|
// is being used here as a demonstration.
|
||||||
|
|
||||||
|
/* struct FindProcessContext find_context = {
|
||||||
|
&os,
|
||||||
|
target_proc,
|
||||||
|
&target_process,
|
||||||
|
false,
|
||||||
|
};
|
||||||
|
|
||||||
|
mf_osinstance_process_address_list_callback(
|
||||||
|
&os, CALLBACK(Address, &find_context, find_process));
|
||||||
|
|
||||||
|
if (find_context.found) {
|
||||||
|
const struct ProcessInfo *info = mf_processinstance_info(&target_process);
|
||||||
|
|
||||||
|
printf("%s process found: 0x%lx] %d %s %s\n", target_proc, info->address,
|
||||||
|
info->pid, info->name, info->path);
|
||||||
|
|
||||||
|
mf_processinstance_drop(target_process);
|
||||||
|
} else {
|
||||||
|
printf("Unable to find %s\n", target_proc);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// find a specific process based on its name
|
||||||
|
// via process_by_name
|
||||||
|
if (!(ret = mf_osinstance_process_by_name(&os, STR(target_proc),
|
||||||
|
&target_process))) {
|
||||||
|
const struct ProcessInfo *info = mf_processinstance_info(&target_process);
|
||||||
|
|
||||||
|
printf("%s process found: 0x%lx] %d %s %s\n", target_proc, info->address,
|
||||||
|
info->pid, info->name, info->path);
|
||||||
|
|
||||||
|
ModuleInfo module_info;
|
||||||
|
mf_processinstance_module_by_name(&target_process, STR(target_proc),
|
||||||
|
&module_info);
|
||||||
|
printf("0x%lx\n", module_info.base);
|
||||||
|
|
||||||
|
/*ConnectorInstance conn2;
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, &conn2)) {
|
||||||
|
log_error("unable to initialize connector");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectorInstance cloned = mf_connectorinstance_clone(&conn2);
|
||||||
|
printf("ok1\n");
|
||||||
|
|
||||||
|
mf_connectorinstance_drop(cloned);
|
||||||
|
printf("ok2\n");*/
|
||||||
|
|
||||||
|
// MemoryView phys_view = mf_connectorinstance_phys_view(&conn2);
|
||||||
|
|
||||||
|
/*if (pthread_mutex_init(&lock, NULL) != 0) {
|
||||||
|
printf("\n mutex init has failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_t tid2;
|
||||||
|
pthread_create(&tid, NULL, readmem, (void *)&tid);
|
||||||
|
pthread_create(&tid2, NULL, readmem, (void *)&tid2);
|
||||||
|
|
||||||
|
pthread_join(tid, NULL);
|
||||||
|
pthread_join(tid2, NULL);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&lock);*/
|
||||||
|
|
||||||
|
if (!(ret = mf_osinstance_process_by_name(&os, STR("notepad++.exe"),
|
||||||
|
&target_process2))) {
|
||||||
|
const struct ProcessInfo *info2 =
|
||||||
|
mf_processinstance_info(&target_process2);
|
||||||
|
|
||||||
|
printf("%s process found: 0x%lx] %d %s %s\n", "notepad++.exe",
|
||||||
|
info2->address, info2->pid, info2->name, info2->path);
|
||||||
|
|
||||||
|
uint8_t buffer[0x8];
|
||||||
|
|
||||||
|
mf_processinstance_read_raw_into(&target_process, info->address + 0x520,
|
||||||
|
MUT_SLICE(u8, buffer, sizeof(buffer)));
|
||||||
|
|
||||||
|
printf("Read: %lx\n", *(uint64_t *)buffer);
|
||||||
|
|
||||||
|
mf_processinstance_read_raw_into(&target_process2, info2->address + 0x520,
|
||||||
|
MUT_SLICE(u8, buffer, sizeof(buffer)));
|
||||||
|
|
||||||
|
printf("Read2: %lx\n", *(uint64_t *)buffer);
|
||||||
|
} else {
|
||||||
|
printf("error notepad++");
|
||||||
|
}
|
||||||
|
|
||||||
|
mf_processinstance_drop(target_process);
|
||||||
|
} else {
|
||||||
|
printf("Unable to find %s\n", target_proc);
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will also free the connector here
|
||||||
|
// as it was _moved_ into the os by `inventory_create_os`
|
||||||
|
mf_osinstance_drop(os);
|
||||||
|
log_info("os plugin/connector freed");
|
||||||
|
|
||||||
|
inventory_free(inventory);
|
||||||
|
log_info("inventory freed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
This example demonstrates how to read the contents of a module from a process.
|
||||||
|
|
||||||
|
To read from a specific module the following steps have to be done:
|
||||||
|
|
||||||
|
- Create an inventory and let it search for plugins in the system
|
||||||
|
- Load the plugins to access physical memory and the operating system
|
||||||
|
(by default the `qemu` plugin and `win32` plugin are being used)
|
||||||
|
- Find the process by the specified name
|
||||||
|
- Find the module_info for the given module in the process
|
||||||
|
- Allocate a buffer which will fit the entire module
|
||||||
|
- Read the entire module into the buffer and ignore partial read errors
|
||||||
|
- Write the contents of the retrieved buffer to the specified output location
|
||||||
|
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
./module_dump.out kvm :: win32 :: notepad.exe notepad.exe notepad.exe.bin
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include "memflow.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// enable info level logging
|
||||||
|
log_init(4);
|
||||||
|
|
||||||
|
// load all available plugins
|
||||||
|
Inventory *inventory = inventory_scan();
|
||||||
|
printf("inventory initialized: %p\n", inventory);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "qemu";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
const char *os_name = argc > 3 ? argv[3]: "win32";
|
||||||
|
const char *os_arg = argc > 4? argv[4]: "";
|
||||||
|
const char *target_proc = argc > 5? argv[5]: "notepad.exe";
|
||||||
|
const char *target_module = argc > 6? argv[6]: "notepad.exe";
|
||||||
|
const char *output_file = argc > 7? argv[7]: "notepad.exe.bin";
|
||||||
|
|
||||||
|
ConnectorInstance connector, *conn = conn_name[0] ? &connector : NULL;
|
||||||
|
|
||||||
|
// initialize the connector plugin
|
||||||
|
if (conn) {
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, conn)) {
|
||||||
|
log_error("unable to initialize connector");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connector initialized: %p\n", connector.container.instance.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the OS plugin
|
||||||
|
OsInstance os;
|
||||||
|
if (inventory_create_os(inventory, os_name, os_arg, conn, &os)) {
|
||||||
|
log_error("unable to initialize os plugin");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("os plugin initialized: %p\n", os.container.instance.instance);
|
||||||
|
|
||||||
|
// find a specific process based on its name via process_by_name
|
||||||
|
ProcessInstance target_process;
|
||||||
|
if (!(ret = mf_osinstance_process_by_name(&os, STR(target_proc), &target_process))) {
|
||||||
|
const struct ProcessInfo *info = mf_processinstance_info(&target_process);
|
||||||
|
|
||||||
|
printf("%s process found: 0x%lx] %d %s %s\n", target_proc, info->address,
|
||||||
|
info->pid, info->name, info->path);
|
||||||
|
|
||||||
|
// find the module by its name
|
||||||
|
ModuleInfo module_info;
|
||||||
|
if (!(ret = mf_processinstance_module_by_name(&target_process, STR(target_module), &module_info))) {
|
||||||
|
printf("%s module found: 0x%lx] 0x%lx %s %s\n", target_proc, module_info.address,
|
||||||
|
module_info.base, module_info.name, module_info.path);
|
||||||
|
|
||||||
|
// read module into buffer, in this case -2 / -3 are partial read/write errors
|
||||||
|
void *module_buffer = malloc(module_info.size);
|
||||||
|
ret = mf_processinstance_read_raw_into(&target_process, module_info.base, MUT_SLICE(u8, module_buffer, module_info.size));
|
||||||
|
if (ret == -2) {
|
||||||
|
printf("%s warning: %s] module only read partially\n", target_proc, target_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
// module has been read
|
||||||
|
printf("%s read module: %s] read 0x%lx bytes\n", target_proc, target_module, module_info.size);
|
||||||
|
|
||||||
|
// write the buffer to the specified location
|
||||||
|
FILE *file = fopen(output_file, "wb");
|
||||||
|
if (file) {
|
||||||
|
fwrite(module_buffer, module_info.size, 1, file);
|
||||||
|
fclose(file);
|
||||||
|
printf("dumped 0x%lx bytes to %s\n", module_info.size, output_file);
|
||||||
|
} else {
|
||||||
|
printf("unable to open output file %s: %s\n", output_file, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(module_buffer);
|
||||||
|
} else {
|
||||||
|
printf("unable to find module: %s\n", target_module);
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cleanup the processinstance
|
||||||
|
mf_processinstance_drop(target_process);
|
||||||
|
} else {
|
||||||
|
printf("unable to find process: %s\n", target_proc);
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will also free the connector here
|
||||||
|
// as it was _moved_ into the os by `inventory_create_os`
|
||||||
|
mf_osinstance_drop(os);
|
||||||
|
log_info("os plugin/connector freed");
|
||||||
|
|
||||||
|
inventory_free(inventory);
|
||||||
|
log_info("inventory freed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
#include "memflow.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// enable info level logging
|
||||||
|
log_init(3);
|
||||||
|
|
||||||
|
// load all available plugins
|
||||||
|
Inventory *inventory = inventory_scan();
|
||||||
|
printf("inventory initialized: %p\n", inventory);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "kvm";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
const char *os_name = argc > 3 ? argv[3]: "win32";
|
||||||
|
const char *os_arg = argc > 4? argv[4]: "";
|
||||||
|
const char *target_proc = argc > 5? argv[5]: "notepad.exe";
|
||||||
|
|
||||||
|
ConnectorInstance connector, *conn = conn_name[0] ? &connector : NULL;
|
||||||
|
|
||||||
|
// initialize the connector plugin
|
||||||
|
if (conn) {
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, conn)) {
|
||||||
|
log_error("unable to initialize connector");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connector initialized: %p\n", connector.container.instance.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the OS plugin
|
||||||
|
OsInstance os;
|
||||||
|
if (inventory_create_os(inventory, os_name, os_arg, conn, &os)) {
|
||||||
|
log_error("unable to initialize os plugin");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("os plugin initialized: %p\n", os.container.instance.instance);
|
||||||
|
|
||||||
|
// find a specific process based on its name via process_by_name
|
||||||
|
ProcessInstance target_process;
|
||||||
|
if (!(ret = mf_osinstance_process_by_name(&os, STR(target_proc), &target_process))) {
|
||||||
|
const struct ProcessInfo *info = mf_processinstance_info(&target_process);
|
||||||
|
|
||||||
|
printf("%s process found: 0x%lx] %d %s %s\n", target_proc, info->address,
|
||||||
|
info->pid, info->name, info->path);
|
||||||
|
|
||||||
|
// iterate over all module info structs and collect them in a buffer
|
||||||
|
COLLECT_CB(ModuleInfo, module_info);
|
||||||
|
mf_processinstance_module_list_callback(&target_process, NULL, module_info);
|
||||||
|
for (size_t i = 0; i < module_info_base.size; i++) {
|
||||||
|
ModuleInfo *module_info = &((ModuleInfo *)module_info_base.buf)[i];
|
||||||
|
printf("%s module found: 0x%lx] 0x%lx %s %s\n", target_proc, module_info->address,
|
||||||
|
module_info->base, module_info->name, module_info->path);
|
||||||
|
}
|
||||||
|
free(module_info_base.buf);
|
||||||
|
|
||||||
|
// cleanup the processinstance
|
||||||
|
mf_processinstance_drop(target_process);
|
||||||
|
} else {
|
||||||
|
printf("Unable to find %s\n", target_proc);
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will also free the connector here
|
||||||
|
// as it was _moved_ into the os by `inventory_create_os`
|
||||||
|
mf_osinstance_drop(os);
|
||||||
|
log_info("os plugin/connector freed");
|
||||||
|
|
||||||
|
inventory_free(inventory);
|
||||||
|
log_info("inventory freed");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
#include "memflow.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// enable debug level logging
|
||||||
|
log_init(3);
|
||||||
|
|
||||||
|
Inventory *inv = inventory_scan();
|
||||||
|
printf("inv: %p\n", inv);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "kvm";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
|
||||||
|
ConnectorInstance conn;
|
||||||
|
if (!inventory_create_connector(inv, conn_name, conn_arg, &conn)) {
|
||||||
|
for (int i = 0; i < 1000 * 1000; i++) {
|
||||||
|
uint8_t buffer[0x1000];
|
||||||
|
|
||||||
|
ConnectorInstance cloned = mf_connectorinstance_clone(&conn);
|
||||||
|
|
||||||
|
mf_connectorinstance_drop(cloned);
|
||||||
|
|
||||||
|
MemoryView phys_view = mf_connectorinstance_phys_view(&conn);
|
||||||
|
|
||||||
|
// regular read_into
|
||||||
|
mf_read_raw_into(&phys_view, 0x1000 + i, MUT_SLICE(u8, buffer, sizeof(buffer)));
|
||||||
|
|
||||||
|
// read multiple
|
||||||
|
ReadData read_data = {0x1000 + i, {buffer, sizeof(buffer)}};
|
||||||
|
mf_read_raw_list(&phys_view, MUT_SLICE(ReadData, &read_data, 1));
|
||||||
|
|
||||||
|
printf("Read: %lx\n", *(uint64_t *)buffer);
|
||||||
|
|
||||||
|
mf_memoryview_drop(phys_view);
|
||||||
|
}
|
||||||
|
|
||||||
|
mf_connectorinstance_drop(conn);
|
||||||
|
printf("conn dropped!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory_free(inv);
|
||||||
|
printf("inv freed!\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,125 @@
|
|||||||
|
#include "memflow.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
bool list_processes(OsInstance *os, Address addr) {
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ProcessInstance process;
|
||||||
|
if ((ret = mf_osinstance_process_by_address(os, addr, &process))) {
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct ProcessInfo *info = mf_processinstance_info(&process);
|
||||||
|
|
||||||
|
ModuleInfo primary_module;
|
||||||
|
if ((ret = mf_processinstance_primary_module(&process, &primary_module))) {
|
||||||
|
// no primary module found, continue iteration - this should _never_ happen
|
||||||
|
printf("%d\t%s\t0x%lx\tN/A\n", info->pid, info->name, info->address);
|
||||||
|
log_debug_errorcode(ret);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d\t%s\t0x%lx\t0x%lx\n", info->pid, info->name, info->address,
|
||||||
|
primary_module.address);
|
||||||
|
|
||||||
|
// iterate over all module addresses and collect them in an array
|
||||||
|
struct ModuleAddressInfo module_addresses[256];
|
||||||
|
COLLECT_CB_INTO_ARR(ModuleAddressInfo, module_address, module_addresses);
|
||||||
|
mf_processinstance_module_address_list_callback(&process, NULL, module_address);
|
||||||
|
|
||||||
|
printf("Read %zu modules\n", module_address_base.size);
|
||||||
|
|
||||||
|
// iterate over all module info structs and collect them in a buffer
|
||||||
|
COLLECT_CB(ModuleInfo, module_info);
|
||||||
|
mf_processinstance_module_list_callback(&process, NULL, module_info);
|
||||||
|
printf("Read %zu modules\n", module_info_base.size);
|
||||||
|
free(module_info_base.buf);
|
||||||
|
|
||||||
|
// iterate over all imports and collect them in a buffer
|
||||||
|
COLLECT_CB(ImportInfo, import_info);
|
||||||
|
mf_processinstance_module_import_list_callback(&process, &primary_module, import_info);
|
||||||
|
printf("Read %zu imports\n", import_info_base.size);
|
||||||
|
free(import_info_base.buf);
|
||||||
|
|
||||||
|
// iterate over all exports and collect them in a buffer
|
||||||
|
COLLECT_CB(ExportInfo, exports);
|
||||||
|
mf_processinstance_module_export_list_callback(&process, &primary_module, exports);
|
||||||
|
printf("Read %zu exports\n", exports_base.size);
|
||||||
|
free(exports_base.buf);
|
||||||
|
|
||||||
|
// iterate over all sections and collect them in a buffer
|
||||||
|
COLLECT_CB(SectionInfo, sections);
|
||||||
|
mf_processinstance_module_section_list_callback(&process, &primary_module, sections);
|
||||||
|
printf("Read %zu sections\n", sections_base.size);
|
||||||
|
free(sections_base.buf);
|
||||||
|
|
||||||
|
mf_processinstance_drop(process);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
// enable debug level logging
|
||||||
|
log_init(2);
|
||||||
|
|
||||||
|
// load all available plugins
|
||||||
|
Inventory *inventory = inventory_scan();
|
||||||
|
|
||||||
|
printf("inventory initialized: %p\n", inventory);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "kvm";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
const char *os_name = argc > 3 ? argv[3]: "win32";
|
||||||
|
const char *os_arg = argc > 4? argv[4]: "";
|
||||||
|
|
||||||
|
ConnectorInstance connector, *conn = conn_name[0] ? &connector : NULL;
|
||||||
|
|
||||||
|
// initialize the connector plugin
|
||||||
|
if (conn) {
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, conn)) {
|
||||||
|
printf("unable to initialize connector\n");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connector initialized: %p\n", connector.container.instance.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
// initialize the OS plugin
|
||||||
|
OsInstance os;
|
||||||
|
if (inventory_create_os(inventory, os_name, os_arg, conn, &os)) {
|
||||||
|
printf("unable to initialize os plugin\n");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("os plugin initialized: %p\n", os.container.instance.instance);
|
||||||
|
|
||||||
|
// iterate over all processes and print them manually
|
||||||
|
printf("Pid\tNAME\tADDRESS\tMAIN_MODULE\n");
|
||||||
|
mf_osinstance_process_address_list_callback(&os, CALLBACK(Address, &os, list_processes));
|
||||||
|
|
||||||
|
// count all processes
|
||||||
|
COUNT_CB(Address, process_address);
|
||||||
|
mf_osinstance_process_address_list_callback(&os, process_address);
|
||||||
|
printf("Counted %zu processes\n", process_address_count);
|
||||||
|
|
||||||
|
// iterate over all process info structs and collect them in an array
|
||||||
|
struct ProcessInfo process_info[256];
|
||||||
|
COLLECT_CB_INTO_ARR(ProcessInfo, process_info_cb, process_info);
|
||||||
|
mf_osinstance_process_info_list_callback(&os, process_info_cb);
|
||||||
|
printf("Read %zu process infos\n", process_info_cb_base.size);
|
||||||
|
|
||||||
|
// This will also free the connector here
|
||||||
|
// as it was _moved_ into the os by `inventory_create_os`
|
||||||
|
mf_osinstance_drop(os);
|
||||||
|
printf("os plugin/connector freed\n");
|
||||||
|
|
||||||
|
inventory_free(inventory);
|
||||||
|
printf("inventory freed\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
CC = g++
|
||||||
|
CFLAGS = -g -O0 -pedantic -std=c++14 -I../../../memflow-ffi/ -L../../../target/release
|
||||||
|
LIBS=-lm -ldl -lpthread -l:libmemflow_ffi.a
|
||||||
|
|
||||||
|
ODIR=./
|
||||||
|
|
||||||
|
%.o: %.cpp $(DEPS)
|
||||||
|
$(CC) -c -o $@ $< $(CFLAGS)
|
||||||
|
|
||||||
|
plist.out: plist.o
|
||||||
|
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: plist.out
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f $(ODIR)/*.o
|
@ -0,0 +1,104 @@
|
|||||||
|
#include "memflow.hpp"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
void fmt_arch(char *arch, int n, ArchitectureIdent ident);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[], const int t[])
|
||||||
|
{
|
||||||
|
log_init(LevelFilter::LevelFilter_Info);
|
||||||
|
int arr[] = {1, 2, 3, 4, 5};
|
||||||
|
int b=3;
|
||||||
|
//CSliceMut<unsigned char>((char*)arr);
|
||||||
|
CSliceRef<uint8_t>((char*)t, sizeof(int));
|
||||||
|
|
||||||
|
/*Inventory *inventory = inventory_scan();
|
||||||
|
|
||||||
|
if (!inventory)
|
||||||
|
{
|
||||||
|
log_error("unable to create inventory");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("inventory initialized: %p\n", inventory);
|
||||||
|
|
||||||
|
const char *conn_name = argc > 1 ? argv[1] : "kvm";
|
||||||
|
const char *conn_arg = argc > 2 ? argv[2] : "";
|
||||||
|
const char *os_name = argc > 3 ? argv[3] : "win32";
|
||||||
|
const char *os_arg = argc > 4 ? argv[4] : "";
|
||||||
|
|
||||||
|
// ConnectorInstance<> connector, *conn = conn_name[0] ? &connector : nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<ConnectorInstance<>> connector = nullptr;
|
||||||
|
|
||||||
|
if (!connector)
|
||||||
|
{
|
||||||
|
connector = std::make_unique<ConnectorInstance<>>();
|
||||||
|
if (inventory_create_connector(inventory, conn_name, conn_arg, connector.get()))
|
||||||
|
{
|
||||||
|
printf("unable to initialize connector\n");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("connector initialized: %p\n", connector->container.instance.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
OsInstance<> os;
|
||||||
|
|
||||||
|
if (inventory_create_os(inventory, os_name, os_arg, connector.get(), &os))
|
||||||
|
{
|
||||||
|
printf("unable to initialize OS\n");
|
||||||
|
inventory_free(inventory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inventory_free(inventory);
|
||||||
|
|
||||||
|
printf("os initialized: %p\n", os.container.instance.instance);
|
||||||
|
|
||||||
|
auto info = os.info();
|
||||||
|
char arch[11];
|
||||||
|
fmt_arch(arch, sizeof(arch), info->arch);
|
||||||
|
|
||||||
|
printf("Kernel base: %llx\nKernel size: %llx\nArchitecture: %s\n", info->base, info->size, arch);
|
||||||
|
|
||||||
|
printf("Process List:\n");
|
||||||
|
|
||||||
|
printf("%-4s | %-8s | %-10s | %-10s | %s\n", "Seq", "Pid", "Sys Arch", "Proc Arch", "Name");
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
os.process_info_list_callback([&i](ProcessInfo info)
|
||||||
|
{
|
||||||
|
char sys_arch[11];
|
||||||
|
char proc_arch[11];
|
||||||
|
|
||||||
|
fmt_arch(sys_arch, sizeof(sys_arch), info.sys_arch);
|
||||||
|
fmt_arch(proc_arch, sizeof(proc_arch), info.proc_arch);
|
||||||
|
|
||||||
|
printf("%-4d | %-8d | %-10s | %-10s | %s\n", i++, info.pid, sys_arch, proc_arch, info.name);
|
||||||
|
|
||||||
|
return true; });
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fmt_arch(char *arch, int n, ArchitectureIdent ident)
|
||||||
|
{
|
||||||
|
switch (ident.tag)
|
||||||
|
{
|
||||||
|
case ArchitectureIdent::Tag::ArchitectureIdent_X86:
|
||||||
|
snprintf(arch, n, "X86_%d", ident.x86._0);
|
||||||
|
break;
|
||||||
|
case ArchitectureIdent::Tag::ArchitectureIdent_AArch64:
|
||||||
|
snprintf(arch, n, "AArch64");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
snprintf(arch, n, "Unknown");
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
#include "memflow.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
log_init(4);
|
|
||||||
|
|
||||||
ConnectorInventory *inv = inventory_scan();
|
|
||||||
printf("inv: %p\n", inv);
|
|
||||||
|
|
||||||
const char *conn_name = argc > 1? argv[1]: "qemu_procfs";
|
|
||||||
const char *conn_arg = argc > 2? argv[2]: "";
|
|
||||||
|
|
||||||
CloneablePhysicalMemoryObj *conn = inventory_create_connector(inv, conn_name, conn_arg);
|
|
||||||
printf("conn: %p\n", conn);
|
|
||||||
|
|
||||||
if (conn) {
|
|
||||||
PhysicalMemoryObj *phys_mem = downcast_cloneable(conn);
|
|
||||||
printf("phys_mem: %p\n", phys_mem);
|
|
||||||
|
|
||||||
uint64_t read = phys_read_u64(phys_mem, addr_to_paddr(0x30000));
|
|
||||||
|
|
||||||
printf("Read: %lx\n", read);
|
|
||||||
|
|
||||||
phys_free(phys_mem);
|
|
||||||
|
|
||||||
connector_free(conn);
|
|
||||||
printf("conn freed!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory_free(inv);
|
|
||||||
printf("inv freed!\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,177 +0,0 @@
|
|||||||
#ifndef MEMFLOW_HLAPI_H
|
|
||||||
#define MEMFLOW_HLAPI_H
|
|
||||||
|
|
||||||
#include "memflow.h"
|
|
||||||
#include "binddestr.h"
|
|
||||||
|
|
||||||
#ifndef NO_STL_CONTAINERS
|
|
||||||
#include <string>
|
|
||||||
#ifndef AUTO_STRING_SIZE
|
|
||||||
#define AUTO_STRING_SIZE 128
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct CConnectorInventory
|
|
||||||
: BindDestr<ConnectorInventory, inventory_free>
|
|
||||||
{
|
|
||||||
CConnectorInventory(ConnectorInventory *inv)
|
|
||||||
: BindDestr(inv) {}
|
|
||||||
|
|
||||||
CConnectorInventory()
|
|
||||||
: CConnectorInventory(::inventory_scan()) {}
|
|
||||||
|
|
||||||
CConnectorInventory(const char *path)
|
|
||||||
: CConnectorInventory(::inventory_scan_path(path)) {}
|
|
||||||
|
|
||||||
WRAP_FN(inventory, add_dir);
|
|
||||||
WRAP_FN(inventory, create_connector);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPhysicalMemory
|
|
||||||
: BindDestr<PhysicalMemoryObj, phys_free>
|
|
||||||
{
|
|
||||||
CPhysicalMemory(PhysicalMemoryObj *mem)
|
|
||||||
: BindDestr(mem) {}
|
|
||||||
|
|
||||||
WRAP_FN_RAW(phys_read_raw_list);
|
|
||||||
WRAP_FN_RAW(phys_write_raw_list);
|
|
||||||
WRAP_FN_RAW(phys_metadata);
|
|
||||||
WRAP_FN_RAW(phys_read_raw_into);
|
|
||||||
WRAP_FN_RAW(phys_read_u32);
|
|
||||||
WRAP_FN_RAW(phys_read_u64);
|
|
||||||
WRAP_FN_RAW(phys_write_raw);
|
|
||||||
WRAP_FN_RAW(phys_write_u32);
|
|
||||||
WRAP_FN_RAW(phys_write_u64);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T phys_read(PhysicalAddress address) {
|
|
||||||
T data;
|
|
||||||
this->phys_read_raw_into(address, (uint8_t *)&data, sizeof(T));
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
int32_t phys_write(PhysicalAddress address, const T &data) {
|
|
||||||
return this->phys_write_raw(address, (const uint8_t *)&data, sizeof(T));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CCloneablePhysicalMemory
|
|
||||||
: BindDestr<CloneablePhysicalMemoryObj, connector_free>
|
|
||||||
{
|
|
||||||
CCloneablePhysicalMemory(CloneablePhysicalMemoryObj *mem)
|
|
||||||
: BindDestr(mem) {}
|
|
||||||
|
|
||||||
WRAP_FN(connector, clone);
|
|
||||||
WRAP_FN_RAW_TYPE(CPhysicalMemory, downcast_cloneable);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CVirtualMemory
|
|
||||||
: BindDestr<VirtualMemoryObj, virt_free>
|
|
||||||
{
|
|
||||||
CVirtualMemory(VirtualMemoryObj *virt_mem)
|
|
||||||
: BindDestr(virt_mem) {}
|
|
||||||
|
|
||||||
WRAP_FN_RAW(virt_read_raw_list);
|
|
||||||
WRAP_FN_RAW(virt_write_raw_list);
|
|
||||||
WRAP_FN_RAW(virt_read_raw_into);
|
|
||||||
WRAP_FN_RAW(virt_read_u32);
|
|
||||||
WRAP_FN_RAW(virt_read_u64);
|
|
||||||
WRAP_FN_RAW(virt_write_raw);
|
|
||||||
WRAP_FN_RAW(virt_write_u32);
|
|
||||||
WRAP_FN_RAW(virt_write_u64);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
T virt_read(Address address) {
|
|
||||||
T data;
|
|
||||||
this->virt_read_raw_into(address, (uint8_t *)&data, sizeof(T));
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
int32_t virt_write(Address address, const T &data) {
|
|
||||||
return this->virt_write_raw(address, (const uint8_t *)&data, sizeof(T));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CArchitecture
|
|
||||||
: BindDestr<ArchitectureObj, arch_free>
|
|
||||||
{
|
|
||||||
CArchitecture(ArchitectureObj *arch)
|
|
||||||
: BindDestr(arch) {}
|
|
||||||
|
|
||||||
WRAP_FN(arch, bits);
|
|
||||||
WRAP_FN(arch, endianess);
|
|
||||||
WRAP_FN(arch, page_size);
|
|
||||||
WRAP_FN(arch, size_addr);
|
|
||||||
WRAP_FN(arch, address_space_bits);
|
|
||||||
WRAP_FN_RAW(is_x86_arch);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct COsProcessInfo
|
|
||||||
: BindDestr<OsProcessInfoObj, os_process_info_free>
|
|
||||||
{
|
|
||||||
COsProcessInfo(OsProcessInfoObj *info)
|
|
||||||
: BindDestr(info) {}
|
|
||||||
|
|
||||||
WRAP_FN(os_process_info, address);
|
|
||||||
WRAP_FN(os_process_info, pid);
|
|
||||||
WRAP_FN(os_process_info, name);
|
|
||||||
WRAP_FN_TYPE(CArchitecture, os_process_info, sys_arch);
|
|
||||||
WRAP_FN_TYPE(CArchitecture, os_process_info, proc_arch);
|
|
||||||
|
|
||||||
#ifndef NO_STL_CONTAINERS
|
|
||||||
std::string name_string(size_t max_size) {
|
|
||||||
char *buf = (char *)malloc(max_size);
|
|
||||||
if (buf) {
|
|
||||||
this->name(buf, max_size);
|
|
||||||
std::string ret = std::string(buf);
|
|
||||||
free(buf);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_string() {
|
|
||||||
char buf[AUTO_STRING_SIZE];
|
|
||||||
size_t ret = this->name(buf, AUTO_STRING_SIZE);
|
|
||||||
return std::string(buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct COsProcessModuleInfo
|
|
||||||
: BindDestr<OsProcessModuleInfoObj, os_process_module_free>
|
|
||||||
{
|
|
||||||
COsProcessModuleInfo(OsProcessModuleInfoObj *modinfo)
|
|
||||||
: BindDestr(modinfo) {}
|
|
||||||
|
|
||||||
WRAP_FN(os_process_module, address);
|
|
||||||
WRAP_FN(os_process_module, parent_process);
|
|
||||||
WRAP_FN(os_process_module, base);
|
|
||||||
WRAP_FN(os_process_module, size);
|
|
||||||
WRAP_FN(os_process_module, name);
|
|
||||||
|
|
||||||
#ifndef NO_STL_CONTAINERS
|
|
||||||
std::string name_string(size_t max_size) {
|
|
||||||
char *buf = (char *)malloc(max_size);
|
|
||||||
if (buf) {
|
|
||||||
this->name(buf, max_size);
|
|
||||||
std::string ret = std::string(buf);
|
|
||||||
free(buf);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
return std::string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_string() {
|
|
||||||
char buf[AUTO_STRING_SIZE];
|
|
||||||
this->name(buf, AUTO_STRING_SIZE);
|
|
||||||
return std::string(buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||||||
|
---
|
||||||
|
GENERATOR:
|
||||||
|
PackageName: memflow_go
|
||||||
|
PackageDescription: "core components of the memflow physical memory introspection framework"
|
||||||
|
PackageLicense: "MIT"
|
||||||
|
Includes: ["memflow.h"]
|
||||||
|
FlagGroups:
|
||||||
|
- { name: "CFLAGS", traits: ["linux"], flags: [ -I../../memflow-ffi/ ] }
|
||||||
|
- { name: "LDFLAGS", traits: ["linux"], flags: [ -L../../target/release -lm -ldl -lpthread -l:libmemflow_ffi.a ] }
|
||||||
|
|
||||||
|
PARSER:
|
||||||
|
IncludePaths: ["./"]
|
||||||
|
SourcesPaths: ["memflow.h"]
|
||||||
|
|
||||||
|
TRANSLATOR:
|
||||||
|
ConstRules:
|
||||||
|
defines: expand
|
||||||
|
Rules:
|
||||||
|
global:
|
||||||
|
- { transform: lower }
|
||||||
|
- { action: accept, from: "^log_" }
|
||||||
|
- { action: accept, from: "^inventory_" }
|
||||||
|
- { action: accept, from: "^connector_" }
|
||||||
|
- { action: accept, from: "^phys_" }
|
||||||
|
- { action: accept, from: "^virt_" }
|
||||||
|
- { action: replace, from: "_", to: _ }
|
||||||
|
- { transform: export }
|
||||||
|
const:
|
||||||
|
- { action: replace, from: "_", to: _ }
|
||||||
|
type:
|
||||||
|
- { action: accept, from: "^MU" }
|
||||||
|
- { action: accept, from: "Inventory" }
|
||||||
|
- { action: accept, from: "^Connector" }
|
||||||
|
- { action: accept, from: "^Physical" }
|
||||||
|
- { action: accept, from: "^Virtual" }
|
||||||
|
- { action: accept, from: "^Translation" }
|
||||||
|
- { action: accept, from: "^Page" }
|
||||||
|
- { action: replace, from: "_t$" }
|
||||||
|
private:
|
||||||
|
- { transform: unexport }
|
||||||
|
post-global:
|
||||||
|
- { action: replace, from: _$ }
|
||||||
|
- { load: snakecase }
|
@ -1,150 +0,0 @@
|
|||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use memflow::connector::{ConnectorArgs, ConnectorInventory};
|
|
||||||
|
|
||||||
use crate::util::*;
|
|
||||||
|
|
||||||
use crate::mem::phys_mem::CloneablePhysicalMemoryObj;
|
|
||||||
|
|
||||||
use log::trace;
|
|
||||||
|
|
||||||
/// Create a new connector inventory
|
|
||||||
///
|
|
||||||
/// This function will try to find connectors using PATH environment variable
|
|
||||||
///
|
|
||||||
/// Note that all functions go through each directories, and look for a `memflow` directory,
|
|
||||||
/// and search for libraries in those.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// ConnectorInventory is inherently unsafe, because it loads shared libraries which can not be
|
|
||||||
/// guaranteed to be safe.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn inventory_scan() -> &'static mut ConnectorInventory {
|
|
||||||
to_heap(ConnectorInventory::scan())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new inventory with custom path string
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `path` must be a valid null terminated string
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn inventory_scan_path(
|
|
||||||
path: *const c_char,
|
|
||||||
) -> Option<&'static mut ConnectorInventory> {
|
|
||||||
let rpath = CStr::from_ptr(path).to_string_lossy();
|
|
||||||
ConnectorInventory::scan_path(rpath.to_string())
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a directory to an existing inventory
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `dir` must be a valid null terminated string
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn inventory_add_dir(
|
|
||||||
inv: &mut ConnectorInventory,
|
|
||||||
dir: *const c_char,
|
|
||||||
) -> i32 {
|
|
||||||
let rdir = CStr::from_ptr(dir).to_string_lossy();
|
|
||||||
|
|
||||||
inv.add_dir(PathBuf::from(rdir.to_string()))
|
|
||||||
.int_result_logged()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a connector with given arguments
|
|
||||||
///
|
|
||||||
/// This creates an instance of a `CloneablePhysicalMemory`. To use it for physical memory
|
|
||||||
/// operations, please call `downcast_cloneable` to create a instance of `PhysicalMemory`.
|
|
||||||
///
|
|
||||||
/// Regardless, this instance needs to be freed using `connector_free`.
|
|
||||||
///
|
|
||||||
/// # Arguments
|
|
||||||
///
|
|
||||||
/// * `name` - name of the connector to use
|
|
||||||
/// * `args` - arguments to be passed to the connector upon its creation
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// Both `name`, and `args` must be valid null terminated strings.
|
|
||||||
///
|
|
||||||
/// Any error strings returned by the connector must not be outputed after the connector gets
|
|
||||||
/// freed, because that operation could cause the underlying shared library to get unloaded.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn inventory_create_connector(
|
|
||||||
inv: &mut ConnectorInventory,
|
|
||||||
name: *const c_char,
|
|
||||||
args: *const c_char,
|
|
||||||
) -> Option<&'static mut CloneablePhysicalMemoryObj> {
|
|
||||||
let rname = CStr::from_ptr(name).to_string_lossy();
|
|
||||||
|
|
||||||
if args.is_null() {
|
|
||||||
inv.create_connector_default(&rname)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
.map(|c| c as CloneablePhysicalMemoryObj)
|
|
||||||
.map(to_heap)
|
|
||||||
} else {
|
|
||||||
let rargs = CStr::from_ptr(args).to_string_lossy();
|
|
||||||
let conn_args = ConnectorArgs::parse(&rargs).map_err(inspect_err).ok()?;
|
|
||||||
|
|
||||||
inv.create_connector(&rname, &conn_args)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
.map(|c| c as CloneablePhysicalMemoryObj)
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clone a connector
|
|
||||||
///
|
|
||||||
/// This method is useful when needing to perform multithreaded operations, as a connector is not
|
|
||||||
/// guaranteed to be thread safe. Every single cloned instance also needs to be freed using
|
|
||||||
/// `connector_free`.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `conn` has to point to a a valid `CloneablePhysicalMemory` created by one of the provided
|
|
||||||
/// functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn connector_clone(
|
|
||||||
conn: &CloneablePhysicalMemoryObj,
|
|
||||||
) -> &'static mut CloneablePhysicalMemoryObj {
|
|
||||||
trace!("connector_clone: {:?}", conn as *const _);
|
|
||||||
Box::leak(Box::new(Box::leak(conn.clone_box())))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a connector instance
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `conn` has to point to a valid `CloneablePhysicalMemoryObj` created by one of the provided
|
|
||||||
/// functions.
|
|
||||||
///
|
|
||||||
/// There has to be no instance of `PhysicalMemory` created from the input `conn`, because they
|
|
||||||
/// will become invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn connector_free(conn: &'static mut CloneablePhysicalMemoryObj) {
|
|
||||||
trace!("connector_free: {:?}", conn as *mut _);
|
|
||||||
let _ = Box::from_raw(*Box::from_raw(conn));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a connector inventory
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `inv` must point to a valid `ConnectorInventory` that was created using one of the provided
|
|
||||||
/// functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn inventory_free(inv: &'static mut ConnectorInventory) {
|
|
||||||
trace!("inventory_free: {:?}", inv as *mut _);
|
|
||||||
let _ = Box::from_raw(inv);
|
|
||||||
}
|
|
@ -1,17 +1,125 @@
|
|||||||
use log::Level;
|
use log::{Level, LevelFilter};
|
||||||
|
use memflow::cglue::IntError;
|
||||||
#[no_mangle]
|
use memflow::error::Error;
|
||||||
pub extern "C" fn log_init(level_num: i32) {
|
use memflow::plugins::Inventory;
|
||||||
let level = match level_num {
|
use std::num::NonZeroI32;
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
use std::ffi::CStr;
|
||||||
2 => Level::Info,
|
use std::os::raw::c_char;
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
/// Initialize logging with selected logging level.
|
||||||
_ => Level::Trace,
|
#[no_mangle]
|
||||||
};
|
pub extern "C" fn log_init(level_filter: LevelFilter) {
|
||||||
simple_logger::SimpleLogger::new()
|
simplelog::TermLogger::init(
|
||||||
.with_level(level.to_level_filter())
|
level_filter,
|
||||||
.init()
|
simplelog::Config::default(),
|
||||||
|
simplelog::TerminalMode::Stdout,
|
||||||
|
simplelog::ColorChoice::Auto,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: add variadic functions when this is being stabilized, see https://github.com/rust-lang/rust/issues/44930
|
||||||
|
|
||||||
|
/// Logs a error message via log::error!
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided string must be a valid null-terminated char array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn log_error(s: *const c_char) {
|
||||||
|
if !s.is_null() {
|
||||||
|
let c_str = CStr::from_ptr(s);
|
||||||
|
if let Ok(r_str) = c_str.to_str() {
|
||||||
|
log::error!("{}", r_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs a warning message via log::warn!
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided string must be a valid null-terminated char array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn log_warn(s: *const c_char) {
|
||||||
|
if !s.is_null() {
|
||||||
|
let c_str = CStr::from_ptr(s);
|
||||||
|
if let Ok(r_str) = c_str.to_str() {
|
||||||
|
log::warn!("{}", r_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs a info message via log::info!
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided string must be a valid null-terminated char array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn log_info(s: *const c_char) {
|
||||||
|
if !s.is_null() {
|
||||||
|
let c_str = CStr::from_ptr(s);
|
||||||
|
if let Ok(r_str) = c_str.to_str() {
|
||||||
|
log::info!("{}", r_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs a debug message via log::debug!
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided string must be a valid null-terminated char array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn log_debug(s: *const c_char) {
|
||||||
|
if !s.is_null() {
|
||||||
|
let c_str = CStr::from_ptr(s);
|
||||||
|
if let Ok(r_str) = c_str.to_str() {
|
||||||
|
log::debug!("{}", r_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs a trace message via log::trace!
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The provided string must be a valid null-terminated char array.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn log_trace(s: *const c_char) {
|
||||||
|
if !s.is_null() {
|
||||||
|
let c_str = CStr::from_ptr(s);
|
||||||
|
if let Ok(r_str) = c_str.to_str() {
|
||||||
|
log::trace!("{}", r_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs an error code with custom log level.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn log_errorcode(level: Level, error: i32) {
|
||||||
|
if let Some(error) = NonZeroI32::new(error) {
|
||||||
|
log::log!(level, "{}", <Error as IntError>::from_int_err(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs an error with debug log level.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn log_debug_errorcode(error: i32) {
|
||||||
|
log_errorcode(Level::Debug, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets new maximum log level.
|
||||||
|
///
|
||||||
|
/// If `inventory` is supplied, the log level is also updated within all plugin instances. However,
|
||||||
|
/// if it is not supplied, plugins will not have their log levels updated, potentially leading to
|
||||||
|
/// lower performance, or less logging than expected.
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn log_set_max_level(level_filter: LevelFilter, inventory: Option<&Inventory>) {
|
||||||
|
if let Some(inventory) = inventory {
|
||||||
|
inventory.set_max_log_level(level_filter);
|
||||||
|
} else {
|
||||||
|
log::set_max_level(level_filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
pub mod phys_mem;
|
#[allow(unused)]
|
||||||
pub mod virt_mem;
|
pub use memflow::mem::phys_mem::*;
|
||||||
|
#[allow(unused)]
|
||||||
|
pub use memflow::mem::virt_mem::*;
|
||||||
|
@ -1,146 +0,0 @@
|
|||||||
use memflow::mem::phys_mem::*;
|
|
||||||
use memflow::types::PhysicalAddress;
|
|
||||||
|
|
||||||
use crate::util::*;
|
|
||||||
|
|
||||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
|
||||||
|
|
||||||
use log::trace;
|
|
||||||
|
|
||||||
pub type CloneablePhysicalMemoryObj = &'static mut dyn CloneablePhysicalMemory;
|
|
||||||
pub type PhysicalMemoryObj = &'static mut dyn PhysicalMemory;
|
|
||||||
|
|
||||||
/// Downcast a cloneable physical memory into a physical memory object.
|
|
||||||
///
|
|
||||||
/// This function will take a `cloneable` and turn it into a `PhysicalMemoryObj`, which then can be
|
|
||||||
/// used by physical memory functions.
|
|
||||||
///
|
|
||||||
/// Please note that this does not free `cloneable`, and the reference is still valid for further
|
|
||||||
/// operations.
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn downcast_cloneable(
|
|
||||||
cloneable: &'static mut CloneablePhysicalMemoryObj,
|
|
||||||
) -> &'static mut PhysicalMemoryObj {
|
|
||||||
Box::leak(Box::new((*cloneable).downcast()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a `PhysicalMemoryObj`
|
|
||||||
///
|
|
||||||
/// This will free a reference to a `PhysicalMemoryObj`. If the physical memory object was created
|
|
||||||
/// using `downcast_cloneable`, this will NOT free the cloneable reference.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `mem` must point to a valid `PhysicalMemoryObj` that was created using one of the provided
|
|
||||||
/// functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn phys_free(mem: &'static mut PhysicalMemoryObj) {
|
|
||||||
trace!("phys_free: {:?}", mem as *mut _);
|
|
||||||
let _ = Box::from_raw(mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a list of values
|
|
||||||
///
|
|
||||||
/// This will perform `len` physical memory reads on the provided `data`. Using lists is preferable
|
|
||||||
/// for performance, because then the underlying connectors can batch those operations.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `data` must be a valid array of `PhysicalReadData` with the length of at least `len`
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn phys_read_raw_list(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
data: *mut PhysicalReadData,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
let data = from_raw_parts_mut(data, len);
|
|
||||||
mem.phys_read_raw_list(data).int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a list of values
|
|
||||||
///
|
|
||||||
/// This will perform `len` physical memory writes on the provided `data`. Using lists is preferable
|
|
||||||
/// for performance, because then the underlying connectors can batch those operations.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `data` must be a valid array of `PhysicalWriteData` with the length of at least `len`
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn phys_write_raw_list(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
data: *const PhysicalWriteData,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
let data = from_raw_parts(data, len);
|
|
||||||
mem.phys_write_raw_list(data).int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve metadata about the physical memory object
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn phys_metadata(mem: &PhysicalMemoryObj) -> PhysicalMemoryMetadata {
|
|
||||||
mem.metadata()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single value into `out` from a provided `PhysicalAddress`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `out` must be a valid pointer to a data buffer of at least `len` size.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn phys_read_raw_into(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
addr: PhysicalAddress,
|
|
||||||
out: *mut u8,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
mem.phys_read_raw_into(addr, from_raw_parts_mut(out, len))
|
|
||||||
.int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single 32-bit value from a provided `PhysicalAddress`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn phys_read_u32(mem: &mut PhysicalMemoryObj, addr: PhysicalAddress) -> u32 {
|
|
||||||
mem.phys_read::<u32>(addr).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single 64-bit value from a provided `PhysicalAddress`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn phys_read_u64(mem: &mut PhysicalMemoryObj, addr: PhysicalAddress) -> u64 {
|
|
||||||
mem.phys_read::<u64>(addr).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single value from `input` into a provided `PhysicalAddress`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `input` must be a valid pointer to a data buffer of at least `len` size.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn phys_write_raw(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
addr: PhysicalAddress,
|
|
||||||
input: *const u8,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
mem.phys_write_raw(addr, from_raw_parts(input, len))
|
|
||||||
.int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single 32-bit value into a provided `PhysicalAddress`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn phys_write_u32(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
addr: PhysicalAddress,
|
|
||||||
val: u32,
|
|
||||||
) -> i32 {
|
|
||||||
mem.phys_write(addr, &val).int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single 64-bit value into a provided `PhysicalAddress`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn phys_write_u64(
|
|
||||||
mem: &mut PhysicalMemoryObj,
|
|
||||||
addr: PhysicalAddress,
|
|
||||||
val: u64,
|
|
||||||
) -> i32 {
|
|
||||||
mem.phys_write(addr, &val).int_result()
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
use memflow::error::PartialResultExt;
|
|
||||||
use memflow::mem::virt_mem::*;
|
|
||||||
use memflow::types::Address;
|
|
||||||
|
|
||||||
use crate::util::*;
|
|
||||||
|
|
||||||
use std::slice::{from_raw_parts, from_raw_parts_mut};
|
|
||||||
|
|
||||||
pub type VirtualMemoryObj = &'static mut dyn VirtualMemory;
|
|
||||||
|
|
||||||
/// Free a virtual memory object reference
|
|
||||||
///
|
|
||||||
/// This function frees the reference to a virtual memory object.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `mem` must be a valid reference to a virtual memory object.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn virt_free(mem: &'static mut VirtualMemoryObj) {
|
|
||||||
let _ = Box::from_raw(mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a list of values
|
|
||||||
///
|
|
||||||
/// This will perform `len` virtual memory reads on the provided `data`. Using lists is preferable
|
|
||||||
/// for performance, because then the underlying connectors can batch those operations, and virtual
|
|
||||||
/// translation function can cut down on read operations.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `data` must be a valid array of `VirtualReadData` with the length of at least `len`
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn virt_read_raw_list(
|
|
||||||
mem: &mut VirtualMemoryObj,
|
|
||||||
data: *mut VirtualReadData,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
let data = from_raw_parts_mut(data, len);
|
|
||||||
mem.virt_read_raw_list(data).data_part().int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a list of values
|
|
||||||
///
|
|
||||||
/// This will perform `len` virtual memory writes on the provided `data`. Using lists is preferable
|
|
||||||
/// for performance, because then the underlying connectors can batch those operations, and virtual
|
|
||||||
/// translation function can cut down on read operations.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `data` must be a valid array of `VirtualWriteData` with the length of at least `len`
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn virt_write_raw_list(
|
|
||||||
mem: &mut VirtualMemoryObj,
|
|
||||||
data: *const VirtualWriteData,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
let data = from_raw_parts(data, len);
|
|
||||||
mem.virt_write_raw_list(data).data_part().int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single value into `out` from a provided `Address`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `out` must be a valid pointer to a data buffer of at least `len` size.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn virt_read_raw_into(
|
|
||||||
mem: &mut VirtualMemoryObj,
|
|
||||||
addr: Address,
|
|
||||||
out: *mut u8,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
mem.virt_read_raw_into(addr, from_raw_parts_mut(out, len))
|
|
||||||
.data_part()
|
|
||||||
.int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single 32-bit value from a provided `Address`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn virt_read_u32(mem: &mut VirtualMemoryObj, addr: Address) -> u32 {
|
|
||||||
mem.virt_read::<u32>(addr).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a single 64-bit value from a provided `Address`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn virt_read_u64(mem: &mut VirtualMemoryObj, addr: Address) -> u64 {
|
|
||||||
mem.virt_read::<u64>(addr).unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single value from `input` into a provided `Address`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `input` must be a valid pointer to a data buffer of at least `len` size.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn virt_write_raw(
|
|
||||||
mem: &mut VirtualMemoryObj,
|
|
||||||
addr: Address,
|
|
||||||
input: *const u8,
|
|
||||||
len: usize,
|
|
||||||
) -> i32 {
|
|
||||||
mem.virt_write_raw(addr, from_raw_parts(input, len))
|
|
||||||
.data_part()
|
|
||||||
.int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single 32-bit value into a provided `Address`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn virt_write_u32(mem: &mut VirtualMemoryObj, addr: Address, val: u32) -> i32 {
|
|
||||||
mem.virt_write(addr, &val).data_part().int_result()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a single 64-bit value into a provided `Address`
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn virt_write_u64(mem: &mut VirtualMemoryObj, addr: Address, val: u64) -> i32 {
|
|
||||||
mem.virt_write(addr, &val).data_part().int_result()
|
|
||||||
}
|
|
@ -0,0 +1,4 @@
|
|||||||
|
#[allow(unused)]
|
||||||
|
pub use memflow::os::*;
|
||||||
|
#[allow(unused)]
|
||||||
|
pub use memflow::plugins::*;
|
@ -0,0 +1,215 @@
|
|||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::c_char;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use memflow::plugins::Inventory;
|
||||||
|
use memflow::plugins::{
|
||||||
|
connector::{ConnectorInstanceArcBox, MuConnectorInstanceArcBox},
|
||||||
|
os::{MuOsInstanceArcBox, OsInstanceArcBox},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::util::*;
|
||||||
|
use memflow::cglue::result::IntResult;
|
||||||
|
|
||||||
|
use log::trace;
|
||||||
|
|
||||||
|
/// Create a new connector inventory
|
||||||
|
///
|
||||||
|
/// This function will try to find connectors using PATH environment variable
|
||||||
|
///
|
||||||
|
/// Note that all functions go through each directories, and look for a `memflow` directory,
|
||||||
|
/// and search for libraries in those.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Inventory is inherently unsafe, because it loads shared libraries which can not be
|
||||||
|
/// guaranteed to be safe.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_scan() -> &'static mut Inventory {
|
||||||
|
to_heap(Inventory::scan())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new inventory with custom path string
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `path` must be a valid null terminated string
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_scan_path(
|
||||||
|
path: *const c_char,
|
||||||
|
) -> Option<&'static mut Inventory> {
|
||||||
|
let rpath = CStr::from_ptr(path).to_string_lossy();
|
||||||
|
Inventory::scan_path(rpath.to_string())
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.ok()
|
||||||
|
.map(to_heap)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a directory to an existing inventory
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `dir` must be a valid null terminated string
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_add_dir(inv: &mut Inventory, dir: *const c_char) -> i32 {
|
||||||
|
let rdir = CStr::from_ptr(dir).to_string_lossy();
|
||||||
|
|
||||||
|
inv.add_dir(PathBuf::from(rdir.to_string()))
|
||||||
|
.into_int_result()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a connector with given arguments
|
||||||
|
///
|
||||||
|
/// This creates an instance of `ConnectorInstance`.
|
||||||
|
///
|
||||||
|
/// This instance needs to be dropped using `connector_drop`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `name` - name of the connector to use
|
||||||
|
/// * `args` - arguments to be passed to the connector upon its creation
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Both `name`, and `args` must be valid null terminated strings.
|
||||||
|
///
|
||||||
|
/// Any error strings returned by the connector must not be outputed after the connector gets
|
||||||
|
/// freed, because that operation could cause the underlying shared library to get unloaded.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_create_connector(
|
||||||
|
inv: &mut Inventory,
|
||||||
|
name: *const c_char,
|
||||||
|
args: *const c_char,
|
||||||
|
out: &mut MuConnectorInstanceArcBox<'static>,
|
||||||
|
) -> i32 {
|
||||||
|
let rname = CStr::from_ptr(name).to_string_lossy();
|
||||||
|
|
||||||
|
if args.is_null() {
|
||||||
|
inv.create_connector(&rname, None, None)
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.into_int_out_result(out)
|
||||||
|
} else {
|
||||||
|
let rargs = CStr::from_ptr(args).to_string_lossy();
|
||||||
|
str::parse(&rargs)
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.and_then(|args| inv.create_connector(&rname, None, Some(&args)))
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.into_int_out_result(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a OS instance with given arguments
|
||||||
|
///
|
||||||
|
/// This creates an instance of `KernelInstance`.
|
||||||
|
///
|
||||||
|
/// This instance needs to be freed using `os_drop`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `name` - name of the OS to use
|
||||||
|
/// * `args` - arguments to be passed to the connector upon its creation
|
||||||
|
/// * `mem` - a previously initialized connector instance
|
||||||
|
/// * `out` - a valid memory location that will contain the resulting os-instance
|
||||||
|
///
|
||||||
|
/// # Remarks
|
||||||
|
///
|
||||||
|
/// The `mem` connector instance is being _moved_ into the os layer.
|
||||||
|
/// This means upon calling `os_drop` it is not unnecessary to call `connector_drop` anymore.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Both `name`, and `args` must be valid null terminated strings.
|
||||||
|
///
|
||||||
|
/// Any error strings returned by the connector must not be outputed after the connector gets
|
||||||
|
/// freed, because that operation could cause the underlying shared library to get unloaded.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_create_os(
|
||||||
|
inv: &mut Inventory,
|
||||||
|
name: *const c_char,
|
||||||
|
args: *const c_char,
|
||||||
|
mem: *mut ConnectorInstanceArcBox<'static>,
|
||||||
|
out: &mut MuOsInstanceArcBox<'static>,
|
||||||
|
) -> i32 {
|
||||||
|
let rname = CStr::from_ptr(name).to_string_lossy();
|
||||||
|
let _args = CStr::from_ptr(args).to_string_lossy();
|
||||||
|
|
||||||
|
let mem_obj = if mem.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mem_obj = mem.read();
|
||||||
|
// Zero out the data so that any automatic destructors on the other side do nothing.
|
||||||
|
std::ptr::write_bytes(mem, 0, 1);
|
||||||
|
Some(mem_obj)
|
||||||
|
};
|
||||||
|
|
||||||
|
if args.is_null() {
|
||||||
|
inv.create_os(&rname, mem_obj, None)
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.into_int_out_result(out)
|
||||||
|
} else {
|
||||||
|
let rargs = CStr::from_ptr(args).to_string_lossy();
|
||||||
|
str::parse(&rargs)
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.and_then(|args| inv.create_os(&rname, mem_obj, Some(&args)))
|
||||||
|
.map_err(inspect_err)
|
||||||
|
.into_int_out_result(out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free a os plugin
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `os` must point to a valid `OsInstance` that was created using one of the provided
|
||||||
|
/// functions.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn os_drop(os: &mut OsInstanceArcBox<'static>) {
|
||||||
|
trace!("connector_drop: {:?}", os as *mut _);
|
||||||
|
std::ptr::drop_in_place(os);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone a connector
|
||||||
|
///
|
||||||
|
/// This method is useful when needing to perform multithreaded operations, as a connector is not
|
||||||
|
/// guaranteed to be thread safe. Every single cloned instance also needs to be dropped using
|
||||||
|
/// `connector_drop`.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `conn` has to point to a a valid `CloneablePhysicalMemory` created by one of the provided
|
||||||
|
/// functions.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn connector_clone(
|
||||||
|
conn: &ConnectorInstanceArcBox<'static>,
|
||||||
|
out: &mut MuConnectorInstanceArcBox<'static>,
|
||||||
|
) {
|
||||||
|
trace!("connector_clone: {:?}", conn as *const _);
|
||||||
|
*out.as_mut_ptr() = conn.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free a connector instance
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `conn` has to point to a valid [`ConnectorInstance`](ConnectorInstanceArcBox) created by one of the provided
|
||||||
|
/// functions.
|
||||||
|
///
|
||||||
|
/// There has to be no instance of `PhysicalMemory` created from the input `conn`, because they
|
||||||
|
/// will become invalid.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn connector_drop(conn: &mut ConnectorInstanceArcBox<'static>) {
|
||||||
|
trace!("connector_drop: {:?}", conn as *mut _);
|
||||||
|
std::ptr::drop_in_place(conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Free a connector inventory
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// `inv` must point to a valid `Inventory` that was created using one of the provided
|
||||||
|
/// functions.
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn inventory_free(inv: &'static mut Inventory) {
|
||||||
|
trace!("inventory_free: {:?}", inv as *mut _);
|
||||||
|
let _ = Box::from_raw(inv);
|
||||||
|
}
|
@ -1,119 +0,0 @@
|
|||||||
use crate::util::*;
|
|
||||||
use memflow::process::*;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::slice::from_raw_parts_mut;
|
|
||||||
|
|
||||||
use memflow::architecture::ArchitectureObj;
|
|
||||||
use memflow::types::Address;
|
|
||||||
|
|
||||||
pub type OsProcessInfoObj = &'static dyn OsProcessInfo;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_info_address(obj: &OsProcessInfoObj) -> Address {
|
|
||||||
obj.address()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_info_pid(obj: &OsProcessInfoObj) -> PID {
|
|
||||||
obj.pid()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retreive name of the process
|
|
||||||
///
|
|
||||||
/// This will copy at most `max_len` characters (including the null terminator) into `out` of the
|
|
||||||
/// name.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `out` must be a buffer with at least `max_len` size
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn os_process_info_name(
|
|
||||||
obj: &OsProcessInfoObj,
|
|
||||||
out: *mut c_char,
|
|
||||||
max_len: usize,
|
|
||||||
) -> usize {
|
|
||||||
let name = obj.name();
|
|
||||||
let name_bytes = name.as_bytes();
|
|
||||||
let out_bytes = from_raw_parts_mut(out as *mut u8, std::cmp::min(max_len, name.len() + 1));
|
|
||||||
let len = out_bytes.len();
|
|
||||||
out_bytes[..(len - 1)].copy_from_slice(&name_bytes[..(len - 1)]);
|
|
||||||
*out_bytes.iter_mut().last().unwrap() = 0;
|
|
||||||
len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_info_sys_arch(obj: &OsProcessInfoObj) -> &ArchitectureObj {
|
|
||||||
to_heap(obj.sys_arch())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_info_proc_arch(obj: &OsProcessInfoObj) -> &ArchitectureObj {
|
|
||||||
to_heap(obj.proc_arch())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a OsProcessInfoObj reference
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `obj` must point to a valid `OsProcessInfoObj`, and was created using one of the API's
|
|
||||||
/// functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn os_process_info_free(obj: &'static mut OsProcessInfoObj) {
|
|
||||||
let _ = Box::from_raw(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type OsProcessModuleInfoObj = &'static dyn OsProcessModuleInfo;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_module_address(obj: &OsProcessModuleInfoObj) -> Address {
|
|
||||||
obj.address()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_module_parent_process(obj: &OsProcessModuleInfoObj) -> Address {
|
|
||||||
obj.parent_process()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_module_base(obj: &OsProcessModuleInfoObj) -> Address {
|
|
||||||
obj.base()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn os_process_module_size(obj: &OsProcessModuleInfoObj) -> usize {
|
|
||||||
obj.size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retreive name of the module
|
|
||||||
///
|
|
||||||
/// This will copy at most `max_len` characters (including the null terminator) into `out` of the
|
|
||||||
/// name.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `out` must be a buffer with at least `max_len` size
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn os_process_module_name(
|
|
||||||
obj: &OsProcessModuleInfoObj,
|
|
||||||
out: *mut c_char,
|
|
||||||
max_len: usize,
|
|
||||||
) -> usize {
|
|
||||||
let name = obj.name();
|
|
||||||
let name_bytes = name.as_bytes();
|
|
||||||
let out_bytes = from_raw_parts_mut(out as *mut u8, std::cmp::min(max_len, name.len() + 1));
|
|
||||||
let len = out_bytes.len();
|
|
||||||
out_bytes[..(len - 1)].copy_from_slice(&name_bytes[..(len - 1)]);
|
|
||||||
*out_bytes.iter_mut().last().unwrap() = 0;
|
|
||||||
len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a OsProcessModuleInfoObj reference
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `obj` must point to a valid `OsProcessModuleInfoObj`, and was created using one of the API's
|
|
||||||
/// functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn os_process_module_free(obj: &'static mut OsProcessModuleInfoObj) {
|
|
||||||
let _ = Box::from_raw(obj);
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
use std::ffi::c_void;
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use memflow_win32::*;
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// this function has to be called with an initialized memory backend
|
|
||||||
/// this function will return a pointer to a win32 object that has to be freed via win32_free()
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn win32_init(mem: *mut c_void) -> *mut Win32 {
|
|
||||||
if !mem.is_null() {
|
|
||||||
let mut _mem: Box<Box<dyn MemoryBackend>> = std::mem::transmute(mem as *mut _);
|
|
||||||
|
|
||||||
let _os = Win32::try_with(&mut **_mem).unwrap();
|
|
||||||
|
|
||||||
Box::leak(_mem);
|
|
||||||
return std::mem::transmute(Box::new(_os));
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// this function has to be called with a pointer that has been initialized from win32_init()
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn win32_free(win32: *mut Win32) {
|
|
||||||
if !win32.is_null() {
|
|
||||||
let _win32: Box<Win32> = std::mem::transmute(win32);
|
|
||||||
// drop _win32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// this function will return a pointer to a win32_offsets object that has to be freed via win32_offsets_free()
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn win32_offsets_init(win32: *mut Win32) -> *mut Win32Offsets {
|
|
||||||
if !win32.is_null() {
|
|
||||||
let _win32: Box<Win32> = std::mem::transmute(win32);
|
|
||||||
|
|
||||||
let _offsets = Win32Offsets::try_with_guid(&_win32.kernel_guid()).unwrap();
|
|
||||||
|
|
||||||
Box::leak(_win32);
|
|
||||||
return std::mem::transmute(Box::new(_offsets));
|
|
||||||
}
|
|
||||||
|
|
||||||
ptr::null_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// this function has to be called with a pointer that has been initialized from win32_offsets_init()
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn win32_offsets_free(offsets: *mut Win32Offsets) {
|
|
||||||
if !offsets.is_null() {
|
|
||||||
let _offsets: Box<Win32Offsets> = std::mem::transmute(offsets);
|
|
||||||
// drop _offsets
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# update cglue-bindgen
|
||||||
|
cargo +nightly install cbindgen
|
||||||
|
cargo +nightly install cglue-bindgen
|
||||||
|
|
||||||
|
DIFFC=$(diff memflow.h <(rustup run nightly cglue-bindgen +nightly -c cglue.toml -- --config cbindgen.toml --crate memflow-ffi -l C))
|
||||||
|
DIFFCPP=$(diff memflow.hpp <(rustup run nightly cglue-bindgen +nightly -c cglue.toml -- --config cbindgen.toml --crate memflow-ffi -l C++))
|
||||||
|
if [ "$DIFFC" != "" ] || [ "$DIFFCPP" != "" ]
|
||||||
|
then
|
||||||
|
exit 1
|
||||||
|
fi
|
@ -1,5 +0,0 @@
|
|||||||
/target
|
|
||||||
**/*.rs.bk
|
|
||||||
*.swp
|
|
||||||
.vscode
|
|
||||||
Cargo.lock
|
|
@ -1,37 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "memflow-qemu-procfs"
|
|
||||||
version = "0.1.5"
|
|
||||||
authors = ["ko1N <ko1N1337@gmail.com>", "Aurimas Blažulionis <0x60@pm.me>"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "qemu procfs connector for the memflow physical memory introspection framework"
|
|
||||||
documentation = "https://docs.rs/memflow-qemu-procfs"
|
|
||||||
readme = "README.md"
|
|
||||||
homepage = "https://memflow.github.io"
|
|
||||||
repository = "https://github.com/memflow/memflow-qemu-procfs"
|
|
||||||
license-file = "LICENSE"
|
|
||||||
keywords = [ "memflow", "introspection", "memory" ]
|
|
||||||
categories = [ "api-bindings", "memory-management", "os" ]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
crate-type = ["lib", "cdylib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
memflow = { version = "0.1", features = ["inventory"] }
|
|
||||||
log = { version = "0.4", default-features = false }
|
|
||||||
procfs = "0.7"
|
|
||||||
libc = "0.2"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
clap = "2.33"
|
|
||||||
simple_logger = "1.0"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
inventory = []
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "read_phys"
|
|
||||||
path = "examples/read_phys.rs"
|
|
@ -1,24 +0,0 @@
|
|||||||
.PHONY: all release debug test install
|
|
||||||
|
|
||||||
all:
|
|
||||||
make test
|
|
||||||
make release
|
|
||||||
|
|
||||||
release:
|
|
||||||
cargo build --release --all-features
|
|
||||||
|
|
||||||
debug:
|
|
||||||
cargo build --all-features
|
|
||||||
|
|
||||||
clean:
|
|
||||||
cargo clean
|
|
||||||
|
|
||||||
test:
|
|
||||||
cargo test --all-features
|
|
||||||
|
|
||||||
install_user:
|
|
||||||
./install.sh
|
|
||||||
|
|
||||||
install:
|
|
||||||
./install.sh --system
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
|||||||
# memflow-qemu-procfs
|
|
||||||
|
|
||||||
This connector implements an interface for Qemu via the Process Filesystem on Linux.
|
|
||||||
|
|
||||||
## Compilation
|
|
||||||
|
|
||||||
### Installing the library
|
|
||||||
|
|
||||||
The `./install.sh` script will just compile and install the plugin.
|
|
||||||
The connector will be installed to `~/.local/lib/memflow` by default.
|
|
||||||
Additionally the `--system` flag can be specified which will install the connector in `/usr/lib/memflow` as well.
|
|
||||||
|
|
||||||
### Building the stand-alone connector for dynamic loading
|
|
||||||
|
|
||||||
The stand-alone connector of this library is feature-gated behind the `inventory` feature.
|
|
||||||
To compile a dynamic library for use with the connector inventory use the following command:
|
|
||||||
|
|
||||||
```
|
|
||||||
cargo build --release --all-features
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using the crate in a rust project
|
|
||||||
|
|
||||||
To use the connector in a rust project just include it in your Cargo.toml
|
|
||||||
|
|
||||||
```
|
|
||||||
memflow-qemu-procfs = "0.1"
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure to not enable the `inventory` feature when importing multiple
|
|
||||||
connectors in a rust project without using the memflow connector inventory.
|
|
||||||
This might cause duplicated exports being generated in your project.
|
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
- `name` - the name of the virtual machine (default argument, optional)
|
|
||||||
|
|
||||||
## Permissions
|
|
||||||
|
|
||||||
The `qemu_procfs` connector requires access to the qemu process via the linux procfs. This means any process which loads this connector requires to have at least ptrace permissions set.
|
|
||||||
|
|
||||||
To set ptrace permissions on a binary simply use:
|
|
||||||
```bash
|
|
||||||
sudo setcap 'CAP_SYS_PTRACE=ep' [filename]
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively you can just run the binary via `sudo`.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Licensed under MIT License, see [LICENSE](LICENSE).
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, shall be licensed as above, without any additional terms or conditions.
|
|
@ -1,45 +0,0 @@
|
|||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
use log::{info, Level};
|
|
||||||
|
|
||||||
use memflow::prelude::v1::*;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(Level::Debug.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut conn = match memflow_qemu_procfs::create_connector(&ConnectorArgs::new()) {
|
|
||||||
Ok(br) => br,
|
|
||||||
Err(e) => {
|
|
||||||
info!("couldn't open memory read context: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let metadata = conn.metadata();
|
|
||||||
info!("Received metadata: {:?}", metadata);
|
|
||||||
|
|
||||||
let mut mem = vec![0; 8];
|
|
||||||
conn.phys_read_raw_into(Address::from(0x1000).into(), &mut mem)
|
|
||||||
.unwrap();
|
|
||||||
info!("Received memory: {:?}", mem);
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let mut counter = 0;
|
|
||||||
loop {
|
|
||||||
let mut buf = vec![0; 0x1000];
|
|
||||||
conn.phys_read_raw_into(Address::from(0x1000).into(), &mut buf)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
counter += 1;
|
|
||||||
if (counter % 10000000) == 0 {
|
|
||||||
let elapsed = start.elapsed().as_millis() as f64;
|
|
||||||
if elapsed > 0.0 {
|
|
||||||
info!("{} reads/sec", (f64::from(counter)) / elapsed * 1000.0);
|
|
||||||
info!("{} ms/read", elapsed / (f64::from(counter)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
cargo build --release --all-features
|
|
||||||
|
|
||||||
# install connector to system dir
|
|
||||||
if [ ! -z "$1" ] && [ $1 = "--system" ]; then
|
|
||||||
echo "installing connector system-wide in /usr/lib/memflow"
|
|
||||||
if [[ ! -d /usr/lib/memflow ]]; then
|
|
||||||
sudo mkdir /usr/lib/memflow
|
|
||||||
fi
|
|
||||||
sudo cp target/release/libmemflow_qemu_procfs.so /usr/lib/memflow
|
|
||||||
fi
|
|
||||||
|
|
||||||
# install connector in user dir
|
|
||||||
echo "installing connector for user in ~/.local/lib/memflow"
|
|
||||||
if [[ ! -d ~/.local/lib/memflow ]]; then
|
|
||||||
mkdir -p ~/.local/lib/memflow
|
|
||||||
fi
|
|
||||||
cp target/release/libmemflow_qemu_procfs.so ~/.local/lib/memflow
|
|
@ -1,475 +0,0 @@
|
|||||||
use log::info;
|
|
||||||
|
|
||||||
use core::ffi::c_void;
|
|
||||||
use libc::{c_ulong, iovec, pid_t, sysconf, _SC_IOV_MAX};
|
|
||||||
|
|
||||||
use memflow::prelude::v1::*;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
struct IoSendVec(iovec);
|
|
||||||
|
|
||||||
unsafe impl Send for IoSendVec {}
|
|
||||||
|
|
||||||
fn qemu_arg_opt(args: &[String], argname: &str, argopt: &str) -> Option<String> {
|
|
||||||
for (idx, arg) in args.iter().enumerate() {
|
|
||||||
if arg == argname {
|
|
||||||
let name = args[idx + 1].split(',');
|
|
||||||
for (i, kv) in name.clone().enumerate() {
|
|
||||||
let kvsplt = kv.split('=').collect::<Vec<_>>();
|
|
||||||
if kvsplt.len() == 2 {
|
|
||||||
if kvsplt[0] == argopt {
|
|
||||||
return Some(kvsplt[1].to_string());
|
|
||||||
}
|
|
||||||
} else if i == 0 {
|
|
||||||
return Some(kv.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_qemu(process: &procfs::process::Process) -> bool {
|
|
||||||
process
|
|
||||||
.cmdline()
|
|
||||||
.ok()
|
|
||||||
.and_then(|cmdline| {
|
|
||||||
cmdline.iter().nth(0).and_then(|cmd| {
|
|
||||||
std::path::Path::new(cmd)
|
|
||||||
.file_name()
|
|
||||||
.and_then(|exe| exe.to_str())
|
|
||||||
.map(|v| v.contains("qemu-system-"))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct QemuProcfs {
|
|
||||||
pub pid: pid_t,
|
|
||||||
pub mem_map: MemoryMap<(Address, usize)>,
|
|
||||||
temp_iov: Box<[IoSendVec]>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl QemuProcfs {
|
|
||||||
pub fn new() -> Result<Self> {
|
|
||||||
let prcs = procfs::process::all_processes()
|
|
||||||
.map_err(|_| Error::Connector("unable to list procfs processes"))?;
|
|
||||||
let prc = prcs
|
|
||||||
.iter()
|
|
||||||
.find(|p| is_qemu(p))
|
|
||||||
.ok_or_else(|| Error::Connector("qemu process not found"))?;
|
|
||||||
info!("qemu process found with pid {:?}", prc.stat.pid);
|
|
||||||
|
|
||||||
Self::with_process(prc)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_guest_name(name: &str) -> Result<Self> {
|
|
||||||
let prcs = procfs::process::all_processes()
|
|
||||||
.map_err(|_| Error::Connector("unable to list procefs processes"))?;
|
|
||||||
let (prc, _) = prcs
|
|
||||||
.iter()
|
|
||||||
.filter(|p| is_qemu(p))
|
|
||||||
.filter_map(|p| {
|
|
||||||
if let Ok(c) = p.cmdline() {
|
|
||||||
Some((p, c))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.find(|(_, c)| qemu_arg_opt(c, "-name", "guest").unwrap_or_default() == name)
|
|
||||||
.ok_or_else(|| Error::Connector("qemu process not found"))?;
|
|
||||||
info!(
|
|
||||||
"qemu process with name {} found with pid {:?}",
|
|
||||||
name, prc.stat.pid
|
|
||||||
);
|
|
||||||
|
|
||||||
Self::with_process(prc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn with_process(prc: &procfs::process::Process) -> Result<Self> {
|
|
||||||
// find biggest memory mapping in qemu process
|
|
||||||
let mut maps = prc
|
|
||||||
.maps()
|
|
||||||
.map_err(|_| Error::Connector("Unable to retrieve Qemu memory maps. Did u run memflow with the correct access rights (SYS_PTRACE or root)?"))?;
|
|
||||||
maps.sort_by(|b, a| {
|
|
||||||
(a.address.1 - a.address.0)
|
|
||||||
.partial_cmp(&(b.address.1 - b.address.0))
|
|
||||||
.unwrap()
|
|
||||||
});
|
|
||||||
let map = maps
|
|
||||||
.get(0)
|
|
||||||
.ok_or_else(|| Error::Connector("Qemu memory map could not be read"))?;
|
|
||||||
info!("qemu memory map found {:?}", map);
|
|
||||||
|
|
||||||
let map_base = map.address.0 as usize;
|
|
||||||
let map_size = (map.address.1 - map.address.0) as usize;
|
|
||||||
info!("qemu memory map size: {:x}", map_size);
|
|
||||||
|
|
||||||
// TODO: instead of hardcoding the memory regions per machine we could just use the hmp to retrieve the proper ranges:
|
|
||||||
// sudo virsh qemu-monitor-command win10 --hmp 'info mtree -f' | grep pc\.ram
|
|
||||||
|
|
||||||
// find machine architecture
|
|
||||||
let machine = qemu_arg_opt(
|
|
||||||
&prc.cmdline()
|
|
||||||
.map_err(|_| Error::Connector("Unable to parse qemu arguments"))?,
|
|
||||||
"-machine",
|
|
||||||
"type",
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|| "pc".into());
|
|
||||||
info!("qemu process started with machine: {}", machine);
|
|
||||||
|
|
||||||
let mut mem_map = MemoryMap::new();
|
|
||||||
if machine.contains("q35") {
|
|
||||||
// q35 -> subtract 2GB
|
|
||||||
/*
|
|
||||||
0000000000000000-000000000009ffff (prio 0, ram): pc.ram KVM
|
|
||||||
00000000000c0000-00000000000c3fff (prio 0, rom): pc.ram @00000000000c0000 KVM
|
|
||||||
0000000000100000-000000007fffffff (prio 0, ram): pc.ram @0000000000100000 KVM
|
|
||||||
0000000100000000-000000047fffffff (prio 0, ram): pc.ram @0000000080000000 KVM
|
|
||||||
*/
|
|
||||||
// we add all regions additionally shifted to the proper qemu memory map address
|
|
||||||
mem_map.push_range(Address::NULL, size::kb(640).into(), map_base.into()); // section: [start - 640kb] -> map to start
|
|
||||||
// If larger than this specific size, second half after 2 gigs gets moved over past 4gb
|
|
||||||
// TODO: Probably the same happens with i1440-fx
|
|
||||||
if map_size >= size::mb(2816) {
|
|
||||||
mem_map.push_range(
|
|
||||||
size::mb(1).into(),
|
|
||||||
size::gb(2).into(),
|
|
||||||
(map_base + size::mb(1)).into(),
|
|
||||||
); // section: [1mb - 2gb] -> map to 1mb
|
|
||||||
mem_map.push_range(
|
|
||||||
size::gb(4).into(),
|
|
||||||
(map_size + size::gb(2)).into(),
|
|
||||||
(map_base + size::gb(2)).into(),
|
|
||||||
); // section: [4gb - max] -> map to 2gb
|
|
||||||
} else {
|
|
||||||
mem_map.push_range(
|
|
||||||
size::mb(1).into(),
|
|
||||||
map_size.into(),
|
|
||||||
(map_base + size::mb(1)).into(),
|
|
||||||
); // section: [1mb - max] -> map to 1mb
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// pc-i1440fx
|
|
||||||
/*
|
|
||||||
0000000000000000-00000000000bffff (prio 0, ram): pc.ram KVM
|
|
||||||
00000000000c0000-00000000000cafff (prio 0, rom): pc.ram @00000000000c0000 KVM
|
|
||||||
00000000000cb000-00000000000cdfff (prio 0, ram): pc.ram @00000000000cb000 KVM
|
|
||||||
00000000000ce000-00000000000e7fff (prio 0, rom): pc.ram @00000000000ce000 KVM
|
|
||||||
00000000000e8000-00000000000effff (prio 0, ram): pc.ram @00000000000e8000 KVM
|
|
||||||
00000000000f0000-00000000000fffff (prio 0, rom): pc.ram @00000000000f0000 KVM
|
|
||||||
0000000000100000-00000000bfffffff (prio 0, ram): pc.ram @0000000000100000 KVM
|
|
||||||
0000000100000000-000000023fffffff (prio 0, ram): pc.ram @00000000c0000000 KVM
|
|
||||||
*/
|
|
||||||
mem_map.push_range(Address::NULL, size::kb(768).into(), map_base.into()); // section: [start - 768kb] -> map to start
|
|
||||||
mem_map.push_range(
|
|
||||||
size::kb(812).into(),
|
|
||||||
size::kb(824).into(),
|
|
||||||
(map_base + size::kb(812)).into(),
|
|
||||||
); // section: [768kb - 812kb] -> map to 768kb
|
|
||||||
mem_map.push_range(
|
|
||||||
size::kb(928).into(),
|
|
||||||
size::kb(960).into(),
|
|
||||||
(map_base + size::kb(928)).into(),
|
|
||||||
); // section: [928kb - 960kb] -> map to 928kb
|
|
||||||
mem_map.push_range(
|
|
||||||
size::mb(1).into(),
|
|
||||||
size::gb(3).into(),
|
|
||||||
(map_base + size::mb(1)).into(),
|
|
||||||
); // section: [1mb - 3gb] -> map to 1mb
|
|
||||||
mem_map.push_range(
|
|
||||||
size::gb(4).into(),
|
|
||||||
(map_size + size::gb(1)).into(),
|
|
||||||
(map_base + size::gb(3)).into(),
|
|
||||||
); // section: [4gb - max] -> map to 3gb
|
|
||||||
}
|
|
||||||
info!("qemu machine mem_map: {:?}", mem_map);
|
|
||||||
|
|
||||||
let iov_max = unsafe { sysconf(_SC_IOV_MAX) } as usize;
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
pid: prc.stat.pid,
|
|
||||||
mem_map,
|
|
||||||
temp_iov: vec![
|
|
||||||
IoSendVec {
|
|
||||||
0: iovec {
|
|
||||||
iov_base: std::ptr::null_mut::<c_void>(),
|
|
||||||
iov_len: 0
|
|
||||||
}
|
|
||||||
};
|
|
||||||
iov_max * 2
|
|
||||||
]
|
|
||||||
.into_boxed_slice(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_iovec(addr: &Address, data: &[u8], liov: &mut IoSendVec, riov: &mut IoSendVec) {
|
|
||||||
let iov_len = data.len();
|
|
||||||
|
|
||||||
liov.0 = iovec {
|
|
||||||
iov_base: data.as_ptr() as *mut c_void,
|
|
||||||
iov_len,
|
|
||||||
};
|
|
||||||
|
|
||||||
riov.0 = iovec {
|
|
||||||
iov_base: addr.as_u64() as *mut c_void,
|
|
||||||
iov_len,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vm_error() -> Error {
|
|
||||||
match unsafe { *libc::__errno_location() } {
|
|
||||||
libc::EFAULT => Error::Connector("process_vm_readv failed: EFAULT (remote memory address is invalid)"),
|
|
||||||
libc::ENOMEM => Error::Connector("process_vm_readv failed: ENOMEM (unable to allocate memory for internal copies)"),
|
|
||||||
libc::EPERM => Error::Connector("process_vm_readv failed: EPERM (insifficient permissions to access the target address space)"),
|
|
||||||
libc::ESRCH => Error::Connector("process_vm_readv failed: ESRCH (process not found)"),
|
|
||||||
libc::EINVAL => Error::Connector("process_vm_readv failed: EINVAL (invalid value)"),
|
|
||||||
_ => Error::Connector("process_vm_readv failed: unknown error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PhysicalMemory for QemuProcfs {
|
|
||||||
fn phys_read_raw_list(&mut self, data: &mut [PhysicalReadData]) -> Result<()> {
|
|
||||||
let mem_map = &self.mem_map;
|
|
||||||
let temp_iov = &mut self.temp_iov;
|
|
||||||
|
|
||||||
let mut void = FnExtend::void();
|
|
||||||
let mut iter = mem_map.map_iter(
|
|
||||||
data.iter_mut()
|
|
||||||
.map(|PhysicalReadData(addr, buf)| (*addr, &mut **buf)),
|
|
||||||
&mut void,
|
|
||||||
);
|
|
||||||
|
|
||||||
let max_iov = temp_iov.len() / 2;
|
|
||||||
let (iov_local, iov_remote) = temp_iov.split_at_mut(max_iov);
|
|
||||||
|
|
||||||
let mut elem = iter.next();
|
|
||||||
|
|
||||||
let mut iov_iter = iov_local.iter_mut().zip(iov_remote.iter_mut()).enumerate();
|
|
||||||
let mut iov_next = iov_iter.next();
|
|
||||||
|
|
||||||
while let Some(((addr, _), out)) = elem {
|
|
||||||
let (cnt, (liov, riov)) = iov_next.unwrap();
|
|
||||||
|
|
||||||
Self::fill_iovec(&addr, out, liov, riov);
|
|
||||||
|
|
||||||
iov_next = iov_iter.next();
|
|
||||||
elem = iter.next();
|
|
||||||
|
|
||||||
if elem.is_none() || iov_next.is_none() {
|
|
||||||
if unsafe {
|
|
||||||
libc::process_vm_readv(
|
|
||||||
self.pid,
|
|
||||||
iov_local.as_ptr().cast(),
|
|
||||||
(cnt + 1) as c_ulong,
|
|
||||||
iov_remote.as_ptr().cast(),
|
|
||||||
(cnt + 1) as c_ulong,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
} == -1
|
|
||||||
{
|
|
||||||
return Err(Self::vm_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
iov_iter = iov_local.iter_mut().zip(iov_remote.iter_mut()).enumerate();
|
|
||||||
iov_next = iov_iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phys_write_raw_list(&mut self, data: &[PhysicalWriteData]) -> Result<()> {
|
|
||||||
let mem_map = &self.mem_map;
|
|
||||||
let temp_iov = &mut self.temp_iov;
|
|
||||||
|
|
||||||
let mut void = FnExtend::void();
|
|
||||||
let mut iter = mem_map.map_iter(data.iter().copied().map(<_>::from), &mut void);
|
|
||||||
//let mut iter = mem_map.map_iter(data.iter(), &mut FnExtend::new(|_|{}));
|
|
||||||
|
|
||||||
let max_iov = temp_iov.len() / 2;
|
|
||||||
let (iov_local, iov_remote) = temp_iov.split_at_mut(max_iov);
|
|
||||||
|
|
||||||
let mut elem = iter.next();
|
|
||||||
|
|
||||||
let mut iov_iter = iov_local.iter_mut().zip(iov_remote.iter_mut()).enumerate();
|
|
||||||
let mut iov_next = iov_iter.next();
|
|
||||||
|
|
||||||
while let Some(((addr, _), out)) = elem {
|
|
||||||
let (cnt, (liov, riov)) = iov_next.unwrap();
|
|
||||||
|
|
||||||
Self::fill_iovec(&addr, out, liov, riov);
|
|
||||||
|
|
||||||
iov_next = iov_iter.next();
|
|
||||||
elem = iter.next();
|
|
||||||
|
|
||||||
if elem.is_none() || iov_next.is_none() {
|
|
||||||
if unsafe {
|
|
||||||
libc::process_vm_writev(
|
|
||||||
self.pid,
|
|
||||||
iov_local.as_ptr().cast(),
|
|
||||||
(cnt + 1) as c_ulong,
|
|
||||||
iov_remote.as_ptr().cast(),
|
|
||||||
(cnt + 1) as c_ulong,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
} == -1
|
|
||||||
{
|
|
||||||
return Err(Self::vm_error());
|
|
||||||
}
|
|
||||||
|
|
||||||
iov_iter = iov_local.iter_mut().zip(iov_remote.iter_mut()).enumerate();
|
|
||||||
iov_next = iov_iter.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn metadata(&self) -> PhysicalMemoryMetadata {
|
|
||||||
PhysicalMemoryMetadata {
|
|
||||||
size: self
|
|
||||||
.mem_map
|
|
||||||
.as_ref()
|
|
||||||
.iter()
|
|
||||||
.last()
|
|
||||||
.map(|map| map.base().as_usize() + map.output().1)
|
|
||||||
.unwrap(),
|
|
||||||
readonly: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new Qemu Procfs Connector instance.
|
|
||||||
#[connector(name = "qemu_procfs")]
|
|
||||||
pub fn create_connector(args: &ConnectorArgs) -> Result<QemuProcfs> {
|
|
||||||
if let Some(name) = args.get("name").or_else(|| args.get_default()) {
|
|
||||||
QemuProcfs::with_guest_name(name)
|
|
||||||
} else {
|
|
||||||
QemuProcfs::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_name() {
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-name".to_string(), "win10-test".to_string()],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
Some("win10-test".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&[
|
|
||||||
"-test".to_string(),
|
|
||||||
"-name".to_string(),
|
|
||||||
"win10-test".to_string()
|
|
||||||
],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
Some("win10-test".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-name".to_string(), "win10-test,arg=opt".to_string()],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
Some("win10-test".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-name".to_string(), "guest=win10-test,arg=opt".to_string()],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
Some("win10-test".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-name".to_string(), "arg=opt,guest=win10-test".to_string()],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
Some("win10-test".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-name".to_string(), "arg=opt".to_string()],
|
|
||||||
"-name",
|
|
||||||
"guest"
|
|
||||||
),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_machine() {
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-machine".to_string(), "q35".to_string()],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
Some("q35".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&[
|
|
||||||
"-test".to_string(),
|
|
||||||
"-machine".to_string(),
|
|
||||||
"q35".to_string()
|
|
||||||
],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
Some("q35".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-machine".to_string(), "q35,arg=opt".to_string()],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
Some("q35".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-machine".to_string(), "type=pc,arg=opt".to_string()],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
Some("pc".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&[
|
|
||||||
"-machine".to_string(),
|
|
||||||
"arg=opt,type=pc-i1440fx".to_string()
|
|
||||||
],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
Some("pc-i1440fx".into())
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
qemu_arg_opt(
|
|
||||||
&["-machine".to_string(), "arg=opt".to_string()],
|
|
||||||
"-machine",
|
|
||||||
"type"
|
|
||||||
),
|
|
||||||
None
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
/target
|
|
||||||
**/*.rs.bk
|
|
||||||
bindings
|
|
||||||
**/node_modules
|
|
||||||
**/*.out
|
|
||||||
**/*.o
|
|
@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "memflow-win32-ffi"
|
|
||||||
version = "0.1.5"
|
|
||||||
authors = ["Aurimas Blažulionis <0x60@pm.me>"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "C bindings to memflow-win32"
|
|
||||||
documentation = "https://docs.rs/memflow-win32-ffi"
|
|
||||||
readme = "README.md"
|
|
||||||
homepage = "https://memflow.github.io"
|
|
||||||
repository = "https://github.com/memflow/memflow"
|
|
||||||
license-file = "../LICENSE"
|
|
||||||
keywords = [ "memflow", "introspection", "memory", "dma" ]
|
|
||||||
categories = [ "api-bindings", "memory-management", "os" ]
|
|
||||||
|
|
||||||
[badges]
|
|
||||||
maintenance = { status = "actively-developed" }
|
|
||||||
codecov = { repository = "github", branch = "master", service = "github" }
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "memflow_win32_ffi"
|
|
||||||
crate-type = ["lib", "cdylib", "staticlib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
memflow-win32 = { version = "0.1", path = "../memflow-win32" }
|
|
||||||
memflow = { version = "0.1", path = "../memflow" }
|
|
||||||
memflow-ffi = { version = "0.1", path = "../memflow-ffi" }
|
|
@ -1,47 +0,0 @@
|
|||||||
# memflow-win32-ffi
|
|
||||||
[![Crates.io](https://img.shields.io/crates/v/memflow.svg)](https://crates.io/crates/memflow)
|
|
||||||
![build and test](https://github.com/memflow/memflow/workflows/Build%20and%20test/badge.svg?branch=dev)
|
|
||||||
[![codecov](https://codecov.io/gh/memflow/memflow/branch/master/graph/badge.svg?token=XT7R158N6W)](https://codecov.io/gh/memflow/memflow)
|
|
||||||
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
|
|
||||||
[![Discord](https://img.shields.io/discord/738739624976973835?color=%20%237289da&label=Discord)](https://discord.gg/afsEtMR)
|
|
||||||
|
|
||||||
The [memflow](https://github.com/memflow/memflow) win32 FFI crate provides an interface to the memflow-win32 API for C/C++. Currently a single `memflow_win32.h` file is generated aside from the dynamic library that can be used to interact with memflow.
|
|
||||||
|
|
||||||
This FFI library is intended to be used in combination with the [memflow-ffi](https://github.com/memflow/memflow/memflow-ffi) library.
|
|
||||||
|
|
||||||
A simple example that initializes the memflow-ffi and memflow-win32-ffi:
|
|
||||||
```cpp
|
|
||||||
#include "memflow_win32.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
log_init(1);
|
|
||||||
|
|
||||||
ConnectorInventory *inv = inventory_try_new();
|
|
||||||
printf("inv: %p\n", inv);
|
|
||||||
|
|
||||||
const char *conn_name = argc > 1? argv[1]: "kvm";
|
|
||||||
const char *conn_arg = argc > 2? argv[2]: "";
|
|
||||||
|
|
||||||
CloneablePhysicalMemoryObj *conn =
|
|
||||||
inventory_create_connector(inv, conn_name, conn_arg);
|
|
||||||
printf("conn: %p\n", conn);
|
|
||||||
|
|
||||||
if (conn) {
|
|
||||||
Kernel *kernel = kernel_build(conn);
|
|
||||||
printf("Kernel: %p\n", kernel);
|
|
||||||
Win32Version ver = kernel_winver(kernel);
|
|
||||||
printf("major: %d\n", ver.nt_major_version);
|
|
||||||
printf("minor: %d\n", ver.nt_minor_version);
|
|
||||||
printf("build: %d\n", ver.nt_build_number);
|
|
||||||
|
|
||||||
kernel_free(kernel);
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory_free(inv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Additional examples can be found in the `examples` folder as well as in the [memflow-ffi](https://github.com/memflow/memflow/memflow-ffi) crate.
|
|
@ -1,3 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
cargo build --release --workspace
|
|
||||||
cbindgen --config cbindgen.toml --crate memflow-win32-ffi --output memflow_win32.h
|
|
@ -1,22 +0,0 @@
|
|||||||
language = "C"
|
|
||||||
|
|
||||||
include_guard = "MEMFLOW_WIN32_H"
|
|
||||||
tab_width = 4
|
|
||||||
documentation_style = "doxy"
|
|
||||||
style = "both"
|
|
||||||
cpp_compat = true
|
|
||||||
includes = ["memflow.h"]
|
|
||||||
|
|
||||||
[parse]
|
|
||||||
parse_deps = true
|
|
||||||
|
|
||||||
include = ["memflow-win32", "memflow"]
|
|
||||||
|
|
||||||
[macro_expansion]
|
|
||||||
bitflags = true
|
|
||||||
|
|
||||||
[fn]
|
|
||||||
sort_by = "None"
|
|
||||||
|
|
||||||
[export]
|
|
||||||
exclude = ["PageType", "Address"]
|
|
@ -1,22 +0,0 @@
|
|||||||
CC =gcc
|
|
||||||
CFLAGS =-I../ -I../../memflow-ffi/ -L../../target/release
|
|
||||||
LIBS=-lm -ldl -lpthread -l:libmemflow_win32_ffi.a
|
|
||||||
|
|
||||||
ODIR=./
|
|
||||||
|
|
||||||
%.o: %.c $(DEPS)
|
|
||||||
$(CC) -c -o $@ $< $(CFLAGS)
|
|
||||||
|
|
||||||
process_list.out: process_list.o
|
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
dump_header.out: dump_header.o
|
|
||||||
$(CC) -o $@ $^ $(CFLAGS) $(LIBS)
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: process_list.out dump_header.out
|
|
||||||
|
|
||||||
.DEFAULT_GOAL := all
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(ODIR)/*.o
|
|
@ -1,61 +0,0 @@
|
|||||||
#include "memflow_win32.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
log_init(1);
|
|
||||||
|
|
||||||
ConnectorInventory *inv = inventory_try_new();
|
|
||||||
printf("inv: %p\n", inv);
|
|
||||||
|
|
||||||
const char *conn_name = argc > 1? argv[1]: "kvm";
|
|
||||||
const char *conn_arg = argc > 2? argv[2]: "";
|
|
||||||
|
|
||||||
const char *proc_name = argc > 3? argv[3]: "lsass.exe";
|
|
||||||
const char *dll_name = argc > 4? argv[4]: "ntdll.dll";
|
|
||||||
|
|
||||||
CloneablePhysicalMemoryObj *conn = inventory_create_connector(inv, conn_name, conn_arg);
|
|
||||||
printf("conn: %p\n", conn);
|
|
||||||
|
|
||||||
if (conn) {
|
|
||||||
Kernel *kernel = kernel_build(conn);
|
|
||||||
printf("Kernel: %p\n", kernel);
|
|
||||||
Win32Version ver = kernel_winver(kernel);
|
|
||||||
printf("major: %d\n", ver.nt_major_version);
|
|
||||||
printf("minor: %d\n", ver.nt_minor_version);
|
|
||||||
printf("build: %d\n", ver.nt_build_number);
|
|
||||||
|
|
||||||
Win32Process *process = kernel_into_process(kernel, proc_name);
|
|
||||||
|
|
||||||
if (process) {
|
|
||||||
Win32ModuleInfo *module = process_module_info(process, dll_name);
|
|
||||||
|
|
||||||
if (module) {
|
|
||||||
OsProcessModuleInfoObj *obj = module_info_trait(module);
|
|
||||||
Address base = os_process_module_base(obj);
|
|
||||||
os_process_module_free(obj);
|
|
||||||
VirtualMemoryObj *virt_mem = process_virt_mem(process);
|
|
||||||
|
|
||||||
char header[256];
|
|
||||||
if (!virt_read_raw_into(virt_mem, base, header, 256)) {
|
|
||||||
printf("Read successful!\n");
|
|
||||||
for (int o = 0; o < 8; o++) {
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
printf("%2hhx ", header[o * 32 + i]);
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
printf("Failed to read!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
virt_free(virt_mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
process_free(process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory_free(inv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
#include "memflow_win32.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
log_init(1);
|
|
||||||
|
|
||||||
ConnectorInventory *inv = inventory_scan();
|
|
||||||
printf("inv: %p\n", inv);
|
|
||||||
|
|
||||||
const char *conn_name = argc > 1? argv[1]: "kvm";
|
|
||||||
const char *conn_arg = argc > 2? argv[2]: "";
|
|
||||||
|
|
||||||
CloneablePhysicalMemoryObj *conn = inventory_create_connector(inv, conn_name, conn_arg);
|
|
||||||
printf("conn: %p\n", conn);
|
|
||||||
|
|
||||||
if (conn) {
|
|
||||||
Kernel *kernel = kernel_build(conn);
|
|
||||||
printf("Kernel: %p\n", kernel);
|
|
||||||
Win32Version ver = kernel_winver(kernel);
|
|
||||||
printf("major: %d\n", ver.nt_major_version);
|
|
||||||
printf("minor: %d\n", ver.nt_minor_version);
|
|
||||||
printf("build: %d\n", ver.nt_build_number);
|
|
||||||
|
|
||||||
Win32ProcessInfo *processes[512];
|
|
||||||
size_t process_count = kernel_process_info_list(kernel, processes, 512);
|
|
||||||
|
|
||||||
printf("Process List:\n");
|
|
||||||
printf("%-8s | %-16s | %-16s | %-12s | %-5s\n", "PID", "Name", "Base", "DTB", "Wow64");
|
|
||||||
|
|
||||||
for (size_t i = 0; i < process_count; i++) {
|
|
||||||
Win32ProcessInfo *process = processes[i];
|
|
||||||
OsProcessInfoObj *info = process_info_trait(process);
|
|
||||||
char name[32];
|
|
||||||
os_process_info_name(info, name, 32);
|
|
||||||
|
|
||||||
printf("%-8d | %-16s | %-16lx | %-12lx | %-5s\n",
|
|
||||||
os_process_info_pid(info),
|
|
||||||
name,
|
|
||||||
process_info_section_base(process),
|
|
||||||
process_info_dtb(process),
|
|
||||||
process_info_wow64(process)? "Yes" : "No"
|
|
||||||
);
|
|
||||||
|
|
||||||
os_process_info_free(info);
|
|
||||||
process_info_free(process);
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel_free(kernel);
|
|
||||||
}
|
|
||||||
|
|
||||||
inventory_free(inv);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,321 +0,0 @@
|
|||||||
#ifndef MEMFLOW_WIN32_H
|
|
||||||
#define MEMFLOW_WIN32_H
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "memflow.h"
|
|
||||||
|
|
||||||
typedef struct Kernel_FFIMemory__FFIVirtualTranslate Kernel_FFIMemory__FFIVirtualTranslate;
|
|
||||||
|
|
||||||
typedef struct Win32ModuleInfo Win32ModuleInfo;
|
|
||||||
|
|
||||||
typedef struct Win32ProcessInfo Win32ProcessInfo;
|
|
||||||
|
|
||||||
typedef struct Win32Process_FFIVirtualMemory Win32Process_FFIVirtualMemory;
|
|
||||||
|
|
||||||
typedef Kernel_FFIMemory__FFIVirtualTranslate Kernel;
|
|
||||||
|
|
||||||
typedef struct StartBlock {
|
|
||||||
Address kernel_hint;
|
|
||||||
Address dtb;
|
|
||||||
} StartBlock;
|
|
||||||
|
|
||||||
typedef struct Win32Version {
|
|
||||||
uint32_t nt_major_version;
|
|
||||||
uint32_t nt_minor_version;
|
|
||||||
uint32_t nt_build_number;
|
|
||||||
} Win32Version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type alias for a PID.
|
|
||||||
*/
|
|
||||||
typedef uint32_t PID;
|
|
||||||
|
|
||||||
typedef Win32Process_FFIVirtualMemory Win32Process;
|
|
||||||
|
|
||||||
typedef struct Win32ArchOffsets {
|
|
||||||
uintptr_t peb_ldr;
|
|
||||||
uintptr_t ldr_list;
|
|
||||||
uintptr_t ldr_data_base;
|
|
||||||
uintptr_t ldr_data_size;
|
|
||||||
uintptr_t ldr_data_full_name;
|
|
||||||
uintptr_t ldr_data_base_name;
|
|
||||||
} Win32ArchOffsets;
|
|
||||||
|
|
||||||
typedef struct Win32ModuleListInfo {
|
|
||||||
Address module_base;
|
|
||||||
Win32ArchOffsets offsets;
|
|
||||||
} Win32ModuleListInfo;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a cloneable kernel object with default caching parameters
|
|
||||||
*
|
|
||||||
* This function will take ownership of the input `mem` object.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `mem` must be a heap allocated memory reference, created by one of the API's functions.
|
|
||||||
* Reference to it becomes invalid.
|
|
||||||
*/
|
|
||||||
Kernel *kernel_build(CloneablePhysicalMemoryObj *mem);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build a cloneable kernel object with custom caching parameters
|
|
||||||
*
|
|
||||||
* This function will take ownership of the input `mem` object.
|
|
||||||
*
|
|
||||||
* vat_cache_entries must be positive, or the program will panic upon memory reads or writes.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `mem` must be a heap allocated memory reference, created by one of the API's functions.
|
|
||||||
* Reference to it becomes invalid.
|
|
||||||
*/
|
|
||||||
Kernel *kernel_build_custom(CloneablePhysicalMemoryObj *mem,
|
|
||||||
uint64_t page_cache_time_ms,
|
|
||||||
PageType page_cache_flags,
|
|
||||||
uintptr_t page_cache_size_kb,
|
|
||||||
uint64_t vat_cache_time_ms,
|
|
||||||
uintptr_t vat_cache_entries);
|
|
||||||
|
|
||||||
Kernel *kernel_clone(const Kernel *kernel);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a kernel object
|
|
||||||
*
|
|
||||||
* This will free the input `kernel` object (including the underlying memory object)
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid reference heap allocated by one of the above functions.
|
|
||||||
*/
|
|
||||||
void kernel_free(Kernel *kernel);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a kernel object and return its underlying memory object
|
|
||||||
*
|
|
||||||
* This will free the input `kernel` object, and return the underlying memory object. It will free
|
|
||||||
* the object from any additional caching that `kernel` had in place.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid reference heap allocated by one of the above functions.
|
|
||||||
*/
|
|
||||||
CloneablePhysicalMemoryObj *kernel_destroy(Kernel *kernel);
|
|
||||||
|
|
||||||
StartBlock kernel_start_block(const Kernel *kernel);
|
|
||||||
|
|
||||||
Win32Version kernel_winver(const Kernel *kernel);
|
|
||||||
|
|
||||||
Win32Version kernel_winver_unmasked(const Kernel *kernel);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a list of peorcess addresses
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `buffer` must be a valid buffer of size at least `max_size`
|
|
||||||
*/
|
|
||||||
uintptr_t kernel_eprocess_list(Kernel *kernel, Address *buffer, uintptr_t max_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a list of processes
|
|
||||||
*
|
|
||||||
* This will fill `buffer` with a list of win32 process information. These processes will need to be
|
|
||||||
* individually freed with `process_info_free`
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `buffer` must be a valid that can contain at least `max_size` references to `Win32ProcessInfo`.
|
|
||||||
*/
|
|
||||||
uintptr_t kernel_process_info_list(Kernel *kernel, Win32ProcessInfo **buffer, uintptr_t max_size);
|
|
||||||
|
|
||||||
Win32ProcessInfo *kernel_kernel_process_info(Kernel *kernel);
|
|
||||||
|
|
||||||
Win32ProcessInfo *kernel_process_info_from_eprocess(Kernel *kernel, Address eprocess);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve process information by name
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `name` must be a valid null terminated string
|
|
||||||
*/
|
|
||||||
Win32ProcessInfo *kernel_process_info(Kernel *kernel, const char *name);
|
|
||||||
|
|
||||||
Win32ProcessInfo *kernel_process_info_pid(Kernel *kernel, PID pid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a process by looking up its name
|
|
||||||
*
|
|
||||||
* This will consume `kernel` and free it later on.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `name` must be a valid null terminated string
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
* invalid.
|
|
||||||
*/
|
|
||||||
Win32Process *kernel_into_process(Kernel *kernel, const char *name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a process by looking up its PID
|
|
||||||
*
|
|
||||||
* This will consume `kernel` and free it later on.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
* invalid.
|
|
||||||
*/
|
|
||||||
Win32Process *kernel_into_process_pid(Kernel *kernel, PID pid);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a kernel process insatance
|
|
||||||
*
|
|
||||||
* This will consume `kernel` and free it later on.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
* invalid.
|
|
||||||
*/
|
|
||||||
Win32Process *kernel_into_kernel_process(Kernel *kernel);
|
|
||||||
|
|
||||||
OsProcessModuleInfoObj *module_info_trait(Win32ModuleInfo *info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a win32 module info instance.
|
|
||||||
*
|
|
||||||
* Note that it is not the same as `OsProcessModuleInfoObj`, and those references need to be freed
|
|
||||||
* manually.
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `info` must be a unique heap allocated reference to `Win32ModuleInfo`, and after this call the
|
|
||||||
* reference will become invalid.
|
|
||||||
*/
|
|
||||||
void module_info_free(Win32ModuleInfo *info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a process with kernel and process info
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `kernel` must be a valid heap allocated reference to a `Kernel` object. After the function
|
|
||||||
* call, the reference becomes invalid.
|
|
||||||
*/
|
|
||||||
Win32Process *process_with_kernel(Kernel *kernel, const Win32ProcessInfo *proc_info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve refernce to the underlying virtual memory object
|
|
||||||
*
|
|
||||||
* This will return a static reference to the virtual memory object. It will only be valid as long
|
|
||||||
* as `process` if valid, and needs to be freed manually using `virt_free` regardless if the
|
|
||||||
* process if freed or not.
|
|
||||||
*/
|
|
||||||
VirtualMemoryObj *process_virt_mem(Win32Process *process);
|
|
||||||
|
|
||||||
Win32Process *process_clone(const Win32Process *process);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees the `process`
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `process` must be a valid heap allocated reference to a `Win32Process` object. After the
|
|
||||||
* function returns, the reference becomes invalid.
|
|
||||||
*/
|
|
||||||
void process_free(Win32Process *process);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a process module list
|
|
||||||
*
|
|
||||||
* This will fill up to `max_len` elements into `out` with references to `Win32ModuleInfo` objects.
|
|
||||||
*
|
|
||||||
* These references then need to be freed with `module_info_free`
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `out` must be a valid buffer able to contain `max_len` references to `Win32ModuleInfo`.
|
|
||||||
*/
|
|
||||||
uintptr_t process_module_list(Win32Process *process, Win32ModuleInfo **out, uintptr_t max_len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the main module of the process
|
|
||||||
*
|
|
||||||
* This function searches for a module with a base address
|
|
||||||
* matching the section_base address from the ProcessInfo structure.
|
|
||||||
* It then returns a reference to a newly allocated
|
|
||||||
* `Win32ModuleInfo` object, if a module was found (null otherwise).
|
|
||||||
*
|
|
||||||
* The reference later needs to be freed with `module_info_free`
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `process` must be a valid Win32Process pointer.
|
|
||||||
*/
|
|
||||||
Win32ModuleInfo *process_main_module_info(Win32Process *process);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup a module
|
|
||||||
*
|
|
||||||
* This will search for a module called `name`, and return a reference to a newly allocated
|
|
||||||
* `Win32ModuleInfo` object, if a module was found (null otherwise).
|
|
||||||
*
|
|
||||||
* The reference later needs to be freed with `module_info_free`
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `process` must be a valid Win32Process pointer.
|
|
||||||
* `name` must be a valid null terminated string.
|
|
||||||
*/
|
|
||||||
Win32ModuleInfo *process_module_info(Win32Process *process, const char *name);
|
|
||||||
|
|
||||||
OsProcessInfoObj *process_info_trait(Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_dtb(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_section_base(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
int32_t process_info_exit_status(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_ethread(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_wow64(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_peb(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_peb_native(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_peb_wow64(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_teb(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Address process_info_teb_wow64(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Win32ModuleListInfo process_info_module_info(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
Win32ModuleListInfo process_info_module_info_native(const Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free a process information reference
|
|
||||||
*
|
|
||||||
* # Safety
|
|
||||||
*
|
|
||||||
* `info` must be a valid heap allocated reference to a Win32ProcessInfo structure
|
|
||||||
*/
|
|
||||||
void process_info_free(Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif /* MEMFLOW_WIN32_H */
|
|
@ -1,151 +0,0 @@
|
|||||||
#ifndef MEMFLOW_WIN32_HLAPI_H
|
|
||||||
#define MEMFLOW_WIN32_HLAPI_H
|
|
||||||
|
|
||||||
#include "memflow_cpp.h"
|
|
||||||
#include "memflow_win32.h"
|
|
||||||
#include "binddestr.h"
|
|
||||||
|
|
||||||
#ifndef NO_STL_CONTAINERS
|
|
||||||
#include <vector>
|
|
||||||
// Maximum number of entries allowed in the returned lists
|
|
||||||
#ifndef AUTO_VEC_SIZE
|
|
||||||
#define AUTO_VEC_SIZE 2048
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct CKernel;
|
|
||||||
|
|
||||||
struct CWin32ModuleInfo
|
|
||||||
: BindDestr<Win32ModuleInfo, module_info_free>
|
|
||||||
{
|
|
||||||
CWin32ModuleInfo(Win32ModuleInfo *modinfo)
|
|
||||||
: BindDestr(modinfo) {}
|
|
||||||
|
|
||||||
WRAP_FN_TYPE(COsProcessModuleInfo, module, info_trait);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CWin32Process
|
|
||||||
: BindDestr<Win32Process, process_free>
|
|
||||||
{
|
|
||||||
CWin32Process(Win32Process *process)
|
|
||||||
: BindDestr(process) {}
|
|
||||||
|
|
||||||
CWin32Process(CKernel &kernel, Win32ProcessInfo *info);
|
|
||||||
|
|
||||||
WRAP_FN_TYPE(CWin32ModuleInfo, process, module_info);
|
|
||||||
WRAP_FN_TYPE(CVirtualMemory, process, virt_mem);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CWin32ProcessInfo
|
|
||||||
: BindDestr<Win32ProcessInfo, process_info_free>
|
|
||||||
{
|
|
||||||
CWin32ProcessInfo(Win32ProcessInfo *info)
|
|
||||||
: BindDestr(info) {}
|
|
||||||
|
|
||||||
WRAP_FN_TYPE(COsProcessInfo, process_info, trait);
|
|
||||||
WRAP_FN(process_info, dtb);
|
|
||||||
WRAP_FN(process_info, section_base);
|
|
||||||
WRAP_FN(process_info, wow64);
|
|
||||||
WRAP_FN(process_info, peb);
|
|
||||||
WRAP_FN(process_info, peb_native);
|
|
||||||
WRAP_FN(process_info, peb_wow64);
|
|
||||||
WRAP_FN(process_info, teb);
|
|
||||||
WRAP_FN(process_info, teb_wow64);
|
|
||||||
WRAP_FN(process_info, module_info);
|
|
||||||
WRAP_FN(process_info, module_info_native);
|
|
||||||
|
|
||||||
inline operator COsProcessInfo() {
|
|
||||||
return this->trait();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CKernel
|
|
||||||
: BindDestr<Kernel, kernel_free>
|
|
||||||
{
|
|
||||||
CKernel(Kernel *kernel)
|
|
||||||
: BindDestr(kernel) {}
|
|
||||||
|
|
||||||
CKernel(CCloneablePhysicalMemory &mem)
|
|
||||||
: BindDestr(kernel_build(mem.invalidate())) {}
|
|
||||||
|
|
||||||
CKernel(
|
|
||||||
CCloneablePhysicalMemory &mem,
|
|
||||||
uint64_t page_cache_time_ms,
|
|
||||||
PageType page_cache_flags,
|
|
||||||
uintptr_t page_cache_size_kb,
|
|
||||||
uint64_t vat_cache_time_ms,
|
|
||||||
uintptr_t vat_cache_entries
|
|
||||||
) : BindDestr(kernel_build_custom(
|
|
||||||
mem.invalidate(),
|
|
||||||
page_cache_time_ms,
|
|
||||||
page_cache_flags,
|
|
||||||
page_cache_size_kb,
|
|
||||||
vat_cache_time_ms,
|
|
||||||
vat_cache_entries
|
|
||||||
)) {}
|
|
||||||
|
|
||||||
WRAP_FN_TYPE(CKernel, kernel, clone);
|
|
||||||
WRAP_FN_TYPE_INVALIDATE(CCloneablePhysicalMemory, kernel, destroy);
|
|
||||||
WRAP_FN(kernel, start_block);
|
|
||||||
WRAP_FN(kernel, winver);
|
|
||||||
WRAP_FN(kernel, winver_unmasked);
|
|
||||||
WRAP_FN(kernel, eprocess_list);
|
|
||||||
WRAP_FN(kernel, process_info_list);
|
|
||||||
WRAP_FN_TYPE(CWin32ProcessInfo, kernel, kernel_process_info);
|
|
||||||
WRAP_FN_TYPE(CWin32ProcessInfo, kernel, process_info_from_eprocess);
|
|
||||||
WRAP_FN_TYPE(CWin32ProcessInfo, kernel, process_info);
|
|
||||||
WRAP_FN_TYPE(CWin32ProcessInfo, kernel, process_info_pid);
|
|
||||||
WRAP_FN_TYPE_INVALIDATE(CWin32Process, kernel, into_process);
|
|
||||||
WRAP_FN_TYPE_INVALIDATE(CWin32Process, kernel, into_process_pid);
|
|
||||||
WRAP_FN_TYPE_INVALIDATE(CWin32Process, kernel, into_kernel_process);
|
|
||||||
|
|
||||||
#ifndef NO_STL_CONTAINERS
|
|
||||||
// Manual eprocess_list impl
|
|
||||||
std::vector<Address> eprocess_vec(size_t max_size) {
|
|
||||||
Address *buf = (Address *)malloc(sizeof(Address *) * max_size);
|
|
||||||
std::vector<Address> ret;
|
|
||||||
|
|
||||||
if (buf) {
|
|
||||||
size_t size = kernel_eprocess_list(this->inner, buf, max_size);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
ret.push_back(buf[i]);
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Address> eprocess_vec() {
|
|
||||||
return this->eprocess_vec(AUTO_VEC_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manual process_info_list impl
|
|
||||||
std::vector<CWin32ProcessInfo> process_info_vec(size_t max_size) {
|
|
||||||
Win32ProcessInfo **buf = (Win32ProcessInfo **)malloc(sizeof(Win32ProcessInfo *) * max_size);
|
|
||||||
std::vector<CWin32ProcessInfo> ret;
|
|
||||||
|
|
||||||
if (buf) {
|
|
||||||
size_t size = kernel_process_info_list(this->inner, buf, max_size);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++)
|
|
||||||
ret.push_back(CWin32ProcessInfo(buf[i]));
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<CWin32ProcessInfo> process_info_vec() {
|
|
||||||
return this->process_info_vec(AUTO_VEC_SIZE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Extra constructors we couldn't define inside the classes
|
|
||||||
CWin32Process::CWin32Process(CKernel &kernel, Win32ProcessInfo *info)
|
|
||||||
: BindDestr(process_with_kernel(kernel.invalidate(), info)) {}
|
|
||||||
|
|
||||||
#endif
|
|
@ -1 +0,0 @@
|
|||||||
pub mod start_block;
|
|
@ -1,17 +0,0 @@
|
|||||||
use memflow::types::Address;
|
|
||||||
use memflow_win32::kernel;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct StartBlock {
|
|
||||||
pub kernel_hint: Address,
|
|
||||||
pub dtb: Address,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<kernel::StartBlock> for StartBlock {
|
|
||||||
fn from(o: kernel::StartBlock) -> StartBlock {
|
|
||||||
StartBlock {
|
|
||||||
kernel_hint: o.kernel_hint,
|
|
||||||
dtb: o.dtb,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
pub mod kernel;
|
|
||||||
pub mod win32;
|
|
@ -1,331 +0,0 @@
|
|||||||
use memflow_ffi::mem::phys_mem::CloneablePhysicalMemoryObj;
|
|
||||||
use memflow_ffi::util::*;
|
|
||||||
use memflow_win32::kernel::Win32Version;
|
|
||||||
use memflow_win32::win32::{kernel, Win32ProcessInfo, Win32VirtualTranslate};
|
|
||||||
|
|
||||||
use memflow::mem::{
|
|
||||||
cache::{CachedMemoryAccess, CachedVirtualTranslate, TimedCacheValidator},
|
|
||||||
CloneablePhysicalMemory, DirectTranslate, VirtualDMA,
|
|
||||||
};
|
|
||||||
|
|
||||||
use memflow::iter::FnExtend;
|
|
||||||
use memflow::process::PID;
|
|
||||||
use memflow::types::{size, Address, PageType};
|
|
||||||
|
|
||||||
use super::process::Win32Process;
|
|
||||||
use crate::kernel::start_block::StartBlock;
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub(crate) type FFIMemory =
|
|
||||||
CachedMemoryAccess<'static, Box<dyn CloneablePhysicalMemory>, TimedCacheValidator>;
|
|
||||||
pub(crate) type FFIVirtualTranslate = CachedVirtualTranslate<DirectTranslate, TimedCacheValidator>;
|
|
||||||
|
|
||||||
pub(crate) type FFIVirtualMemory =
|
|
||||||
VirtualDMA<FFIMemory, FFIVirtualTranslate, Win32VirtualTranslate>;
|
|
||||||
|
|
||||||
pub type Kernel = kernel::Kernel<FFIMemory, FFIVirtualTranslate>;
|
|
||||||
|
|
||||||
/// Build a cloneable kernel object with default caching parameters
|
|
||||||
///
|
|
||||||
/// This function will take ownership of the input `mem` object.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `mem` must be a heap allocated memory reference, created by one of the API's functions.
|
|
||||||
/// Reference to it becomes invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_build(
|
|
||||||
mem: &'static mut CloneablePhysicalMemoryObj,
|
|
||||||
) -> Option<&'static mut Kernel> {
|
|
||||||
let mem: Box<dyn CloneablePhysicalMemory> = Box::from_raw(*Box::from_raw(mem));
|
|
||||||
kernel::Kernel::builder(mem)
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a cloneable kernel object with custom caching parameters
|
|
||||||
///
|
|
||||||
/// This function will take ownership of the input `mem` object.
|
|
||||||
///
|
|
||||||
/// vat_cache_entries must be positive, or the program will panic upon memory reads or writes.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `mem` must be a heap allocated memory reference, created by one of the API's functions.
|
|
||||||
/// Reference to it becomes invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_build_custom(
|
|
||||||
mem: &'static mut CloneablePhysicalMemoryObj,
|
|
||||||
page_cache_time_ms: u64,
|
|
||||||
page_cache_flags: PageType,
|
|
||||||
page_cache_size_kb: usize,
|
|
||||||
vat_cache_time_ms: u64,
|
|
||||||
vat_cache_entries: usize,
|
|
||||||
) -> Option<&'static mut Kernel> {
|
|
||||||
let mem: Box<dyn CloneablePhysicalMemory> = Box::from_raw(*Box::from_raw(mem));
|
|
||||||
kernel::Kernel::builder(mem)
|
|
||||||
.build_page_cache(move |connector, arch| {
|
|
||||||
CachedMemoryAccess::builder(connector)
|
|
||||||
.arch(arch)
|
|
||||||
.validator(TimedCacheValidator::new(
|
|
||||||
Duration::from_millis(page_cache_time_ms).into(),
|
|
||||||
))
|
|
||||||
.page_type_mask(page_cache_flags)
|
|
||||||
.cache_size(size::kb(page_cache_size_kb))
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.build_vat_cache(move |vat, arch| {
|
|
||||||
CachedVirtualTranslate::builder(vat)
|
|
||||||
.arch(arch)
|
|
||||||
.validator(TimedCacheValidator::new(
|
|
||||||
Duration::from_millis(vat_cache_time_ms).into(),
|
|
||||||
))
|
|
||||||
.entries(vat_cache_entries)
|
|
||||||
.build()
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.build()
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_clone(kernel: &Kernel) -> &'static mut Kernel {
|
|
||||||
Box::leak(Box::new((*kernel).clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a kernel object
|
|
||||||
///
|
|
||||||
/// This will free the input `kernel` object (including the underlying memory object)
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid reference heap allocated by one of the above functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_free(kernel: &'static mut Kernel) {
|
|
||||||
let _ = Box::from_raw(kernel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destroy a kernel object and return its underlying memory object
|
|
||||||
///
|
|
||||||
/// This will free the input `kernel` object, and return the underlying memory object. It will free
|
|
||||||
/// the object from any additional caching that `kernel` had in place.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid reference heap allocated by one of the above functions.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_destroy(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
) -> &'static mut CloneablePhysicalMemoryObj {
|
|
||||||
let kernel = Box::from_raw(kernel);
|
|
||||||
Box::leak(Box::new(Box::leak(kernel.destroy().destroy())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_start_block(kernel: &Kernel) -> StartBlock {
|
|
||||||
kernel.kernel_info.start_block.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_winver(kernel: &Kernel) -> Win32Version {
|
|
||||||
kernel.kernel_info.kernel_winver.mask_build_number()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_winver_unmasked(kernel: &Kernel) -> Win32Version {
|
|
||||||
kernel.kernel_info.kernel_winver
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a list of peorcess addresses
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `buffer` must be a valid buffer of size at least `max_size`
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_eprocess_list(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
buffer: *mut Address,
|
|
||||||
max_size: usize,
|
|
||||||
) -> usize {
|
|
||||||
let mut ret = 0;
|
|
||||||
|
|
||||||
let buffer = std::slice::from_raw_parts_mut(buffer, max_size);
|
|
||||||
|
|
||||||
let mut extend_fn = FnExtend::new(|addr| {
|
|
||||||
if ret < max_size {
|
|
||||||
buffer[ret] = addr;
|
|
||||||
ret += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
kernel
|
|
||||||
.eprocess_list_extend(&mut extend_fn)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(|_| ret)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a list of processes
|
|
||||||
///
|
|
||||||
/// This will fill `buffer` with a list of win32 process information. These processes will need to be
|
|
||||||
/// individually freed with `process_info_free`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `buffer` must be a valid that can contain at least `max_size` references to `Win32ProcessInfo`.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_process_info_list(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
buffer: *mut &'static mut Win32ProcessInfo,
|
|
||||||
max_size: usize,
|
|
||||||
) -> usize {
|
|
||||||
let mut ret = 0;
|
|
||||||
|
|
||||||
let buffer = std::slice::from_raw_parts_mut(buffer, max_size);
|
|
||||||
|
|
||||||
let mut extend_fn = FnExtend::new(|info| {
|
|
||||||
if ret < max_size {
|
|
||||||
buffer[ret] = Box::leak(Box::new(info));
|
|
||||||
ret += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
kernel
|
|
||||||
.process_info_list_extend(&mut extend_fn)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(|_| ret)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process info
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_kernel_process_info(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
) -> Option<&'static mut Win32ProcessInfo> {
|
|
||||||
kernel
|
|
||||||
.kernel_process_info()
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_process_info_from_eprocess(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
eprocess: Address,
|
|
||||||
) -> Option<&'static mut Win32ProcessInfo> {
|
|
||||||
kernel
|
|
||||||
.process_info_from_eprocess(eprocess)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve process information by name
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `name` must be a valid null terminated string
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_process_info(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
name: *const c_char,
|
|
||||||
) -> Option<&'static mut Win32ProcessInfo> {
|
|
||||||
let name = CStr::from_ptr(name).to_string_lossy();
|
|
||||||
kernel
|
|
||||||
.process_info(&name)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn kernel_process_info_pid(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
pid: PID,
|
|
||||||
) -> Option<&'static mut Win32ProcessInfo> {
|
|
||||||
kernel
|
|
||||||
.process_info_pid(pid)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process conversion
|
|
||||||
|
|
||||||
/// Create a process by looking up its name
|
|
||||||
///
|
|
||||||
/// This will consume `kernel` and free it later on.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `name` must be a valid null terminated string
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
/// invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_into_process(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
name: *const c_char,
|
|
||||||
) -> Option<&'static mut Win32Process> {
|
|
||||||
let kernel = Box::from_raw(kernel);
|
|
||||||
let name = CStr::from_ptr(name).to_string_lossy();
|
|
||||||
kernel
|
|
||||||
.into_process(&name)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a process by looking up its PID
|
|
||||||
///
|
|
||||||
/// This will consume `kernel` and free it later on.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
/// invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_into_process_pid(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
pid: PID,
|
|
||||||
) -> Option<&'static mut Win32Process> {
|
|
||||||
let kernel = Box::from_raw(kernel);
|
|
||||||
kernel
|
|
||||||
.into_process_pid(pid)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a kernel process insatance
|
|
||||||
///
|
|
||||||
/// This will consume `kernel` and free it later on.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid reference to `Kernel`. After the function the reference to it becomes
|
|
||||||
/// invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn kernel_into_kernel_process(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
) -> Option<&'static mut Win32Process> {
|
|
||||||
let kernel = Box::from_raw(kernel);
|
|
||||||
kernel
|
|
||||||
.into_kernel_process()
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(to_heap)
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
pub mod kernel;
|
|
||||||
pub mod module;
|
|
||||||
pub mod process;
|
|
||||||
pub mod process_info;
|
|
@ -1,24 +0,0 @@
|
|||||||
use memflow_ffi::process::OsProcessModuleInfoObj;
|
|
||||||
use memflow_ffi::util::to_heap;
|
|
||||||
use memflow_win32::win32::Win32ModuleInfo;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn module_info_trait(
|
|
||||||
info: &'static mut Win32ModuleInfo,
|
|
||||||
) -> &'static mut OsProcessModuleInfoObj {
|
|
||||||
to_heap(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a win32 module info instance.
|
|
||||||
///
|
|
||||||
/// Note that it is not the same as `OsProcessModuleInfoObj`, and those references need to be freed
|
|
||||||
/// manually.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `info` must be a unique heap allocated reference to `Win32ModuleInfo`, and after this call the
|
|
||||||
/// reference will become invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn module_info_free(info: &'static mut Win32ModuleInfo) {
|
|
||||||
let _ = Box::from_raw(info);
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
use super::kernel::{FFIVirtualMemory, Kernel};
|
|
||||||
|
|
||||||
use memflow::iter::FnExtend;
|
|
||||||
use memflow_ffi::mem::virt_mem::VirtualMemoryObj;
|
|
||||||
use memflow_ffi::util::*;
|
|
||||||
use memflow_win32::win32::{self, Win32ModuleInfo, Win32ProcessInfo};
|
|
||||||
|
|
||||||
use std::ffi::CStr;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
|
|
||||||
pub type Win32Process = win32::Win32Process<FFIVirtualMemory>;
|
|
||||||
|
|
||||||
/// Create a process with kernel and process info
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `kernel` must be a valid heap allocated reference to a `Kernel` object. After the function
|
|
||||||
/// call, the reference becomes invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_with_kernel(
|
|
||||||
kernel: &'static mut Kernel,
|
|
||||||
proc_info: &Win32ProcessInfo,
|
|
||||||
) -> &'static mut Win32Process {
|
|
||||||
let kernel = Box::from_raw(kernel);
|
|
||||||
to_heap(Win32Process::with_kernel(*kernel, proc_info.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve refernce to the underlying virtual memory object
|
|
||||||
///
|
|
||||||
/// This will return a static reference to the virtual memory object. It will only be valid as long
|
|
||||||
/// as `process` if valid, and needs to be freed manually using `virt_free` regardless if the
|
|
||||||
/// process if freed or not.
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_virt_mem(
|
|
||||||
process: &'static mut Win32Process,
|
|
||||||
) -> &'static mut VirtualMemoryObj {
|
|
||||||
to_heap(&mut process.virt_mem)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_clone(process: &Win32Process) -> &'static mut Win32Process {
|
|
||||||
to_heap((*process).clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Frees the `process`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `process` must be a valid heap allocated reference to a `Win32Process` object. After the
|
|
||||||
/// function returns, the reference becomes invalid.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_free(process: &'static mut Win32Process) {
|
|
||||||
let _ = Box::from_raw(process);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve a process module list
|
|
||||||
///
|
|
||||||
/// This will fill up to `max_len` elements into `out` with references to `Win32ModuleInfo` objects.
|
|
||||||
///
|
|
||||||
/// These references then need to be freed with `module_info_free`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `out` must be a valid buffer able to contain `max_len` references to `Win32ModuleInfo`.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_module_list(
|
|
||||||
process: &mut Win32Process,
|
|
||||||
out: *mut &'static mut Win32ModuleInfo,
|
|
||||||
max_len: usize,
|
|
||||||
) -> usize {
|
|
||||||
let mut ret = 0;
|
|
||||||
|
|
||||||
let buffer = std::slice::from_raw_parts_mut(out, max_len);
|
|
||||||
|
|
||||||
let mut extend_fn = FnExtend::new(|info| {
|
|
||||||
if ret < max_len {
|
|
||||||
buffer[ret] = to_heap(info);
|
|
||||||
ret += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
process
|
|
||||||
.module_list_extend(&mut extend_fn)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
.map(|_| ret)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the main module of the process
|
|
||||||
///
|
|
||||||
/// This function searches for a module with a base address
|
|
||||||
/// matching the section_base address from the ProcessInfo structure.
|
|
||||||
/// It then returns a reference to a newly allocated
|
|
||||||
/// `Win32ModuleInfo` object, if a module was found (null otherwise).
|
|
||||||
///
|
|
||||||
/// The reference later needs to be freed with `module_info_free`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `process` must be a valid Win32Process pointer.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_main_module_info(
|
|
||||||
process: &mut Win32Process,
|
|
||||||
) -> Option<&'static mut Win32ModuleInfo> {
|
|
||||||
process
|
|
||||||
.main_module_info()
|
|
||||||
.map(to_heap)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lookup a module
|
|
||||||
///
|
|
||||||
/// This will search for a module called `name`, and return a reference to a newly allocated
|
|
||||||
/// `Win32ModuleInfo` object, if a module was found (null otherwise).
|
|
||||||
///
|
|
||||||
/// The reference later needs to be freed with `module_info_free`
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `process` must be a valid Win32Process pointer.
|
|
||||||
/// `name` must be a valid null terminated string.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_module_info(
|
|
||||||
process: &mut Win32Process,
|
|
||||||
name: *const c_char,
|
|
||||||
) -> Option<&'static mut Win32ModuleInfo> {
|
|
||||||
let name = CStr::from_ptr(name).to_string_lossy();
|
|
||||||
|
|
||||||
process
|
|
||||||
.module_info(&name)
|
|
||||||
.map(to_heap)
|
|
||||||
.map_err(inspect_err)
|
|
||||||
.ok()
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
use memflow::types::Address;
|
|
||||||
use memflow_ffi::process::OsProcessInfoObj;
|
|
||||||
use memflow_ffi::util::to_heap;
|
|
||||||
use memflow_win32::win32::{Win32ModuleListInfo, Win32ProcessInfo};
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_trait(
|
|
||||||
info: &'static mut Win32ProcessInfo,
|
|
||||||
) -> &'static mut OsProcessInfoObj {
|
|
||||||
to_heap(info)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_dtb(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.dtb
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_section_base(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.section_base
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_exit_status(info: &Win32ProcessInfo) -> i32 {
|
|
||||||
info.exit_status
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_ethread(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.ethread
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_wow64(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.wow64()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_peb(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.peb()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_peb_native(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.peb_native()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_peb_wow64(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.peb_wow64().unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_teb(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.teb.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_teb_wow64(info: &Win32ProcessInfo) -> Address {
|
|
||||||
info.teb_wow64.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_module_info(info: &Win32ProcessInfo) -> Win32ModuleListInfo {
|
|
||||||
info.module_info()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn process_info_module_info_native(info: &Win32ProcessInfo) -> Win32ModuleListInfo {
|
|
||||||
info.module_info_native()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Free a process information reference
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `info` must be a valid heap allocated reference to a Win32ProcessInfo structure
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn process_info_free(info: &'static mut Win32ProcessInfo) {
|
|
||||||
let _ = Box::from_raw(info);
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
name: Binary build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branch:
|
||||||
|
- 'main'
|
||||||
|
- 'stable'
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
cross-build:
|
||||||
|
name: Publish binary builds
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
target: ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu", "aarch64-unknown-linux-gnu", "armv7-unknown-linux-gnueabihf", "x86_64-pc-windows-gnu"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
- name: Download renamer
|
||||||
|
run: curl -sSf https://raw.githubusercontent.com/memflow/memflowup/master/target_rename.sh > target_rename.sh
|
||||||
|
- name: Build artifacts
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --release --all-features --workspace --verbose --target ${{ matrix.target }}
|
||||||
|
- name: Rename and collect artifacts
|
||||||
|
id: artifacts
|
||||||
|
run: echo "::set-output name=artifact::$(sh ./target_rename.sh "${{ matrix.target }}" | head -n 1)"
|
||||||
|
- name: Upload build artifacts
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
tag_name: bin-${{ github.ref_name }}
|
||||||
|
files: |
|
||||||
|
${{ steps.artifacts.outputs.artifact }}
|
@ -0,0 +1,115 @@
|
|||||||
|
name: Build and test
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
env:
|
||||||
|
CARGO_TERM_COLOR: always
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
flags: [--all-features, --no-default-features]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: cargo build --workspace ${{ matrix.flags }} --verbose
|
||||||
|
|
||||||
|
- name: Build examples
|
||||||
|
run: cargo build --workspace ${{ matrix.flags }} --examples --verbose
|
||||||
|
|
||||||
|
build-cross-targets:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [aarch64-unknown-linux-gnu, armv7-unknown-linux-gnueabihf, i686-unknown-linux-gnu]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --target ${{ matrix.target }} --workspace --all-features --verbose
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Install rust 1.70.0
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
|
||||||
|
- name: Pre-build binaries (for inventory integration tests)
|
||||||
|
run: cargo build --workspace --all-features --verbose
|
||||||
|
|
||||||
|
- name: Run all tests
|
||||||
|
run: cargo test --workspace --all-features --verbose
|
||||||
|
if: runner.os == 'Linux'
|
||||||
|
|
||||||
|
- name: Run all tests
|
||||||
|
run: cargo test --workspace --exclude memflow-derive --all-features --verbose
|
||||||
|
if: runner.os != 'Linux'
|
||||||
|
|
||||||
|
test-cross:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target: [aarch64-unknown-linux-gnu, i686-unknown-linux-gnu]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: nightly # currently required due to -Zdoctest-xcompile
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
override: true
|
||||||
|
- name: Pre-build binaries (for inventory integration tests)
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: build
|
||||||
|
args: --target ${{ matrix.target }} --workspace --all-features --verbose --release
|
||||||
|
- name: Run all tests
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
use-cross: true
|
||||||
|
command: test
|
||||||
|
args: -Zdoctest-xcompile --target ${{ matrix.target }} --workspace --all-features --verbose --release
|
||||||
|
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
toolchain: 1.70.0
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy
|
||||||
|
- run: rustup component add clippy
|
||||||
|
- name: Check formatting
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- uses: actions-rs/clippy-check@v1
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
args: --all-targets --all-features --workspace -- -D clippy::all
|
||||||
|
|
@ -0,0 +1,10 @@
|
|||||||
|
/target
|
||||||
|
**/*.rs.bk
|
||||||
|
*.swp
|
||||||
|
*.so
|
||||||
|
*.dll
|
||||||
|
*.dylib
|
||||||
|
.vscode
|
||||||
|
*.so
|
||||||
|
.vagrant
|
||||||
|
TODO.md
|
@ -1,80 +1,18 @@
|
|||||||
[package]
|
[profile.bench]
|
||||||
name = "memflow-win32"
|
debug = true
|
||||||
version = "0.1.5"
|
|
||||||
authors = ["ko1N <ko1N1337@gmail.com>", "Aurimas Blažulionis <0x60@pm.me>"]
|
|
||||||
edition = "2018"
|
|
||||||
description = "win32 integration of the memflow physical memory introspection framework"
|
|
||||||
documentation = "https://docs.rs/memflow-win32"
|
|
||||||
readme = "README.md"
|
|
||||||
homepage = "https://memflow.github.io"
|
|
||||||
repository = "https://github.com/memflow/memflow"
|
|
||||||
license-file = "../LICENSE"
|
|
||||||
keywords = [ "memflow", "introspection", "memory", "dma" ]
|
|
||||||
categories = [ "api-bindings", "memory-management", "os" ]
|
|
||||||
|
|
||||||
[badges]
|
[workspace]
|
||||||
maintenance = { status = "actively-developed" }
|
resolver = "1"
|
||||||
codecov = { repository = "github", branch = "master", service = "github" }
|
|
||||||
|
|
||||||
[dependencies]
|
members = [
|
||||||
memflow = { version = "0.1", path = "../memflow", default-features = false }
|
"memflow-win32",
|
||||||
log = { version = "0.4", default-features = false }
|
"memflow-win32-defs",
|
||||||
dataview = "0.1"
|
]
|
||||||
pelite = { version = "0.9", default-features = false }
|
|
||||||
widestring = { version = "0.4", default-features = false, features = ["alloc"] }
|
|
||||||
no-std-compat = { version = "0.4", features = ["alloc"] }
|
|
||||||
serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] }
|
|
||||||
|
|
||||||
# will be replaced by our own signature scanner
|
default-members = [
|
||||||
regex = { version = "1", optional = true }
|
"memflow-win32",
|
||||||
|
"memflow-win32-defs",
|
||||||
|
]
|
||||||
|
|
||||||
# symbolstore
|
# [patch.crates-io]
|
||||||
dirs = { version = "2.0", optional = true }
|
# memflow = { path = "../memflow/memflow" }
|
||||||
ureq = { version = "1.2", optional = true }
|
|
||||||
pdb = { version = "0.6", optional = true }
|
|
||||||
pbr = { version = "1.0", optional = true }
|
|
||||||
progress-streams = { version = "1.1", optional = true }
|
|
||||||
|
|
||||||
[dev_dependencies]
|
|
||||||
simple_logger = "1.0"
|
|
||||||
win_key_codes = "0.1"
|
|
||||||
rand = "0.7"
|
|
||||||
rand_xorshift = "0.2"
|
|
||||||
clap = "2.33"
|
|
||||||
toml = "0.5"
|
|
||||||
colored = "2.0"
|
|
||||||
|
|
||||||
[build_dependencies]
|
|
||||||
toml = "0.5"
|
|
||||||
dataview = "0.1"
|
|
||||||
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["std", "serde_derive", "embed_offsets", "symstore", "download_progress", "regex"]
|
|
||||||
std = ["no-std-compat/std", "memflow/std"]
|
|
||||||
embed_offsets = ["serde", "memflow/serde_derive"]
|
|
||||||
collections = []
|
|
||||||
alloc = []
|
|
||||||
serde_derive = ["serde", "memflow/serde_derive", "pelite/std", "pelite/serde"]
|
|
||||||
symstore = ["dirs", "ureq", "pdb"]
|
|
||||||
download_progress = ["pbr", "progress-streams"]
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "dump_offsets"
|
|
||||||
path = "examples/dump_offsets.rs"
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "generate_offsets"
|
|
||||||
path = "examples/generate_offsets.rs"
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "read_keys"
|
|
||||||
path = "examples/read_keys.rs"
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "multithreading"
|
|
||||||
path = "examples/multithreading.rs"
|
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "read_bench"
|
|
||||||
path = "examples/read_bench.rs"
|
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
use dataview::Pod;
|
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
error::Error,
|
|
||||||
fs::{self, File},
|
|
||||||
io::{Read, Write},
|
|
||||||
path::Path,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[path = "src/offsets/offset_table.rs"]
|
|
||||||
#[cfg(feature = "embed_offsets")]
|
|
||||||
mod offset_table;
|
|
||||||
|
|
||||||
#[cfg(feature = "embed_offsets")]
|
|
||||||
use offset_table::Win32OffsetFile;
|
|
||||||
|
|
||||||
#[cfg(feature = "embed_offsets")]
|
|
||||||
fn embed_offsets() -> Result<(), Box<dyn Error>> {
|
|
||||||
let out_dir = env::var("OUT_DIR")?;
|
|
||||||
let dest_path = Path::new(&out_dir).join("win32_offsets.bin");
|
|
||||||
let mut all_the_files = File::create(&dest_path)?;
|
|
||||||
|
|
||||||
// iterate offsets folder
|
|
||||||
for f in fs::read_dir("./offsets")? {
|
|
||||||
let f = f?;
|
|
||||||
|
|
||||||
if !f.file_type()?.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut file = File::open(f.path())?;
|
|
||||||
let mut tomlstr = String::new();
|
|
||||||
file.read_to_string(&mut tomlstr)?;
|
|
||||||
|
|
||||||
let offsets: Win32OffsetFile = toml::from_str(&tomlstr)?;
|
|
||||||
all_the_files.write_all(offsets.as_bytes())?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "embed_offsets"))]
|
|
||||||
fn embed_offsets() -> Result<(), Box<dyn Error>> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
|
||||||
embed_offsets()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
use clap::*;
|
|
||||||
use log::{error, Level};
|
|
||||||
|
|
||||||
use memflow::connector::*;
|
|
||||||
|
|
||||||
use memflow_win32::prelude::{Kernel, Win32OffsetFile};
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let matches = App::new("dump offsets example")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.arg(Arg::with_name("verbose").short("v").multiple(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("connector")
|
|
||||||
.long("connector")
|
|
||||||
.short("c")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("args")
|
|
||||||
.long("args")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(""),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("output")
|
|
||||||
.long("output")
|
|
||||||
.short("o")
|
|
||||||
.takes_value(true),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// set log level
|
|
||||||
let level = match matches.occurrences_of("verbose") {
|
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
|
||||||
2 => Level::Info,
|
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
|
||||||
_ => Level::Trace,
|
|
||||||
};
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(level.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// create inventory + connector
|
|
||||||
let inventory = unsafe { ConnectorInventory::scan() };
|
|
||||||
let connector = unsafe {
|
|
||||||
inventory.create_connector(
|
|
||||||
matches.value_of("connector").unwrap(),
|
|
||||||
&ConnectorArgs::parse(matches.value_of("args").unwrap()).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let kernel = Kernel::builder(connector)
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let winver = kernel.kernel_info.kernel_winver;
|
|
||||||
|
|
||||||
if winver != (0, 0).into() {
|
|
||||||
let offsets = if let Some(guid) = &kernel.kernel_info.kernel_guid {
|
|
||||||
Win32OffsetFile {
|
|
||||||
pdb_file_name: guid.file_name.as_str().into(),
|
|
||||||
pdb_guid: guid.guid.as_str().into(),
|
|
||||||
|
|
||||||
arch: kernel.kernel_info.start_block.arch.into(),
|
|
||||||
|
|
||||||
nt_major_version: winver.major_version(),
|
|
||||||
nt_minor_version: winver.minor_version(),
|
|
||||||
nt_build_number: winver.build_number(),
|
|
||||||
|
|
||||||
offsets: kernel.offsets.into(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Win32OffsetFile {
|
|
||||||
pdb_file_name: Default::default(),
|
|
||||||
pdb_guid: Default::default(),
|
|
||||||
|
|
||||||
arch: kernel.kernel_info.start_block.arch.into(),
|
|
||||||
|
|
||||||
nt_major_version: winver.major_version(),
|
|
||||||
nt_minor_version: winver.minor_version(),
|
|
||||||
nt_build_number: winver.build_number(),
|
|
||||||
|
|
||||||
offsets: kernel.offsets.into(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// write offsets to file
|
|
||||||
let offsetstr = toml::to_string_pretty(&offsets).unwrap();
|
|
||||||
match matches.value_of("output") {
|
|
||||||
Some(output) => {
|
|
||||||
let mut file = File::create(output).unwrap();
|
|
||||||
file.write_all(offsetstr.as_bytes()).unwrap();
|
|
||||||
}
|
|
||||||
None => println!("{}", offsetstr),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error!("kernel version has to be valid in order to generate a offsets file");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,211 +0,0 @@
|
|||||||
use memflow::connector::ConnectorInventory;
|
|
||||||
use memflow::connector::ConnectorArgs;
|
|
||||||
use memflow::mem::*;
|
|
||||||
|
|
||||||
use memflow_win32::error::{Error, Result};
|
|
||||||
use memflow_win32::win32::{Kernel, Win32ModuleInfo, Win32Process};
|
|
||||||
|
|
||||||
use clap::*;
|
|
||||||
use log::Level;
|
|
||||||
|
|
||||||
use colored::*;
|
|
||||||
|
|
||||||
static mut HAD_ERROR: bool = false;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let (connector, args_str) = parse_args();
|
|
||||||
|
|
||||||
let args = ConnectorArgs::parse(&args_str)?;
|
|
||||||
|
|
||||||
// create inventory + connector
|
|
||||||
let inventory = unsafe { ConnectorInventory::scan() };
|
|
||||||
let connector = unsafe { inventory.create_connector(&connector, &args)? };
|
|
||||||
|
|
||||||
let mut kernel = build_kernel(connector)?;
|
|
||||||
|
|
||||||
{
|
|
||||||
println!("Kernel info:");
|
|
||||||
let info = &kernel.kernel_info;
|
|
||||||
let start_block = &info.start_block;
|
|
||||||
println!(
|
|
||||||
"{:#?} ... {}",
|
|
||||||
start_block,
|
|
||||||
some_str(&start_block.dtb.non_null())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"kernel_base: {:x} ... {}",
|
|
||||||
info.kernel_base,
|
|
||||||
some_str(&info.kernel_base.non_null())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"kernel_size: {:x} ... {}",
|
|
||||||
info.kernel_size,
|
|
||||||
bool_str(info.kernel_size != 0)
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"kernel_guid: {:?} ... {}",
|
|
||||||
info.kernel_guid,
|
|
||||||
some_str(&info.kernel_guid)
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"kernel_winver: {:?} ... {}",
|
|
||||||
info.kernel_winver.as_tuple(),
|
|
||||||
bool_str(info.kernel_winver != (0, 0).into())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"eprocess_base: {:x} ... {}",
|
|
||||||
info.eprocess_base,
|
|
||||||
some_str(&info.eprocess_base.non_null())
|
|
||||||
);
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
println!("Kernel Process:");
|
|
||||||
if let Ok(proc_info) = kernel.kernel_process_info() {
|
|
||||||
let mut kernel_proc = Win32Process::with_kernel_ref(&mut kernel, proc_info);
|
|
||||||
let modules = modules(&mut kernel_proc)?;
|
|
||||||
println!("checking module list:");
|
|
||||||
println!(
|
|
||||||
"ntoskrnl.exe ... {}",
|
|
||||||
some_str(
|
|
||||||
&modules
|
|
||||||
.iter()
|
|
||||||
.find(|e| e.name.to_lowercase() == "ntoskrnl.exe")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"hal.dll ... {}",
|
|
||||||
some_str(&modules.iter().find(|e| e.name.to_lowercase() == "hal.dll"))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
println!("{}", bool_str(false));
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
println!("Process List:");
|
|
||||||
let proc_list = kernel.process_info_list()?;
|
|
||||||
let lsass = proc_list
|
|
||||||
.iter()
|
|
||||||
.find(|p| p.name.to_lowercase() == "lsass.exe");
|
|
||||||
println!("lsass.exe ... {}", some_str(&lsass));
|
|
||||||
println!();
|
|
||||||
|
|
||||||
if let Some(proc) = lsass {
|
|
||||||
println!("{} info:", proc.name);
|
|
||||||
println!("pid: {} ... {}", proc.pid, bool_str(proc.pid < 10000));
|
|
||||||
println!("dtb: {} ... {}", proc.dtb, some_str(&proc.dtb.non_null()));
|
|
||||||
println!(
|
|
||||||
"section_base: {} ... {}",
|
|
||||||
proc.section_base,
|
|
||||||
some_str(&proc.section_base.non_null())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"ethread: {} ... {}",
|
|
||||||
proc.ethread,
|
|
||||||
some_str(&proc.ethread.non_null())
|
|
||||||
);
|
|
||||||
println!("teb: {:?} ... {}", proc.teb, bool_str(proc.teb.is_none()));
|
|
||||||
println!(
|
|
||||||
"teb_wow64: {:?} ... {}",
|
|
||||||
proc.teb_wow64,
|
|
||||||
bool_str(proc.teb_wow64.is_none())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"peb_native: {} ... {}",
|
|
||||||
proc.peb_native,
|
|
||||||
some_str(&proc.peb_native.non_null())
|
|
||||||
);
|
|
||||||
println!(
|
|
||||||
"peb_wow64: {:?} ... {}",
|
|
||||||
proc.teb_wow64,
|
|
||||||
bool_str(proc.peb_wow64.is_none())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
if HAD_ERROR {
|
|
||||||
Err(Error::Other(
|
|
||||||
"Some errors encountered, not all functionality may be present!",
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn some_str<T>(r: &Option<T>) -> ColoredString {
|
|
||||||
bool_str(r.is_some())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ok_str<T>(r: &Result<T>) -> ColoredString {
|
|
||||||
bool_str(r.is_ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bool_str(b: bool) -> ColoredString {
|
|
||||||
if b {
|
|
||||||
"ok".green()
|
|
||||||
} else {
|
|
||||||
unsafe { HAD_ERROR = true };
|
|
||||||
"error".red()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modules<V: VirtualMemory>(process: &mut Win32Process<V>) -> Result<Vec<Win32ModuleInfo>> {
|
|
||||||
let modules = process.module_list();
|
|
||||||
println!("modules ... {}", ok_str(&modules));
|
|
||||||
modules
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_kernel<T: PhysicalMemory>(
|
|
||||||
mem: T,
|
|
||||||
) -> Result<Kernel<impl PhysicalMemory, impl VirtualTranslate>> {
|
|
||||||
let kernel = Kernel::builder(mem).build_default_caches().build();
|
|
||||||
println!("Kernel::build ... {}", ok_str(&kernel));
|
|
||||||
println!();
|
|
||||||
kernel
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_args() -> (String, String) {
|
|
||||||
let matches = App::new("read_keys example")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.arg(Arg::with_name("verbose").short("v").multiple(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("connector")
|
|
||||||
.long("connector")
|
|
||||||
.short("c")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("args")
|
|
||||||
.long("args")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(""),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// set log level
|
|
||||||
let level = match matches.occurrences_of("verbose") {
|
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
|
||||||
2 => Level::Info,
|
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
|
||||||
_ => Level::Trace,
|
|
||||||
};
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(level.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(
|
|
||||||
matches.value_of("connector").unwrap().into(),
|
|
||||||
matches.value_of("args").unwrap().into(),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
use std::thread;
|
|
||||||
|
|
||||||
use clap::*;
|
|
||||||
use log::{info, Level};
|
|
||||||
|
|
||||||
use memflow::connector::*;
|
|
||||||
use memflow::mem::*;
|
|
||||||
|
|
||||||
use memflow_win32::win32::Kernel;
|
|
||||||
|
|
||||||
pub fn parallel_init<T: PhysicalMemory + Clone + 'static>(connector: T) {
|
|
||||||
(0..8)
|
|
||||||
.map(|_| connector.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(|c| {
|
|
||||||
thread::spawn(move || {
|
|
||||||
Kernel::builder(c)
|
|
||||||
.no_symbol_store()
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.for_each(|t| t.join().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parallel_kernels<T: PhysicalMemory + Clone + 'static>(connector: T) {
|
|
||||||
let kernel = Kernel::builder(connector).build().unwrap();
|
|
||||||
|
|
||||||
(0..8)
|
|
||||||
.map(|_| kernel.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut k| {
|
|
||||||
thread::spawn(move || {
|
|
||||||
let _eprocesses = k.eprocess_list().unwrap();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.for_each(|t| t.join().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parallel_kernels_cached<T: PhysicalMemory + Clone + 'static>(connector: T) {
|
|
||||||
let kernel = Kernel::builder(connector)
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(0..8)
|
|
||||||
.map(|_| kernel.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut k| {
|
|
||||||
thread::spawn(move || {
|
|
||||||
let eprocesses = k.eprocess_list().unwrap();
|
|
||||||
info!("eprocesses list fetched: {}", eprocesses.len());
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.for_each(|t| t.join().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parallel_processes<T: PhysicalMemory + Clone + 'static>(connector: T) {
|
|
||||||
let kernel = Kernel::builder(connector)
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let process = kernel.into_process("wininit.exe").unwrap();
|
|
||||||
|
|
||||||
(0..8)
|
|
||||||
.map(|_| process.clone())
|
|
||||||
.into_iter()
|
|
||||||
.map(|mut p| {
|
|
||||||
thread::spawn(move || {
|
|
||||||
let module_list = p.module_list().unwrap();
|
|
||||||
info!("wininit.exe module_list: {}", module_list.len());
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.for_each(|t| t.join().unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let matches = App::new("read_keys example")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.arg(Arg::with_name("verbose").short("v").multiple(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("connector")
|
|
||||||
.long("connector")
|
|
||||||
.short("c")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("args")
|
|
||||||
.long("args")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(""),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// set log level
|
|
||||||
let level = match matches.occurrences_of("verbose") {
|
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
|
||||||
2 => Level::Info,
|
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
|
||||||
_ => Level::Trace,
|
|
||||||
};
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(level.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// create inventory + connector
|
|
||||||
let inventory = unsafe { ConnectorInventory::scan() };
|
|
||||||
let connector = unsafe {
|
|
||||||
inventory.create_connector(
|
|
||||||
matches.value_of("connector").unwrap(),
|
|
||||||
&ConnectorArgs::parse(matches.value_of("args").unwrap()).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
println!("test");
|
|
||||||
|
|
||||||
// parallel test functions
|
|
||||||
// see each function's implementation for further details
|
|
||||||
|
|
||||||
parallel_init(connector.clone());
|
|
||||||
|
|
||||||
parallel_kernels(connector.clone());
|
|
||||||
|
|
||||||
parallel_kernels_cached(connector.clone());
|
|
||||||
|
|
||||||
parallel_processes(connector);
|
|
||||||
}
|
|
@ -1,216 +0,0 @@
|
|||||||
use std::io::Write;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use clap::*;
|
|
||||||
use log::Level;
|
|
||||||
|
|
||||||
use memflow::connector::*;
|
|
||||||
use memflow::mem::*;
|
|
||||||
use memflow::process::*;
|
|
||||||
use memflow::types::*;
|
|
||||||
|
|
||||||
use memflow_win32::error::Result;
|
|
||||||
use memflow_win32::offsets::Win32Offsets;
|
|
||||||
use memflow_win32::win32::{Kernel, KernelInfo, Win32ModuleInfo, Win32Process};
|
|
||||||
|
|
||||||
use rand::{Rng, SeedableRng};
|
|
||||||
use rand_xorshift::XorShiftRng as CurRng;
|
|
||||||
|
|
||||||
fn rwtest<T: VirtualMemory>(
|
|
||||||
proc: &mut Win32Process<T>,
|
|
||||||
module: &dyn OsProcessModuleInfo,
|
|
||||||
chunk_sizes: &[usize],
|
|
||||||
chunk_counts: &[usize],
|
|
||||||
read_size: usize,
|
|
||||||
) {
|
|
||||||
let mut rng = CurRng::seed_from_u64(0);
|
|
||||||
|
|
||||||
println!("Performance bench:");
|
|
||||||
print!("{:#7}", "SIZE");
|
|
||||||
|
|
||||||
for i in chunk_counts {
|
|
||||||
print!(", x{:02x} mb/s, x{:02x} calls/s", *i, *i);
|
|
||||||
}
|
|
||||||
|
|
||||||
println!();
|
|
||||||
|
|
||||||
let start = Instant::now();
|
|
||||||
let mut ttdur = Duration::new(0, 0);
|
|
||||||
|
|
||||||
for i in chunk_sizes {
|
|
||||||
print!("0x{:05x}", *i);
|
|
||||||
for o in chunk_counts {
|
|
||||||
let mut done_size = 0_usize;
|
|
||||||
let mut total_dur = Duration::new(0, 0);
|
|
||||||
let mut calls = 0;
|
|
||||||
let mut bufs = vec![(vec![0 as u8; *i], 0); *o];
|
|
||||||
|
|
||||||
let base_addr = rng.gen_range(
|
|
||||||
module.base().as_u64(),
|
|
||||||
module.base().as_u64() + module.size() as u64,
|
|
||||||
);
|
|
||||||
|
|
||||||
while done_size < read_size {
|
|
||||||
for (_, addr) in bufs.iter_mut() {
|
|
||||||
*addr = base_addr + rng.gen_range(0, 0x2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
{
|
|
||||||
let mut batcher = proc.virt_mem.virt_batcher();
|
|
||||||
|
|
||||||
for (buf, addr) in bufs.iter_mut() {
|
|
||||||
batcher.read_raw_into(Address::from(*addr), buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
total_dur += now.elapsed();
|
|
||||||
done_size += *i * *o;
|
|
||||||
calls += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ttdur += total_dur;
|
|
||||||
let total_time = total_dur.as_secs_f64();
|
|
||||||
|
|
||||||
print!(
|
|
||||||
", {:8.2}, {:11.2}",
|
|
||||||
(done_size / 0x0010_0000) as f64 / total_time,
|
|
||||||
calls as f64 / total_time
|
|
||||||
);
|
|
||||||
std::io::stdout().flush().expect("");
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
}
|
|
||||||
|
|
||||||
let total_dur = start.elapsed();
|
|
||||||
println!(
|
|
||||||
"Total bench time: {:.2} {:.2}",
|
|
||||||
total_dur.as_secs_f64(),
|
|
||||||
ttdur.as_secs_f64()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_bench<T: PhysicalMemory + ?Sized, V: VirtualTranslate>(
|
|
||||||
phys_mem: &mut T,
|
|
||||||
vat: &mut V,
|
|
||||||
kernel_info: KernelInfo,
|
|
||||||
) -> Result<()> {
|
|
||||||
let offsets = Win32Offsets::builder().kernel_info(&kernel_info).build()?;
|
|
||||||
let mut kernel = Kernel::new(phys_mem, vat, offsets, kernel_info);
|
|
||||||
|
|
||||||
let proc_list = kernel.process_info_list()?;
|
|
||||||
let mut rng = CurRng::seed_from_u64(rand::thread_rng().gen_range(0, !0u64));
|
|
||||||
loop {
|
|
||||||
let mut prc = Win32Process::with_kernel_ref(
|
|
||||||
&mut kernel,
|
|
||||||
proc_list[rng.gen_range(0, proc_list.len())].clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mod_list: Vec<Win32ModuleInfo> = prc
|
|
||||||
.module_list()?
|
|
||||||
.into_iter()
|
|
||||||
.filter(|module| module.size() > 0x1000)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if !mod_list.is_empty() {
|
|
||||||
let tmod = &mod_list[rng.gen_range(0, mod_list.len())];
|
|
||||||
println!(
|
|
||||||
"Found test module {} ({:x}) in {}",
|
|
||||||
tmod.name(),
|
|
||||||
tmod.size(),
|
|
||||||
prc.proc_info.name(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mem_map = prc.virt_mem.virt_page_map(size::gb(1));
|
|
||||||
|
|
||||||
println!("Memory map (with up to 1GB gaps):");
|
|
||||||
|
|
||||||
for (addr, len) in mem_map {
|
|
||||||
println!("{:x}-{:x}", addr, addr + len);
|
|
||||||
}
|
|
||||||
|
|
||||||
rwtest(
|
|
||||||
&mut prc,
|
|
||||||
tmod,
|
|
||||||
&[0x10000, 0x1000, 0x100, 0x10, 0x8],
|
|
||||||
&[32, 8, 1],
|
|
||||||
0x0010_0000 * 32,
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let matches = App::new("read_keys example")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.arg(Arg::with_name("verbose").short("v").multiple(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("connector")
|
|
||||||
.long("connector")
|
|
||||||
.short("c")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("args")
|
|
||||||
.long("args")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(""),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// set log level
|
|
||||||
let level = match matches.occurrences_of("verbose") {
|
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
|
||||||
2 => Level::Info,
|
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
|
||||||
_ => Level::Trace,
|
|
||||||
};
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(level.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// create inventory + connector
|
|
||||||
let inventory = unsafe { ConnectorInventory::scan() };
|
|
||||||
let mut connector = unsafe {
|
|
||||||
inventory.create_connector(
|
|
||||||
matches.value_of("connector").unwrap(),
|
|
||||||
&ConnectorArgs::parse(matches.value_of("args").unwrap()).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// scan for win32 kernel
|
|
||||||
let kernel_info = KernelInfo::scanner(&mut connector).scan()?;
|
|
||||||
|
|
||||||
let mut vat = DirectTranslate::new();
|
|
||||||
|
|
||||||
println!("Benchmarking uncached reads:");
|
|
||||||
read_bench(&mut connector, &mut vat, kernel_info.clone()).unwrap();
|
|
||||||
|
|
||||||
println!();
|
|
||||||
println!("Benchmarking cached reads:");
|
|
||||||
let mut mem_cached = CachedMemoryAccess::builder(&mut connector)
|
|
||||||
.arch(kernel_info.start_block.arch)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut vat_cached = CachedVirtualTranslate::builder(vat)
|
|
||||||
.arch(kernel_info.start_block.arch)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
read_bench(&mut mem_cached, &mut vat_cached, kernel_info).unwrap();
|
|
||||||
|
|
||||||
println!("TLB Hits {}\nTLB Miss {}", vat_cached.hitc, vat_cached.misc);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
use std::{thread, time};
|
|
||||||
|
|
||||||
use clap::*;
|
|
||||||
use log::Level;
|
|
||||||
|
|
||||||
use memflow::connector::*;
|
|
||||||
|
|
||||||
use memflow_win32::win32::{Kernel, Keyboard};
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let matches = App::new("read_keys example")
|
|
||||||
.version(crate_version!())
|
|
||||||
.author(crate_authors!())
|
|
||||||
.arg(Arg::with_name("verbose").short("v").multiple(true))
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("connector")
|
|
||||||
.long("connector")
|
|
||||||
.short("c")
|
|
||||||
.takes_value(true)
|
|
||||||
.required(true),
|
|
||||||
)
|
|
||||||
.arg(
|
|
||||||
Arg::with_name("args")
|
|
||||||
.long("args")
|
|
||||||
.short("a")
|
|
||||||
.takes_value(true)
|
|
||||||
.default_value(""),
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// set log level
|
|
||||||
let level = match matches.occurrences_of("verbose") {
|
|
||||||
0 => Level::Error,
|
|
||||||
1 => Level::Warn,
|
|
||||||
2 => Level::Info,
|
|
||||||
3 => Level::Debug,
|
|
||||||
4 => Level::Trace,
|
|
||||||
_ => Level::Trace,
|
|
||||||
};
|
|
||||||
simple_logger::SimpleLogger::new()
|
|
||||||
.with_level(level.to_level_filter())
|
|
||||||
.init()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// create inventory + connector
|
|
||||||
let inventory = unsafe { ConnectorInventory::scan() };
|
|
||||||
let connector = unsafe {
|
|
||||||
inventory.create_connector(
|
|
||||||
matches.value_of("connector").unwrap(),
|
|
||||||
&ConnectorArgs::parse(matches.value_of("args").unwrap()).unwrap(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// creating the kernel object
|
|
||||||
let mut kernel = Kernel::builder(connector)
|
|
||||||
.build_default_caches()
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// fetch keyboard state
|
|
||||||
let kbd = Keyboard::try_with(&mut kernel).unwrap();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let kbs = kbd.state_with_kernel(&mut kernel).unwrap();
|
|
||||||
println!("space down: {:?}", kbs.is_down(win_key_codes::VK_SPACE));
|
|
||||||
thread::sleep(time::Duration::from_millis(1000));
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
cargo build --release --all-features
|
||||||
|
|
||||||
|
# install connector to system dir
|
||||||
|
if [ ! -z "$1" ] && [ $1 = "--system" ]; then
|
||||||
|
echo "installing connector system-wide in /usr/local/lib/memflow"
|
||||||
|
if [[ ! -d /usr/local/lib/memflow ]]; then
|
||||||
|
sudo mkdir /usr/local/lib/memflow
|
||||||
|
fi
|
||||||
|
sudo cp target/release/libmemflow_win32.so /usr/local/lib/memflow/libmemflow_win32.7.so
|
||||||
|
fi
|
||||||
|
|
||||||
|
# install connector in user dir
|
||||||
|
echo "installing connector for user in ~/.local/lib/memflow"
|
||||||
|
if [[ ! -d ~/.local/lib/memflow ]]; then
|
||||||
|
mkdir -p ~/.local/lib/memflow
|
||||||
|
fi
|
||||||
|
cp target/release/libmemflow_win32.so ~/.local/lib/memflow/libmemflow_win32.7.so
|
@ -0,0 +1,42 @@
|
|||||||
|
[package]
|
||||||
|
name = "memflow-win32-defs"
|
||||||
|
version = "0.2.0"
|
||||||
|
authors = ["ko1N <ko1N1337@gmail.com>", "Aurimas Blažulionis <0x60@pm.me>"]
|
||||||
|
edition = "2018"
|
||||||
|
description = "static offset templates for "
|
||||||
|
documentation = "https://docs.rs/memflow-win32-defs"
|
||||||
|
readme = "../README.md"
|
||||||
|
homepage = "https://memflow.io"
|
||||||
|
repository = "https://github.com/memflow/memflow-win32"
|
||||||
|
license = "MIT"
|
||||||
|
keywords = [ "memflow", "introspection", "memory", "dma" ]
|
||||||
|
categories = [ "api-bindings", "memory-management", "os" ]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
memflow = { version = "0.2", default-features = false }
|
||||||
|
log = { version = "^0.4.14", default-features = false }
|
||||||
|
no-std-compat = { version = "^0.4.1", features = ["alloc"] }
|
||||||
|
serde = { version = "^1.0.133", default-features = false, optional = true, features = ["derive"] }
|
||||||
|
|
||||||
|
# symbolstore
|
||||||
|
dirs = { version = "^5.0.0", optional = true }
|
||||||
|
ureq = { version = "^2.4.0", optional = true }
|
||||||
|
pdb = { version = "^0.8.0", optional = true }
|
||||||
|
indicatif = { version = "^0.17.2", optional = true }
|
||||||
|
progress-streams = { version = "^1.1.0", optional = true }
|
||||||
|
|
||||||
|
[dev_dependencies]
|
||||||
|
simplelog = "^0.12.0"
|
||||||
|
clap = { version = "^4.0.26", features = ["cargo"] }
|
||||||
|
toml = "^0.8.8"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["symstore", "download_progress"]
|
||||||
|
std = ["no-std-compat/std"]
|
||||||
|
symstore = ["dirs", "ureq", "pdb", "std"]
|
||||||
|
download_progress = ["indicatif", "progress-streams"]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "generate_offsets"
|
||||||
|
path = "examples/generate_offsets.rs"
|
||||||
|
required-features = ["symstore", "serde"]
|
@ -1,22 +1,14 @@
|
|||||||
pub mod ntos;
|
|
||||||
pub mod start_block;
|
|
||||||
pub mod sysproc;
|
|
||||||
|
|
||||||
use std::prelude::v1::*;
|
|
||||||
|
|
||||||
pub use start_block::StartBlock;
|
|
||||||
|
|
||||||
use std::cmp::{Ord, Ordering, PartialEq};
|
use std::cmp::{Ord, Ordering, PartialEq};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
|
#[cfg_attr(feature = "serde", derive(::serde::Serialize))]
|
||||||
pub struct Win32GUID {
|
pub struct Win32Guid {
|
||||||
pub file_name: String,
|
pub file_name: String,
|
||||||
pub guid: String,
|
pub guid: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Win32GUID {
|
impl Win32Guid {
|
||||||
pub fn new(file_name: &str, guid: &str) -> Self {
|
pub fn new(file_name: &str, guid: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
file_name: file_name.to_string(),
|
file_name: file_name.to_string(),
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue