Skip to content

Commit

Permalink
Merge pull request #19 from Sewer56/wine-stuff
Browse files Browse the repository at this point in the history
Enabling Testing and Running dll-syringe on WINE
  • Loading branch information
OpenByteDev authored Apr 2, 2024
2 parents c41bc09 + 9fa3fc3 commit c8762b1
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 65 deletions.
2 changes: 2 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[build]
target = ['i686-pc-windows-msvc','x86_64-pc-windows-msvc']
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
/target
Cargo.lock
.vscode
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"rust-analyzer.checkOnSave.command": "clippy",
"[rust]": {
"editor.formatOnSave": true
},
"rust-analyzer.linkedProjects": [
"Cargo.toml",
"tests/helpers/test_payload/Cargo.toml",
"tests/helpers/test_target/Cargo.toml",
]
}
23 changes: 23 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Test on Windows",
"type": "shell",
"command": "pwsh ./scripts/test.ps1",
"problemMatcher": []
},
{
"label": "Clean",
"type": "shell",
"command": "pwsh ./scripts/clean.ps1",
"problemMatcher": []
},
{
"label": "Test on Wine (See Readme)",
"type": "shell",
"command": "pwsh ./scripts/test-wine.ps1",
"problemMatcher": []
},
]
}
42 changes: 36 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ syringe.eject(injected_payload).unwrap();
## Remote Procedure Calls (RPC)
This crate supports two mechanisms for rpc. Both only work one-way for calling exported functions in the target process and are only intended for one-time initialization usage. For extended communication a dedicated rpc library should be used.

| | `RemotePayloadProcedure` | `RemoteRawProcedure` |
| ---------------- | ------------------------------ | ------------------------------------------ |
| Feature | `rpc-payload` | `rpc-raw` |
| Argument and Return Requirements | `Serialize + DeserializeOwned` | `Copy`, Argument size has to be smaller than `usize` in target process |
| Function Definition | Using macro `payload_procedure!` | Any `extern "system"` or `extern "C"` with `#[no_mangle]` |
| | `RemotePayloadProcedure` | `RemoteRawProcedure` |
| -------------------------------- | -------------------------------- | ---------------------------------------------------------------------- |
| Feature | `rpc-payload` | `rpc-raw` |
| Argument and Return Requirements | `Serialize + DeserializeOwned` | `Copy`, Argument size has to be smaller than `usize` in target process |
| Function Definition | Using macro `payload_procedure!` | Any `extern "system"` or `extern "C"` with `#[no_mangle]` |

### RemotePayloadProcedure
A rpc mechanism based on [`bincode`](https://crates.io/crates/bincode).
Expand Down Expand Up @@ -119,6 +119,36 @@ syringe.eject(injected_payload).unwrap();
## License
Licensed under MIT license ([LICENSE](https://github.com/OpenByteDev/dll-syringe/blob/master/LICENSE) or http://opensource.org/licenses/MIT)

## Instructions for Contributors

### Prerequisites

You will need the nightly toolchains of Rust and Cargo to build/test this project.

```
rustup target add x86_64-pc-windows-msvc --toolchain nightly
rustup target add i686-pc-windows-msvc --toolchain nightly
```

> [!NOTE]
> Also applies to developing on Linux, you'll need it for your IDE (i.e. rust-analyzer or RustRover) to work properly.
### Run Tests

Run the `./scripts/test.ps1` script from PowerShell.

### Running Tests on Linux

You'll need `cargo xwin` to build the MSVC targets on Linux:

```
cargo install cargo-xwin
```

After that, you can run the tests with `./scripts/test-wine.ps1` PowerShell script.
(As opposed to `./scripts/test.ps1`)

Make sure you have Wine installed!

## Attribution
Inspired by [Reloaded.Injector](https://github.com/Reloaded-Project/Reloaded.Injector) from [Sewer](https://github.com/Sewer56).

9 changes: 0 additions & 9 deletions clean.ps1

This file was deleted.

2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"
11 changes: 11 additions & 0 deletions scripts/clean.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Navigate up one folder from the current script location
Set-Location "$PSScriptRoot\.."
cargo clean

Set-Location "./tests/helpers/test_payload"
cargo clean
Set-Location "../../.."

Set-Location "./tests/helpers/test_target"
cargo clean
Set-Location "../../.."
17 changes: 17 additions & 0 deletions scripts/test-wine.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Navigate up one folder from the current script location
Set-Location "$PSScriptRoot\.."

# Testing
$env:CROSS_SYSROOT = "." # pretend we cross

# Prebuild dummy projects.
cargo +nightly xwin rustc --manifest-path "tests/helpers/test_target/Cargo.toml" --target i686-pc-windows-msvc --xwin-arch x86 --xwin-cache-dir "target/cache/x86"
cargo +nightly xwin rustc --manifest-path "tests/helpers/test_payload/Cargo.toml" --target i686-pc-windows-msvc --xwin-arch x86 --xwin-cache-dir "target/cache/x86"
cargo +nightly xwin rustc --manifest-path "tests/helpers/test_target/Cargo.toml" --target x86_64-pc-windows-msvc --xwin-arch x86_64 --xwin-cache-dir "target/cache/x64"
cargo +nightly xwin rustc --manifest-path "tests/helpers/test_payload/Cargo.toml" --target x86_64-pc-windows-msvc --xwin-arch x86_64 --xwin-cache-dir "target/cache/x64"

# Windows/MSVC x86
cargo +nightly xwin test --target i686-pc-windows-msvc --xwin-arch x86 --xwin-cache-dir "target/cache/x86"

# Windows/MSVC x64
cargo +nightly xwin test --target x86_64-pc-windows-msvc --xwin-arch x86_64 --xwin-cache-dir "target/cache/x64"
8 changes: 8 additions & 0 deletions scripts/test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Navigate up one folder from the current script location
Set-Location "$PSScriptRoot\.."

# Windows/MSVC x86
cargo test --target i686-pc-windows-msvc -- --nocapture

# Windows/MSVC x64
cargo test --target x86_64-pc-windows-msvc -- --nocapture
6 changes: 6 additions & 0 deletions src/process/memory/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,12 @@ impl<'a> ProcessMemorySlice<'a> {
}

let mut bytes_written = 0;
if buf.is_empty() {
// This works around a discrepancy between Wine and actual Windows.
// On Wine, a 0 sized write fails, on Windows this suceeds. Will file as bug soon.
return Ok(());
}

let result = unsafe {
WriteProcessMemory(
self.process.as_raw_handle(),
Expand Down
2 changes: 0 additions & 2 deletions test.ps1

This file was deleted.

92 changes: 46 additions & 46 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,28 @@
use std::{
env::{current_dir, var},
error::Error,
fs::{canonicalize, remove_file, File},
io::{copy, ErrorKind},
path::PathBuf,
process::{Command, Stdio},
str::FromStr,
sync::Mutex,
};

pub fn build_test_payload_x86() -> Result<PathBuf, Box<dyn Error>> {
build_helper_crate(
"test_payload",
Some(&find_x86_variant_of_target()),
false,
"dll",
)
build_helper_crate("test_payload", &find_x86_variant_of_target(), false, "dll")
}

pub fn build_test_target_x86() -> Result<PathBuf, Box<dyn Error>> {
build_helper_crate(
"test_target",
Some(&find_x86_variant_of_target()),
false,
"exe",
)
build_helper_crate("test_target", &find_x86_variant_of_target(), false, "exe")
}

pub fn build_test_payload_x64() -> Result<PathBuf, Box<dyn Error>> {
build_helper_crate(
"test_payload",
Some(&find_x64_variant_of_target()),
false,
"dll",
)
build_helper_crate("test_payload", &find_x64_variant_of_target(), false, "dll")
}

pub fn build_test_target_x64() -> Result<PathBuf, Box<dyn Error>> {
build_helper_crate(
"test_target",
Some(&find_x64_variant_of_target()),
false,
"exe",
)
build_helper_crate("test_target", &find_x64_variant_of_target(), false, "exe")
}

fn find_x64_variant_of_target() -> String {
Expand All @@ -51,45 +35,61 @@ fn find_x86_variant_of_target() -> String {

pub fn build_helper_crate(
crate_name: &str,
target: Option<&str>,
target: &str,
release: bool,
ext: &str,
) -> Result<PathBuf, Box<dyn Error>> {
let payload_crate_path = PathBuf::from_str(".\\tests\\helpers")?
.join(crate_name)
.canonicalize()?;

let mut command = Command::new("cargo");
command
.arg("build")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());
if let Some(target) = target {
command.arg("--target").arg(target);
// For cross/wine testing, we precompile in external script.
if !is_cross() {
let mut command = Command::new("cargo");
command
.arg("build")
.arg("--target")
.arg(target)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null());

let exit_code = command.current_dir(&payload_crate_path).spawn()?.wait()?;
assert!(
exit_code.success(),
"Failed to build helper crate {} for target {}",
crate_name,
target
);
}
let exit_code = command.current_dir(&payload_crate_path).spawn()?.wait()?;
assert!(
exit_code.success(),
"Failed to build helper crate {} for target {}",
crate_name,
target.unwrap_or("default")
);

let mut payload_artifact_path = payload_crate_path;
payload_artifact_path.push("target");

if let Some(target) = target {
payload_artifact_path.push(target);
}

payload_artifact_path.push(target);
payload_artifact_path.push(if release { "release" } else { "debug" });
payload_artifact_path.push(format!("{crate_name}.{ext}"));
assert!(&payload_artifact_path.exists());
assert!(
&payload_artifact_path.exists(),
"Artifact doesn't exist! {:?}",
&payload_artifact_path
);

Ok(payload_artifact_path)
}

/// Detects cross-rs.
///
/// Remarks:
///
/// I wish I could install Rust itself via `Rustup` here, but the Ubuntu image that ships with
/// `cross` doesn't have the right packages to support encryption, thus we can't download toolchains
/// (I tried). And I also didn't have good luck with pre-build step and downloading extra packages.
///
/// So as a compromise, we build the test binaries outside for testing from Linux.
fn is_cross() -> bool {
var("CROSS_SYSROOT").is_ok()
}

#[macro_export]
macro_rules! syringe_test {
(fn $test_name:ident ($process:ident : OwnedProcess, $payload_path:ident : &Path $(,)?) $body:block) => {
Expand Down
41 changes: 40 additions & 1 deletion tests/process.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use core::mem::zeroed;
use dll_syringe::process::{BorrowedProcess, OwnedProcess, Process};
use std::{fs, mem, time::Duration};
use std::{ffi::CString, fs, mem, mem::size_of, time::Duration};
use winapi::um::{
libloaderapi::{GetProcAddress, LoadLibraryA},
winnt::OSVERSIONINFOW,
};

#[allow(unused)]
mod common;
Expand Down Expand Up @@ -91,6 +96,11 @@ process_test! {
fn long_process_paths_are_supported(
process: OwnedProcess
) {
if is_running_under_wine() || is_older_than_windows_10() {
println!("Test skipped due to running under an environment with unsupported long paths. (Wine or older than Windows 10).");
return;
}

let process_path = process.path().unwrap();
process.kill().unwrap();

Expand Down Expand Up @@ -138,3 +148,32 @@ fn current_pseudo_process_eq_current_process() {
assert_eq!(pseudo.try_to_owned().unwrap(), normal);
assert_eq!(pseudo, normal.try_clone().unwrap());
}

fn is_running_under_wine() -> bool {
unsafe {
let ntdll = CString::new("ntdll.dll").unwrap();
let lib = LoadLibraryA(ntdll.as_ptr());
if !lib.is_null() {
let func_name = CString::new("wine_get_version").unwrap();
let func = GetProcAddress(lib, func_name.as_ptr());
!func.is_null()
} else {
false
}
}
}

// winapi crate doesn't have this.
// This is in ntdll, so already loaded for every Windows process.
extern "system" {
fn RtlGetVersion(lpVersionInformation: &mut OSVERSIONINFOW) -> u32;
}

fn is_older_than_windows_10() -> bool {
unsafe {
let mut os_info: OSVERSIONINFOW = zeroed();
os_info.dwOSVersionInfoSize = size_of::<OSVERSIONINFOW>() as u32;
RtlGetVersion(&mut os_info);
os_info.dwMajorVersion < 10
}
}

0 comments on commit c8762b1

Please sign in to comment.