diff --git a/.github/workflows/generate-templates.yml b/.github/workflows/generate-templates.yml deleted file mode 100644 index 19e13a3e..00000000 --- a/.github/workflows/generate-templates.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Generate Templates - -on: - push: - branches: - - main -jobs: - generate-templates: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - - name: Generate templates - env: - OUTPUT_DIRECTORY: ${{ github.workspace }}/api/generated - TEMPLATES_DIRECTORY: ${{ github.workspace }}/api/templates - run: cargo build --package markers - - - name: Commit and push - uses: EndBug/add-and-commit@v9 - with: - default_author: github_actions - add: "api/generated" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/validate-examples.yml b/.github/workflows/validate-examples.yml index d8101a72..fd7eb2b9 100644 --- a/.github/workflows/validate-examples.yml +++ b/.github/workflows/validate-examples.yml @@ -9,24 +9,8 @@ on: - main types: [opened, synchronize] jobs: - discover: - runs-on: ubuntu-latest - outputs: - examples: ${{ steps.discover.outputs.examples }} - steps: - - uses: actions/checkout@v4 - - - id: discover - run: | - examples=$(find examples -mindepth 2 -maxdepth 2 -type d | jq -R -s -c 'split("\n") | map(select(length > 0))') - echo "examples=$examples" >> $GITHUB_OUTPUT - validate: - needs: [discover] runs-on: ubuntu-latest - strategy: - matrix: - example: ${{ fromJson(needs.discover.outputs.examples) }} steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable @@ -44,45 +28,14 @@ jobs: sudo apt-get update sudo apt-get install -y mingw-w64 wine wine32 wine64 - - name: Generate templates - env: - OUTPUT_DIRECTORY: ${{ github.workspace }}/api/generated - TEMPLATES_DIRECTORY: ${{ github.workspace }}/api/templates - run: cargo build --package markers - - name: Build for Windows run: cargo build --bin obfuscator --release --target x86_64-pc-windows-gnu - - name: Build examples - run: | - language=$(echo "${{ matrix.example }}" | cut -d/ -f2) - case $language in - rust) cargo build --manifest-path ${{ matrix.example }}/Cargo.toml --release --target x86_64-pc-windows-gnu ;; - cpp) x86_64-w64-mingw32-g++ ${{ matrix.example }}/main.cpp -o ${{ matrix.example }}/example.exe -static-libgcc -static-libstdc++ ;; - esac + - name: Build example + run: x86_64-w64-mingw32-g++ examples/cpp/hello/main.cpp -o example.exe - - name: Obfuscate examples - run: | - language=$(echo "${{ matrix.example }}" | cut -d/ -f2) - case $language in - rust) - name=$(cargo metadata --manifest-path ${{ matrix.example }}/Cargo.toml --no-deps --format-version 1 | jq -r '.packages[0].name') - wine target/x86_64-pc-windows-gnu/release/obfuscator.exe ${{ matrix.example }}/target/x86_64-pc-windows-gnu/release/$name.exe --virtualization - ;; - cpp) - wine target/x86_64-pc-windows-gnu/release/obfuscator.exe ${{ matrix.example }}/example.exe --virtualization - ;; - esac + - name: Obfuscate example + run: wine target/x86_64-pc-windows-gnu/release/obfuscator.exe example.exe --virtualization - - name: Validate examples - run: | - language=$(echo "${{ matrix.example }}" | cut -d/ -f2) - case $language in - rust) - name=$(cargo metadata --manifest-path ${{ matrix.example }}/Cargo.toml --no-deps --format-version 1 | jq -r '.packages[0].name') - wine ${{ matrix.example }}/target/x86_64-pc-windows-gnu/release/$name.exe - ;; - cpp) - wine ${{ matrix.example }}/example.exe - ;; - esac + - name: Validate example + run: wine example.exe diff --git a/Cargo.toml b/Cargo.toml index 6f440681..605c06a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,4 @@ [workspace] -members = ["logger", "obfuscator", "runtime", "tests", "markers"] +members = ["logger", "obfuscator", "runtime", "tests"] resolver = "2" exclude = ["examples"] diff --git a/README.md b/README.md index 4d361276..80de41f8 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Obfuscator for compiled 64-bit portable executables. ### 1. Obfuscator #### Analysis -- **Disassembly**: the binary's code section is disassembled and partitioned into basic blocks by tracing all reachable control flow or by scanning for explicit inline markers as shown in the [examples](https://github.com/RadonCoding/binsafe/tree/main/examples). +- **Disassembly**: the binary's code section is disassembled and partitioned into basic blocks by tracing all reachable control flow or by scanning the `.binsafe` section that [marked functions](https://github.com/RadonCoding/binsafe/tree/main/examples) are placed in. #### Virtualization - **Lifting**: instructions are translated into bytecode that the runtime interprets. diff --git a/api/binsafe.hpp b/api/binsafe.hpp new file mode 100644 index 00000000..a8130835 --- /dev/null +++ b/api/binsafe.hpp @@ -0,0 +1,9 @@ +#pragma once + +#if defined(__GNUC__) || defined(__clang__) +#define BINSAFE __attribute__((section(".binsafe"), noinline)) +#elif defined(_MSC_VER) +#define BINSAFE __declspec(code_seg(".binsafe")) __declspec(noinline) +#else +#define BINSAFE +#endif diff --git a/api/binsafe.rs b/api/binsafe.rs new file mode 100644 index 00000000..49652dc5 --- /dev/null +++ b/api/binsafe.rs @@ -0,0 +1,8 @@ +#[macro_export] +macro_rules! binsafe { + ($item:item) => { + #[link_section = ".binsafe"] + #[inline(never)] + $item + }; +} diff --git a/api/generated/binsafe.hpp b/api/generated/binsafe.hpp deleted file mode 100644 index 96b4fd6e..00000000 --- a/api/generated/binsafe.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#define BINSAFE_BEGIN() \ - __label__ _l##__LINE__; \ - __asm__ volatile(".byte 0xEB, 0x08, 0x42, 0x49, 0x4E, 0x53, 0x41, 0x46, 0x45, 0x01" ::: "memory"); \ - __asm__ goto("" :::: _l##__LINE__) - -#define BINSAFE_END() \ - _l##__LINE__: \ - __asm__ volatile(".byte 0xEB, 0x08, 0x42, 0x49, 0x4E, 0x53, 0x41, 0x46, 0x45, 0x02" ::: "memory") - \ No newline at end of file diff --git a/api/generated/binsafe.rs b/api/generated/binsafe.rs deleted file mode 100644 index 6f091a4b..00000000 --- a/api/generated/binsafe.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[macro_export] -macro_rules! binsafe_begin { - () => { - unsafe { - ::std::arch::asm!(".byte 0xEB, 0x08, 0x42, 0x49, 0x4E, 0x53, 0x41, 0x46, 0x45, 0x01", options(nostack, preserves_flags)); - } - }; -} - -#[macro_export] -macro_rules! binsafe_end { - () => { - unsafe { - ::std::arch::asm!(".byte 0xEB, 0x08, 0x42, 0x49, 0x4E, 0x53, 0x41, 0x46, 0x45, 0x02", options(nostack, preserves_flags)); - } - }; -} diff --git a/api/templates/binsafe.hpp b/api/templates/binsafe.hpp deleted file mode 100644 index 5617c925..00000000 --- a/api/templates/binsafe.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#define BINSAFE_BEGIN() \ - __label__ _l##__LINE__; \ - __asm__ volatile(".byte {begin}" ::: "memory"); \ - __asm__ goto("" :::: _l##__LINE__) - -#define BINSAFE_END() \ - _l##__LINE__: \ - __asm__ volatile(".byte {end}" ::: "memory") - \ No newline at end of file diff --git a/api/templates/binsafe.rs b/api/templates/binsafe.rs deleted file mode 100644 index d3b738bd..00000000 --- a/api/templates/binsafe.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[macro_export] -macro_rules! binsafe_begin { - () => { - unsafe { - ::std::arch::asm!(".byte {begin}", options(nostack, preserves_flags)); - } - }; -} - -#[macro_export] -macro_rules! binsafe_end { - () => { - unsafe { - ::std::arch::asm!(".byte {end}", options(nostack, preserves_flags)); - } - }; -} diff --git a/example.ps1 b/example.ps1 index a3397a24..f0b6972b 100644 --- a/example.ps1 +++ b/example.ps1 @@ -1,50 +1,103 @@ +using namespace System.IO + param( - [Parameter(Mandatory)] - [string]$Example -) + [Parameter(Mandatory, Position = 0)] + [DirectoryInfo]$Example, -$ErrorActionPreference = "Stop" + [Parameter(Position = 1)] + [ValidateSet("debug", "release")] + [string]$Configuration = "debug", -$language = $Example.Split("/")[1] + [Parameter(Position = 2)] + [string[]]$Arguments +) -$env:OUTPUT_DIRECTORY = "$PWD/api/generated" -$env:TEMPLATES_DIRECTORY = "$PWD/api/templates" +$ErrorActionPreference = "Stop" -cargo build --package markers +$language = Split-Path (Split-Path $Example -Parent) -Leaf -if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - -cargo build --bin obfuscator +if ($Configuration -eq "release") { + cargo build --bin obfuscator --release $Arguments +} +else { + cargo build --bin obfuscator $Arguments +} if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } switch ($language) { "rust" { - cargo build --manifest-path "$Example/Cargo.toml" + $manifest = Join-Path $Example.FullName "Cargo.toml" + + if ($Configuration -eq "release") { + cargo build --manifest-path $manifest --release + } + else { + cargo build --manifest-path $manifest + } if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - $metadata = cargo metadata --manifest-path "$Example/Cargo.toml" --format-version 1 --no-deps | ConvertFrom-Json + $metadata = cargo metadata ` + --manifest-path $manifest ` + --format-version 1 ` + --no-deps | ConvertFrom-Json + + $manifest = [Path]::GetFullPath($manifest) - $package = $metadata.packages[0] - $target = $package.targets | Where-Object { $_.kind -contains "bin" } | Select-Object -First 1 + $package = $metadata.packages | + Where-Object { + [Path]::GetFullPath($_.manifest_path) -eq $manifest + } | + Select-Object -First 1 - $source = Join-Path $metadata.target_directory "debug\$($target.name).exe" + $target = $package.targets | + Where-Object { $_.kind -contains "bin" } | + Select-Object -First 1 + + $source = Join-Path ` + $metadata.target_directory ` + "$Configuration\$($target.name).exe" } + "cpp" { - g++ "$Example/main.cpp" - + $main = Join-Path $Example.FullName "main.cpp" + + $source = Join-Path ` + $Example.Parent.Parent.FullName ` + "$($Example.Name).exe" + + if ($Configuration -eq "release") { + g++ $main -o $source -O2 + } + else { + g++ $main -o $source -O0 -g + } + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + } - $source = Join-Path $PWD "a.exe" + default { + throw "Unsupported language: $language" } } -& "target/debug/obfuscator.exe" --virtualization $source +$source = [Path]::GetFullPath($source) + +if (-not (Test-Path -LiteralPath $source -PathType Leaf)) { + throw "Source executable not found: $source" +} + +$obfuscator = Join-Path $PWD "target\$Configuration\obfuscator.exe" + +& $obfuscator --virtualization -v $source if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } -$destination = [System.IO.Path]::ChangeExtension($source, ".protected.exe") +$destination = [Path]::ChangeExtension( + $source, + ".protected.exe" +) & $destination @@ -53,4 +106,4 @@ $code = $LASTEXITCODE Remove-Item $source -Force -ErrorAction SilentlyContinue Remove-Item $destination -Force -ErrorAction SilentlyContinue -exit $code \ No newline at end of file +exit $code diff --git a/examples/cpp/bench/main.cpp b/examples/cpp/bench/main.cpp new file mode 100644 index 00000000..cb387215 --- /dev/null +++ b/examples/cpp/bench/main.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include "../../../api/binsafe.hpp" + +#define ENCIPHER(data, key, delta) \ + do \ + { \ + uint32_t v0 = (data)[0]; \ + uint32_t v1 = (data)[1]; \ + \ + for (int i = 0; i < 10000; ++i) \ + { \ + uint32_t sum = 0; \ + \ + for (uint32_t r = 0; r < 32; r++) \ + { \ + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + (key)[sum & 3]); \ + sum += (delta); \ + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + (key)[(sum >> 11) & 3]); \ + } \ + } \ + \ + (data)[0] = v0; \ + (data)[1] = v1; \ + } while (0) + +BINSAFE void encipher(uint32_t data[2], uint32_t const key[4], uint32_t delta) +{ + ENCIPHER(data, key, delta); +} + +int main() +{ + constexpr uint32_t key[4] = {0xA3B1C2D3, 0xE4F50617, 0x28394A5B, 0x6C7D8E9F}; + constexpr uint32_t delta = 0x9E3779B9; + + uint32_t data_native[2] = {0x12345678, 0x9ABCDEF0}; + + std::chrono::high_resolution_clock::time_point start_native = std::chrono::high_resolution_clock::now(); + + ENCIPHER(data_native, key, delta); + + std::chrono::high_resolution_clock::time_point end_native = std::chrono::high_resolution_clock::now(); + + std::chrono::duration elapsed_native = end_native - start_native; + + uint32_t data_virtualized[2] = {0x12345678, 0x9ABCDEF0}; + + std::chrono::high_resolution_clock::time_point start_virtualized = std::chrono::high_resolution_clock::now(); + + encipher(data_virtualized, key, delta); + + std::chrono::high_resolution_clock::time_point end_virtualized = std::chrono::high_resolution_clock::now(); + + std::chrono::duration elapsed_virtualized = end_virtualized - start_virtualized; + + assert(data_native[0] == data_virtualized[0]); + assert(data_native[1] == data_virtualized[1]); + + std::cout << "Native: " << elapsed_native.count() << " ms" << std::endl; + std::cout << "Virtualized: " << elapsed_virtualized.count() << " ms" << std::endl; + std::cout << "Overhead: " << (elapsed_virtualized.count() / elapsed_native.count()) << "x" << std::endl; + + return 0; +} diff --git a/examples/cpp/crack-me/main.cpp b/examples/cpp/crack-me/main.cpp new file mode 100644 index 00000000..cda659ca --- /dev/null +++ b/examples/cpp/crack-me/main.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include "../../../api/binsafe.hpp" + +enum class Operation : uint8_t +{ + Add, + Sub, + Xor +}; + +struct Constraint +{ + uint8_t lhs; + uint8_t rhs; + Operation operation; + uint8_t value; +}; + +template +constexpr auto create(const char (&key)[N]) +{ + constexpr size_t length = N - 1; + + std::array result{}; + + size_t out = 0; + + for (size_t i = 0; i < length - 1; ++i) + { + const uint8_t a = static_cast(key[i]); + const uint8_t b = static_cast(key[i + 1]); + + result[out++] = { + static_cast(i), + static_cast(i + 1), + Operation::Add, + static_cast(a + b)}; + + result[out++] = { + static_cast(i), + static_cast(i + 1), + Operation::Xor, + static_cast(a ^ b)}; + } + + return result; +} + +template +BINSAFE bool validate(char const *serial) +{ + if constexpr (i == constraints.size()) + { + return true; + } + else + { + const Constraint &constraint = constraints[i]; + + const uint8_t lhs = + static_cast(serial[constraint.lhs]); + + const uint8_t rhs = + static_cast(serial[constraint.rhs]); + + bool valid = false; + + switch (constraint.operation) + { + case Operation::Add: + valid = static_cast(lhs + rhs) == constraint.value; + break; + + case Operation::Sub: + valid = static_cast(lhs - rhs) == constraint.value; + break; + + case Operation::Xor: + valid = static_cast(lhs ^ rhs) == constraint.value; + break; + } + + return valid && validate(serial); + } +} + +constexpr auto constraints = + create("0xP4553D17501P455"); + +BINSAFE bool validate(char const *serial) +{ + constexpr size_t length = (constraints.size() / 2) + 1; + + for (size_t i = 0; i < length; ++i) + { + if (serial[i] == '\0') + return false; + } + + if (serial[length] != '\0') + return false; + + return validate<0, constraints>(serial); +} + +BINSAFE int main(int argc, char *argv[]) +{ + if (argc < 2) + { + std::cout << "Usage: crack-me \n"; + return 1; + } + + if (validate(argv[1])) + { + std::cout << "Access granted.\n"; + return 0; + } + + std::cout << "Access denied.\n"; + return 0; +} \ No newline at end of file diff --git a/examples/cpp/exception-cxx/main.cpp b/examples/cpp/exception-cxx/main.cpp new file mode 100644 index 00000000..f6b0d7e0 --- /dev/null +++ b/examples/cpp/exception-cxx/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include "../../../api/binsafe.hpp" + +BINSAFE uint32_t guarded(uint32_t value) +{ + try + { + throw value; + } + catch (uint32_t caught) + { + return caught; + } + + return 0; +} + +int main() +{ + constexpr uint32_t value = 0x12345678; + + uint32_t a = 0; + uint32_t b = 0; + + try + { + throw value; + } + catch (uint32_t caught) + { + a = caught; + } + + b = guarded(value); + + assert(a == b); + + return 0; +} diff --git a/examples/cpp/hello/main.cpp b/examples/cpp/hello/main.cpp index 2de2d73c..8eff4bc0 100644 --- a/examples/cpp/hello/main.cpp +++ b/examples/cpp/hello/main.cpp @@ -1,10 +1,13 @@ #include -#include "../../../api/generated/binsafe.hpp" +#include "../../../api/binsafe.hpp" -int main() +BINSAFE void hello() { - BINSAFE_BEGIN(); std::cout << "Hello, world!" << std::endl; +} + +int main() +{ + hello(); return 0; - BINSAFE_END(); -} \ No newline at end of file +} diff --git a/examples/cpp/multithread/main.cpp b/examples/cpp/multithread/main.cpp new file mode 100644 index 00000000..a213b627 --- /dev/null +++ b/examples/cpp/multithread/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include "../../../api/binsafe.hpp" + +BINSAFE uint32_t echo(uint32_t input) +{ + uint32_t output = input; + + return output; +} + +int main() +{ + constexpr int thread_count = 8; + + constexpr uint32_t value = 0x12345678; + + uint32_t results[thread_count] = {}; + + std::thread threads[thread_count]; + + for (int i = 0; i < thread_count; ++i) + { + threads[i] = std::thread([&, i]() + { results[i] = echo(value); }); + } + + for (int i = 0; i < thread_count; ++i) + { + threads[i].join(); + } + + for (int i = 0; i < thread_count; ++i) + { + assert(results[i] == value); + } + + return 0; +} diff --git a/examples/rust/hello/Cargo.toml b/examples/rust/hello/Cargo.toml index f6f3649e..fbba8f5c 100644 --- a/examples/rust/hello/Cargo.toml +++ b/examples/rust/hello/Cargo.toml @@ -1,3 +1,5 @@ +[workspace] + [package] name = "hello" version = "0.1.0" diff --git a/examples/rust/hello/src/main.rs b/examples/rust/hello/src/main.rs index 055d0495..ee5a3574 100644 --- a/examples/rust/hello/src/main.rs +++ b/examples/rust/hello/src/main.rs @@ -1,10 +1,14 @@ include!(concat!( env!("CARGO_MANIFEST_DIR"), - "/../../../api/generated/binsafe.rs" + "/../../../api/binsafe.rs" )); +binsafe! { + fn hello() { + println!("Hello, world!"); + } +} + fn main() { - binsafe_begin!(); - println!("Hello, world!"); - binsafe_end!(); + hello(); } diff --git a/markers/Cargo.toml b/markers/Cargo.toml deleted file mode 100644 index 7b443ecc..00000000 --- a/markers/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[package] -name = "markers" -version = "0.1.0" -edition = "2024" - -[dependencies] diff --git a/markers/build.rs b/markers/build.rs deleted file mode 100644 index 4d396b5b..00000000 --- a/markers/build.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::{env, fs, path::PathBuf}; - -include!("src/lib.rs"); - -fn bytes_to_csv(bytes: &[u8]) -> String { - bytes - .iter() - .map(|b| format!("0x{b:02X}")) - .collect::>() - .join(", ") -} - -fn main() { - println!("cargo:rerun-if-env-changed=OUTPUT_DIRECTORY"); - println!("cargo:rerun-if-env-changed=TEMPLATES_DIRECTORY"); - - let (Ok(output), Ok(templates)) = ( - env::var("OUTPUT_DIRECTORY"), - env::var("TEMPLATES_DIRECTORY"), - ) else { - return; - }; - - let output = PathBuf::from(output); - let templates = PathBuf::from(templates); - - let begin = bytes_to_csv(&MARKER_BEGIN); - let end = bytes_to_csv(&MARKER_END); - - fs::create_dir_all(&output).unwrap(); - - for entry in fs::read_dir(&templates).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - let content = fs::read_to_string(&path).unwrap(); - fs::write( - output.join(path.file_name().unwrap()), - content.replace("{begin}", &begin).replace("{end}", &end), - ) - .unwrap(); - } -} diff --git a/markers/src/lib.rs b/markers/src/lib.rs deleted file mode 100644 index ea9b3060..00000000 --- a/markers/src/lib.rs +++ /dev/null @@ -1,27 +0,0 @@ -pub const MARKER_SIZE: usize = 10; - -pub const MARKER_BEGIN: [u8; MARKER_SIZE] = [ - 0xEB, - (MARKER_SIZE - 2) as u8, - b'B', - b'I', - b'N', - b'S', - b'A', - b'F', - b'E', - 0x01, -]; - -pub const MARKER_END: [u8; MARKER_SIZE] = [ - 0xEB, - (MARKER_SIZE - 2) as u8, - b'B', - b'I', - b'N', - b'S', - b'A', - b'F', - b'E', - 0x02, -]; diff --git a/obfuscator/Cargo.toml b/obfuscator/Cargo.toml index 84f0dc30..2329f43b 100644 --- a/obfuscator/Cargo.toml +++ b/obfuscator/Cargo.toml @@ -6,7 +6,6 @@ edition = "2021" [dependencies] logger = { path = "../logger" } runtime = { path = "../runtime" } -markers = { path = "../markers" } iced-x86 = { version = "1.21.0", features = ["code_asm"] } rand = "0.8.5" clap = { version = "4.5.53", features = ["derive"] } diff --git a/obfuscator/src/args.rs b/obfuscator/src/args.rs index 02f5ea44..ce34a981 100644 --- a/obfuscator/src/args.rs +++ b/obfuscator/src/args.rs @@ -7,6 +7,9 @@ use clap::Parser; pub struct Args { pub input: PathBuf, - #[arg(short = 'v', long = "virtualization")] + #[arg(long = "virtualization")] pub virtualization: bool, + + #[arg(short = 'v', long = "verbose")] + pub verbose: bool, } diff --git a/obfuscator/src/engine.rs b/obfuscator/src/engine.rs index e5b5a1fe..c318e0e7 100644 --- a/obfuscator/src/engine.rs +++ b/obfuscator/src/engine.rs @@ -4,13 +4,14 @@ use exe::{ ThunkFunctions, VecPE, HDR32_MAGIC, HDR64_MAGIC, PE, RVA, }; use iced_x86::{Decoder, DecoderOptions, FlowControl, Formatter, Instruction, IntelFormatter}; -use logger::info; -use markers::{MARKER_BEGIN, MARKER_END, MARKER_SIZE}; +use logger::{debug, info}; use runtime::runtime::Runtime; use std::{collections::HashSet, fmt, mem}; use crate::{args::Args, exceptions, protections::Protection}; +const BINSAFE_SECTION: &str = ".binsafe"; + pub struct Block { pub rva: u32, pub offset: usize, @@ -124,7 +125,10 @@ impl<'a> Engine<'a> { pub fn scan(&mut self) { let entry_point = self.pe.get_entrypoint().unwrap(); - let code_section = self.pe.get_section_by_rva(entry_point).unwrap(); + let code_section = self + .pe + .get_section_by_name(BINSAFE_SECTION) + .unwrap_or(self.pe.get_section_by_rva(entry_point).unwrap()); info!( "Section VA: 0x{:X}, file offset: 0x{:X}", @@ -139,137 +143,37 @@ impl<'a> Engine<'a> { let ip = code_section.virtual_address.0 as u64; let code = code_section.read(&self.pe).unwrap().to_vec(); - if self.scan_markers(ip, &code) { - return; - } + let data_references = self.collect_data_references(&code, ip); + let code_references = + self.collect_code_references(&code, ip, &code_section, &data_references); - self.scan_blocks(ip, &code, &code_section); + self.scan_blocks(ip, &code, &code_references); } - fn scan_markers(&mut self, ip: u64, code: &[u8]) -> bool { - let mut markers = Vec::new(); - let mut begin = None; - let mut cursor = 0; - - while cursor + MARKER_SIZE <= code.len() { - if code[cursor..cursor + MARKER_SIZE] == MARKER_BEGIN { - begin = Some(cursor + MARKER_SIZE); - cursor += MARKER_SIZE; - continue; - } - if code[cursor..cursor + MARKER_SIZE] == MARKER_END { - if let Some(start) = begin.take() { - markers.push((start, cursor)); - } - cursor += MARKER_SIZE; - continue; - } - cursor += 1; - } - - if markers.is_empty() { - return false; + fn capture_block(&mut self, block: &mut Vec, end: u32) { + if block.is_empty() { + return; } - info!("Found {} marked regions", markers.len()); - - let mut capture = |block: &mut Vec, end: u32| { - if block.is_empty() { - return; - } - let rva = block[0].ip() as u32; - let offset = self.pe.translate(PETranslation::Memory(RVA(rva))).unwrap(); - let size = (end - rva) as usize; - let block = Block { - rva, - offset, - size, - instructions: mem::take(block), - }; - info!("{block}"); - self.blocks.push(block); - }; - - for &(start, end) in &markers { - let rva = ip as u32 + start as u32; - let mut decoder = Decoder::with_ip( - self.bitness, - &code[start..end], - rva as u64, - DecoderOptions::NONE, - ); - - let mut instruction = Instruction::default(); - let mut block = Vec::new(); - - while decoder.can_decode() { - decoder.decode_out(&mut instruction); - - if instruction.is_invalid() { - if !block.is_empty() { - capture(&mut block, instruction.ip() as u32); - } - break; - } - - block.push(instruction); - - if !matches!(instruction.flow_control(), FlowControl::Next) { - capture(&mut block, instruction.next_ip() as u32); - } - } - - if !block.is_empty() { - capture(&mut block, ip as u32 + end as u32); - } + let rva = block[0].ip() as u32; + let offset = self.pe.translate(PETranslation::Memory(RVA(rva))).unwrap(); + let size = (end - rva) as usize; + self.blocks.push(Block { + rva, + offset, + size, + instructions: mem::take(block), + }); + + if self.args.verbose { + debug!("{}", self.blocks[self.blocks.len() - 1]) } - - for (start, end) in markers { - let start = self - .pe - .translate(PETranslation::Memory(RVA(ip as u32 + start as u32))) - .unwrap(); - let end = self - .pe - .translate(PETranslation::Memory(RVA(ip as u32 + end as u32))) - .unwrap(); - - self.nop(start - MARKER_SIZE, MARKER_SIZE); - self.nop(end, MARKER_SIZE); - } - - info!("Found {} blocks", self.blocks.len()); - - true } - fn scan_blocks(&mut self, ip: u64, code: &[u8], code_section: &ImageSectionHeader) { - let data_references = self.collect_data_references(code, ip); - - let mut code_references = self - .collect_code_references(code, ip, code_section, &data_references) - .into_iter() - .collect::>(); - code_references.sort(); - - let mut capture = |block: &mut Vec, end: u32| { - if block.is_empty() { - return; - } - let rva = block[0].ip() as u32; - let offset = self.pe.translate(PETranslation::Memory(RVA(rva))).unwrap(); - let size = (end - rva) as usize; - self.blocks.push(Block { - rva, - offset, - size, - instructions: mem::take(block), - }); - }; - + fn collect_blocks(&mut self, code: &[u8], ip: u64, code_references: &[u32]) { let mut decoder = Decoder::with_ip(self.bitness, code, ip, DecoderOptions::NONE); let mut instruction = Instruction::default(); - let mut block = Vec::new(); + let mut block: Vec = Vec::new(); let mut inblock = false; while decoder.can_decode() { @@ -277,7 +181,7 @@ impl<'a> Engine<'a> { if code_references.binary_search(&rva).is_ok() { if inblock { - capture(&mut block, rva); + self.capture_block(&mut block, rva); } inblock = true; } @@ -302,7 +206,7 @@ impl<'a> Engine<'a> { && code_references[position - 1] < end; if overlaps { - capture(&mut block, start); + self.capture_block(&mut block, start); inblock = false; continue; } @@ -310,44 +214,45 @@ impl<'a> Engine<'a> { block.push(instruction); if !matches!(instruction.flow_control(), FlowControl::Next) { - capture(&mut block, instruction.next_ip() as u32); + self.capture_block(&mut block, instruction.next_ip() as u32); inblock = false; } } + } - info!("Found {} blocks", self.blocks.len()); - - // let start = 1230; - // let end = 1335; - // let middle = start + (end - start) / 2; - - // self.blocks.drain(end..); - // self.blocks.drain(..start); + fn scan_blocks(&mut self, ip: u64, code: &[u8], code_references: &HashSet) { + let mut sorted_code_references = code_references.iter().copied().collect::>(); + sorted_code_references.sort(); - // debug!("{} to {} (middle: {})", start, end, middle); + self.collect_blocks(code, ip, &sorted_code_references); - // debug!( - // "0x{:016X} {}", - // self.pe.get_image_base().unwrap() + self.blocks[0].rva as u64, - // self.blocks[0] - // ); + info!("Found {} blocks", self.blocks.len()); } fn collect_data_references(&self, code: &[u8], ip: u64) -> HashSet { let mut data_references = HashSet::new(); - let mut decoder = Decoder::with_ip(self.bitness, code, ip, DecoderOptions::NONE); - let mut instruction = Instruction::default(); + let mut cursor = 0; - while decoder.can_decode() { + while cursor < code.len() { + let mut decoder = Decoder::with_ip( + self.bitness, + &code[cursor..], + ip + cursor as u64, + DecoderOptions::NONE, + ); + let mut instruction = Instruction::default(); decoder.decode_out(&mut instruction); if instruction.is_invalid() { + cursor += 1; continue; } if instruction.is_ip_rel_memory_operand() { data_references.insert(instruction.ip_rel_memory_address() as u32); } + + cursor += instruction.len(); } data_references @@ -411,13 +316,21 @@ impl<'a> Engine<'a> { data_references: &HashSet, ) -> HashSet { let mut references = self.collect_static_references(code_section); - let mut decoder = Decoder::with_ip(self.bitness, code, ip, DecoderOptions::NONE); - let mut instruction = Instruction::default(); + let mut cursor = 0; let mut previous = Vec::new(); - while decoder.can_decode() { + while cursor < code.len() { + let mut decoder = Decoder::with_ip( + self.bitness, + &code[cursor..], + ip + cursor as u64, + DecoderOptions::NONE, + ); + let mut instruction = Instruction::default(); decoder.decode_out(&mut instruction); + if instruction.is_invalid() { + cursor += 1; previous.clear(); continue; } @@ -427,6 +340,7 @@ impl<'a> Engine<'a> { | FlowControl::UnconditionalBranch | FlowControl::Call => { let target = instruction.near_branch_target() as u32; + if code_section.has_rva(RVA(target)) { references.insert(target); } @@ -450,6 +364,7 @@ impl<'a> Engine<'a> { | FlowControl::UnconditionalBranch | FlowControl::Return ) { + cursor += instruction.len(); continue; } @@ -462,6 +377,8 @@ impl<'a> Engine<'a> { } previous.clear(); + + cursor += instruction.len(); } references diff --git a/obfuscator/src/exceptions.rs b/obfuscator/src/exceptions.rs index 6bba5fd4..05a8ec03 100644 --- a/obfuscator/src/exceptions.rs +++ b/obfuscator/src/exceptions.rs @@ -1,5 +1,5 @@ use exe::{Buffer, ImageDirectoryEntry, PETranslation, VecPE, PE}; -use std::collections::HashSet; +use std::{collections::HashSet, mem::size_of}; #[repr(C, packed)] struct RuntimeFunction { @@ -9,26 +9,92 @@ struct RuntimeFunction { } pub fn get_exception_handlers(pe: &VecPE) -> HashSet { - let mut results = HashSet::new(); + let mut handlers = HashSet::new(); let exceptions = pe .get_data_directory(ImageDirectoryEntry::Exception) .unwrap(); if exceptions.virtual_address.0 == 0 || exceptions.size == 0 { - return results; + return handlers; } let offset = pe .translate(PETranslation::Memory(exceptions.virtual_address)) .unwrap(); - let size = exceptions.size as usize / size_of::(); - let functions = pe.get_slice_ref::(offset, size).unwrap(); + let count = exceptions.size as usize / size_of::(); + let functions = pe.get_slice_ref::(offset, count).unwrap(); + + let mut unwinds = Vec::new(); for rf in functions { - results.insert(rf.begin_address); - results.insert(rf.end_address); + handlers.insert(rf.begin_address); + + if rf.unwind_info_address != 0 { + unwinds.push(rf.unwind_info_address); + } } - results + unwinds.sort_unstable(); + unwinds.dedup(); + + for rva in unwinds { + get_exception_unwinders(pe, rva, &mut handlers); + } + + handlers +} + +fn get_exception_unwinders(pe: &VecPE, rva: u32, handlers: &mut HashSet) { + let offset = match pe.translate(PETranslation::Memory(rva.into())) { + Ok(o) => o, + Err(_) => return, + }; + + let header = match pe.get_slice_ref::(offset, 4) { + Ok(h) => h, + Err(_) => return, + }; + + let version = header[0] & 0x07; + let flags = header[0] >> 3; + let codes = header[2] as usize; + + if version != 1 { + return; + } + + let mut position = 4 + codes * 2; + + if position % 4 != 0 { + position += 4 - (position % 4); + } + + if flags & 0x04 != 0 { + let bytes = match pe.get_slice_ref::(offset, position + 12) { + Ok(b) => b, + Err(_) => return, + }; + + let chained = u32::from_le_bytes(bytes[position + 8..position + 12].try_into().unwrap()); + + handlers.insert(chained); + + get_exception_unwinders(pe, chained, handlers); + + return; + } + + if flags & 0x03 == 0 { + return; + } + + let bytes = match pe.get_slice_ref::(offset, position + 4) { + Ok(b) => b, + Err(_) => return, + }; + + let handler = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap()); + + handlers.insert(handler); } diff --git a/obfuscator/src/protections/virtualization/attestation/anti_debug.rs b/obfuscator/src/protections/virtualization/attestation/anti_debug.rs index 4b2b4bbf..059aa8a9 100644 --- a/obfuscator/src/protections/virtualization/attestation/anti_debug.rs +++ b/obfuscator/src/protections/virtualization/attestation/anti_debug.rs @@ -5,8 +5,6 @@ use runtime::runtime::ImportDef; use runtime::vm::bytecode::{VMReg, VMSeg, VMWidth}; use runtime::vm::encoders::Encode; -const ACCUMULATOR: VMReg = VMReg::Rbx; - const NT_QUERY_INFORMATION_PROCESS_PROLOGUE: [u8; 3] = [0x4C, 0x8B, 0xD1]; const NT_SET_INFORMATION_THREAD_PROLOGUE: [u8; 3] = [0x4C, 0x8B, 0xD1]; const NT_QUERY_INFORMATION_THREAD_PROLOGUE: [u8; 3] = [0x4C, 0x8B, 0xD1]; @@ -28,24 +26,18 @@ pub fn generate( engine: &mut Engine, rng: &mut impl Rng, expected: &mut u64, - mix: Operation, ) -> Vec> { let mut instructions = Vec::>::new(); instructions.extend(reserve(0x30)); - instructions.extend(set_register(ACCUMULATOR, 0)); + instructions.extend(set_register(VMReg::Vp0, 0)); instructions.extend(query_kd_debugger_enabled(engine, rng, expected)); instructions.extend(query_process_debug_object_handle(engine, rng, expected)); instructions.extend(set_hide_from_debugger(engine, rng, expected)); instructions.extend(query_hide_from_debbuger(engine, rng, expected)); - instructions.extend(spill_register(ACCUMULATOR)); - instructions.extend(spill_register(VMReg::Vt0)); - instructions.extend(register_operation(mix)); - instructions.extend(reload_register(VMReg::Vp0)); - instructions.extend(release(0x30)); instructions @@ -66,12 +58,18 @@ fn query_kd_debugger_enabled( VMSeg::None, VMWidth::Lower8, )); - b.extend(accumulate(rng, ACCUMULATOR, None, 0, &mut *expected)); + b.extend(accumulate_immediate( + rng, + VMReg::Vp0, + None, + 0, + &mut *expected, + )); b.extend(import(engine, ImportDef::NtQuerySystemInformation)); b.extend(accumulate_prologue( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rax, &NT_QUERY_INFORMATION_PROCESS_PROLOGUE, expected, @@ -87,7 +85,7 @@ fn query_kd_debugger_enabled( VMSeg::None, )); // SystemInformation -> RDX - b.extend(reload_register(VMReg::Rdx)); + b.extend(store_register(VMReg::Rdx)); // SystemInformationLength -> R8 b.extend(set_register(VMReg::R8, 0x2)); // ReturnLength -> R9 @@ -95,9 +93,9 @@ fn query_kd_debugger_enabled( // NtQuerySystemInformation b.extend(invoke(VMReg::Rax)); - b.extend(accumulate( + b.extend(accumulate_immediate( rng, - ACCUMULATOR, + VMReg::Vp0, Some(VMReg::Rax), STATUS_SUCCESS, expected, @@ -106,7 +104,7 @@ fn query_kd_debugger_enabled( // READ KernelDebuggerEnabled + KernelDebuggerNotPresent b.extend(accumulate_memory( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rsp, 0x20, VMWidth::Lower16, @@ -127,7 +125,7 @@ fn query_process_debug_object_handle( b.extend(import(engine, ImportDef::NtQueryInformationProcess)); b.extend(accumulate_prologue( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rax, &NT_QUERY_INFORMATION_PROCESS_PROLOGUE, expected, @@ -145,17 +143,17 @@ fn query_process_debug_object_handle( VMSeg::None, )); // ProcessInformation -> R8 - b.extend(reload_register(VMReg::R8)); + b.extend(store_register(VMReg::R8)); // ProcessInformationLength -> R9 b.extend(set_register(VMReg::R9, 8)); // ReturnLength -> [RSP + ...] - b.extend(store_memory(VMReg::Rsp, VMReg::None, 1, 0x20, 0)); + b.extend(store_immediate(VMReg::Rsp, VMReg::None, 1, 0x20, 0)); // NtQueryInformationProcess b.extend(invoke(VMReg::Rax)); - b.extend(accumulate( + b.extend(accumulate_immediate( rng, - ACCUMULATOR, + VMReg::Vp0, Some(VMReg::Rax), STATUS_PORT_NOT_SET, expected, @@ -164,7 +162,7 @@ fn query_process_debug_object_handle( // READ ProcessDebugObjectHandle b.extend(accumulate_memory( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rsp, 0x28, VMWidth::Lower64, @@ -185,7 +183,7 @@ fn set_hide_from_debugger( b.extend(import(engine, ImportDef::NtSetInformationThread)); b.extend(accumulate_prologue( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rax, &NT_SET_INFORMATION_THREAD_PROLOGUE, expected, @@ -201,9 +199,9 @@ fn set_hide_from_debugger( // NtSetInformationThread b.extend(invoke(VMReg::Rax)); - b.extend(accumulate( + b.extend(accumulate_immediate( rng, - ACCUMULATOR, + VMReg::Vp0, Some(VMReg::Rax), STATUS_SUCCESS, expected, @@ -222,7 +220,7 @@ fn query_hide_from_debbuger( b.extend(import(engine, ImportDef::NtQueryInformationThread)); b.extend(accumulate_prologue( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rax, &NT_QUERY_INFORMATION_THREAD_PROLOGUE, expected, @@ -241,17 +239,17 @@ fn query_hide_from_debbuger( VMSeg::None, )); // ThreadInformation -> R8 - b.extend(reload_register(VMReg::R8)); + b.extend(store_register(VMReg::R8)); // ThreadInformationLength -> R9 b.extend(set_register(VMReg::R9, 1)); // ReturnLength -> [RSP + ...] - b.extend(store_memory(VMReg::Rsp, VMReg::None, 1, 0x20, 0)); + b.extend(store_immediate(VMReg::Rsp, VMReg::None, 1, 0x20, 0)); // NtQueryInformationThread b.extend(invoke(VMReg::Rax)); - b.extend(accumulate( + b.extend(accumulate_immediate( rng, - ACCUMULATOR, + VMReg::Vp0, Some(VMReg::Rax), STATUS_SUCCESS, expected, @@ -260,7 +258,7 @@ fn query_hide_from_debbuger( // READ ThreadHideFromDebugger b.extend(accumulate_byte( rng, - ACCUMULATOR, + VMReg::Vp0, VMReg::Rsp, 0x28, 1, diff --git a/obfuscator/src/protections/virtualization/attestation/anti_tamper.rs b/obfuscator/src/protections/virtualization/attestation/anti_tamper.rs index 7b0fefd0..8cc15a54 100644 --- a/obfuscator/src/protections/virtualization/attestation/anti_tamper.rs +++ b/obfuscator/src/protections/virtualization/attestation/anti_tamper.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, i32}; +use std::convert::TryInto; use crate::engine::Engine; use crate::protections::virtualization::attestation::*; @@ -6,21 +6,19 @@ use exe::{Buffer, PE, RVA}; use rand::Rng; use runtime::mapper::Mappable; use runtime::runtime::{DataDef, FnDef}; -use runtime::vm::bytecode::{Flag, VMReg, VMSeg, VMVec, VMWidth}; +use runtime::vm::bytecode::{VMReg, VMSeg, VMWidth}; use runtime::vm::encoders::Encode; -const ACCUMULATOR: VMVec = VMVec::Ymm6; - pub fn generate( engine: &mut Engine, rng: &mut impl Rng, expected: &mut u64, - mix: Operation, ) -> Vec> { let operation = Operation::random(rng); - let mut lane0 = 0; - let mut lane1 = 0; + let seed = engine.rt.keys.initializer; + + let mut hash = seed; for &function in FnDef::VARIANTS.iter() { let rva = engine.rt.lookup(engine.rt.function_labels[&function]) as u32; @@ -29,21 +27,21 @@ pub fn generate( let offset = engine.pe.translate(RVA(rva).into()).unwrap(); let bytes = engine.pe.read(offset, size).unwrap(); - let mut chunks = bytes.chunks_exact(size_of::()); + let mut chunks = bytes.chunks_exact(size_of::()); for chunk in &mut chunks { - let lo = u64::from_le_bytes(chunk[0..8].try_into().unwrap()); - let hi = u64::from_le_bytes(chunk[8..16].try_into().unwrap()); - apply_operation(operation, lo, &mut lane0); - apply_operation(operation, hi, &mut lane1); + let word = u64::from_le_bytes(chunk.try_into().unwrap()); + apply_operation(operation, word, &mut hash); + hash = apply_lcg(engine, hash); } for &byte in chunks.remainder() { - apply_operation(operation, byte as u64, &mut lane0); + apply_operation(operation, byte as u64, &mut hash); + hash = apply_lcg(engine, hash); } } - *expected = lane0 ^ lane1; + *expected = hash; let count = FnDef::VARIANTS.len(); @@ -51,66 +49,78 @@ pub fn generate( let mut instructions = Vec::>::new(); - instructions.extend(set_vector(ACCUMULATOR, 0, 0)); - - instructions.extend(foreach(VMReg::Rax, Bound::Immediate(count), 1, || { - let mut outer = Vec::>::new(); - - outer.extend(load_memory( - VMReg::VImage, - VMReg::Rax, - 8, - functions, - VMSeg::None, - VMWidth::Lower64, - )); - outer.extend(reload_register(VMReg::Rcx)); - - outer.extend(spill_register(VMReg::Rcx)); - outer.extend(immediate(0x20)); - outer.extend(shr(None, None)); - outer.extend(reload_register(VMReg::Rdx)); - - outer.extend(mask(Some(VMReg::Rcx), 0xFFFF_FFFF)); - outer.extend(spill_register(VMReg::VImage)); - outer.extend(add(None, None)); - outer.extend(reload_register(VMReg::Rcx)); - - outer.extend(mask(Some(VMReg::Rdx), 15)); - outer.extend(reload_register(VMReg::R8)); - outer.extend(sub(Some(VMReg::Rdx), Some(VMReg::R8))); - outer.extend(reload_register(VMReg::R9)); - - outer.extend(foreach(VMReg::R10, Bound::Register(VMReg::R9), 16, || { - let mut inner = Vec::>::new(); - - inner.extend(spill_vector(ACCUMULATOR, VMWidth::Lower128)); - inner.extend(load_memory( - VMReg::Rcx, - VMReg::R10, - 1, - 0, + instructions.extend(set_register(VMReg::Vp1, seed)); + + instructions.extend(foreach( + engine, + VMReg::Rax, + Bound::Immediate(count), + 1, + |engine| { + let mut outer = Vec::>::new(); + + outer.extend(load_memory( + VMReg::VImage, + VMReg::Rax, + 8, + functions, VMSeg::None, - VMWidth::Lower128, + VMWidth::Lower64, )); - inner.push(vector_operation(operation)); - inner.extend(reload_vector(ACCUMULATOR, VMWidth::Lower128)); + outer.extend(store_register(VMReg::Rcx)); + + outer.extend(load_register(VMReg::Rcx)); + outer.extend(immediate(0x20)); + outer.extend(shr(None, None)); + outer.extend(store_register(VMReg::Rdx)); - inner - })); + outer.extend(mask(Some(VMReg::Rcx), 0xFFFF_FFFF)); + outer.extend(load_register(VMReg::VImage)); + outer.extend(add(None, None)); + outer.extend(store_register(VMReg::Rcx)); - outer.extend(compute_memory(VMReg::Rcx, VMReg::R10, 1, 0, VMSeg::None)); - outer.extend(reload_register(VMReg::Rdx)); + outer.extend(mask(Some(VMReg::Rdx), 7)); + outer.extend(store_register(VMReg::R8)); + outer.extend(sub(Some(VMReg::Rdx), Some(VMReg::R8))); + outer.extend(store_register(VMReg::R9)); - outer.extend(skip( - engine, - VMReg::R8, - VMCondition::cmp(Flag::Zero, 1), - |_| { - foreach(VMReg::R9, Bound::Register(VMReg::R8), 1, || { + outer.extend(foreach( + engine, + VMReg::R10, + Bound::Register(VMReg::R9), + 8, + |engine| { let mut inner = Vec::>::new(); - inner.extend(spill_vector(ACCUMULATOR, VMWidth::Lower64)); + inner.extend(load_register(VMReg::Vp1)); + inner.extend(load_memory( + VMReg::Rcx, + VMReg::R10, + 1, + 0, + VMSeg::None, + VMWidth::Lower64, + )); + inner.extend(register_operation(operation, None, None)); + inner.extend(register_lcg(engine, None)); + inner.extend(store_register(VMReg::Vp1)); + + inner + }, + )); + + outer.extend(compute_memory(VMReg::Rcx, VMReg::R9, 1, 0, VMSeg::None)); + outer.extend(store_register(VMReg::Rdx)); + + outer.extend(foreach( + engine, + VMReg::R9, + Bound::Register(VMReg::R8), + 1, + |engine| { + let mut inner = Vec::>::new(); + + inner.extend(load_register(VMReg::Vp1)); inner.extend(load_memory( VMReg::Rdx, VMReg::R9, @@ -119,22 +129,17 @@ pub fn generate( VMSeg::None, VMWidth::Lower8, )); - inner.extend(register_operation(operation)); - inner.extend(reload_vector(ACCUMULATOR, VMWidth::Lower64)); + inner.extend(register_operation(operation, None, None)); + inner.extend(register_lcg(engine, None)); + inner.extend(store_register(VMReg::Vp1)); inner - }) - }, - )); - - outer - })); - - instructions.extend(spill_vector(ACCUMULATOR, VMWidth::Lower128)); - instructions.extend(xor(None, None)); - instructions.extend(spill_register(VMReg::Vt0)); - instructions.extend(register_operation(mix)); - instructions.extend(reload_register(VMReg::Vp1)); + }, + )); + + outer + }, + )); instructions } diff --git a/obfuscator/src/protections/virtualization/attestation/debug.rs b/obfuscator/src/protections/virtualization/attestation/debug.rs index d9a5f2e7..bf41af85 100644 --- a/obfuscator/src/protections/virtualization/attestation/debug.rs +++ b/obfuscator/src/protections/virtualization/attestation/debug.rs @@ -29,11 +29,11 @@ pub fn print(engine: &mut Engine, message: &str, register: Option) -> Vec let mut instructions = Vec::>::new(); for ®ister in VOLATILE { - instructions.extend(spill_register(register)); + instructions.extend(load_register(register)); } if let Some(register) = register { - instructions.extend(spill_register(register)); + instructions.extend(load_register(register)); } let length = (message.len() + if register.is_some() { 19 } else { 2 } + 15) & !15; @@ -56,8 +56,8 @@ pub fn print(engine: &mut Engine, message: &str, register: Option) -> Vec offset as i32, VMSeg::None, )); - instructions.extend(reload_register(VMReg::Rcx)); - instructions.extend(reload_register(VMReg::Rdx)); + instructions.extend(store_register(VMReg::Rcx)); + instructions.extend(store_register(VMReg::Rdx)); instructions.extend(call(engine, FnDef::Format)); offset += 16; @@ -70,13 +70,13 @@ pub fn print(engine: &mut Engine, message: &str, register: Option) -> Vec } instructions.extend(compute_memory(VMReg::Rsp, VMReg::None, 1, 0, VMSeg::None)); - instructions.extend(reload_register(VMReg::Rcx)); + instructions.extend(store_register(VMReg::Rcx)); instructions.extend(call(engine, FnDef::Print)); instructions.extend(release(length as u64)); for ®ister in VOLATILE.iter().rev() { - instructions.extend(reload_register(register)); + instructions.extend(store_register(register)); } instructions diff --git a/obfuscator/src/protections/virtualization/attestation/mod.rs b/obfuscator/src/protections/virtualization/attestation/mod.rs index 88d4397b..54f36334 100644 --- a/obfuscator/src/protections/virtualization/attestation/mod.rs +++ b/obfuscator/src/protections/virtualization/attestation/mod.rs @@ -4,10 +4,7 @@ use crate::engine::Engine; use crate::protections::virtualization::{crypt, language::*}; use rand::Rng; use runtime::runtime::DataDef; -use runtime::vm::bytecode::{Flag, VMCondition, VMPrecision, VMReg, VMSeg, VMWidth}; -use runtime::vm::encoders::vector_add::VectorAdd; -use runtime::vm::encoders::vector_sub::VectorSub; -use runtime::vm::encoders::vector_xor::VectorXor; +use runtime::vm::bytecode::{Flag, VMCondition, VMReg, VMSeg, VMWidth}; use runtime::vm::encoders::Encode; mod anti_debug; @@ -15,8 +12,11 @@ mod anti_tamper; #[cfg(debug_assertions)] mod debug; -// Masks lower 34 bits of timestamp, creating a ~5s window on a 3.5 GHz CPU -const WINDOW: u64 = 0x22; +// Masks lower 32 bits of timestamp, creating a ~1s window on a 3.5 GHz CPU +const WINDOW: u64 = 0x20; + +const MINIMUM_CYCLES: u64 = 1_000_000; +const MAXIMUM_CYCLES: u64 = 100_000_000; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Operation { @@ -34,7 +34,7 @@ impl Operation { } } - pub fn invert(self) -> Self { + fn inverse(self) -> Self { match self { Operation::Add => Operation::Sub, Operation::Sub => Operation::Add, @@ -50,26 +50,21 @@ pub fn generate(engine: &mut Engine, key: u64) -> Vec>> { let mut block = Vec::>::new(); - block.extend(timestamp()); - block.extend(mask(None, !((1u64 << WINDOW) - 1))); - - block.extend(load_data(engine, DataDef::VmKeyMul, VMWidth::Lower64)); - block.extend(mul(None, None)); - - block.extend(load_data(engine, DataDef::VmKeyAdd, VMWidth::Lower64)); - block.extend(add(None, None)); - - block.extend(reload_register(VMReg::Vt0)); - - block.extend(sub(Some(VMReg::Vt0), Some(VMReg::Vt1))); - block.extend(reload_register(VMReg::Rax)); - let mut vp0 = 0; let mut vp1 = 0; let mix0 = Operation::random(&mut rng); let mix1 = Operation::random(&mut rng); + block.extend(timestamp()); + block.extend(mask(None, !((1u64 << WINDOW) - 1))); + block.extend(register_lcg(engine, None)); + block.extend(store_register(VMReg::Vt0)); + + block.extend(sub(Some(VMReg::Vt0), Some(VMReg::Vt1))); + + block.extend(store_register(VMReg::Rax)); + block.extend(skip( engine, VMReg::Rax, @@ -77,8 +72,74 @@ pub fn generate(engine: &mut Engine, key: u64) -> Vec>> { |engine| { let mut b = Vec::>::new(); - b.extend(anti_debug::generate(engine, &mut rng, &mut vp0, mix0)); - b.extend(anti_tamper::generate(engine, &mut rng, &mut vp1, mix1)); + b.extend(timestamp()); + + b.extend(anti_debug::generate(engine, &mut rng, &mut vp0)); + b.extend(anti_tamper::generate(engine, &mut rng, &mut vp1)); + + b.extend(store_register(VMReg::Rax)); + + b.extend(timestamp()); + + b.extend(sub(Some(VMReg::Rax), None)); + + b.extend(store_register(VMReg::Rax)); + + b.extend(load_register(VMReg::Rax)); + b.extend(immediate(MINIMUM_CYCLES)); + b.extend(sub(None, None)); + b.extend(discard()); + + b.extend(flag(Flag::Carry)); + + b.extend(store_register(VMReg::Rcx)); + + b.extend(accumulate_immediate( + &mut rng, + VMReg::Vp0, + Some(VMReg::Rcx), + 0, + &mut vp0, + )); + b.extend(accumulate_immediate( + &mut rng, + VMReg::Vp1, + Some(VMReg::Rcx), + 0, + &mut vp1, + )); + + b.extend(immediate(MAXIMUM_CYCLES)); + b.extend(load_register(VMReg::Rax)); + b.extend(sub(None, None)); + b.extend(discard()); + + b.extend(flag(Flag::Carry)); + + b.extend(store_register(VMReg::Rcx)); + + b.extend(accumulate_immediate( + &mut rng, + VMReg::Vp0, + Some(VMReg::Rcx), + 0, + &mut vp0, + )); + b.extend(accumulate_immediate( + &mut rng, + VMReg::Vp1, + Some(VMReg::Rcx), + 0, + &mut vp1, + )); + + b.extend(register_operation(mix0, Some(VMReg::Vp0), Some(VMReg::Vt0))); + b.extend(register_lcg(engine, None)); + b.extend(store_register(VMReg::Vp0)); + + b.extend(register_operation(mix1, Some(VMReg::Vp1), Some(VMReg::Vt0))); + b.extend(register_lcg(engine, None)); + b.extend(store_register(VMReg::Vp1)); b.extend(copy(VMReg::Vt0, VMReg::Vt1)); @@ -86,7 +147,7 @@ pub fn generate(engine: &mut Engine, key: u64) -> Vec>> { }, )); - block.extend(correct(engine, &mut rng, key, vp0, mix0, vp1, mix1)); + block.extend(correct(engine, &mut rng, key, vp0, vp1, mix0, mix1)); blocks.push(block); @@ -98,51 +159,59 @@ fn correct( rng: &mut impl Rng, key: u64, vp0: u64, - mix0: Operation, vp1: u64, + mix0: Operation, mix1: Operation, ) -> Vec> { let mut instructions = Vec::>::new(); - instructions.extend(load_absolute(engine, DataDef::VmAttestation, VMReg::Rax)); - instructions.extend(load_absolute(engine, DataDef::VmCode, VMReg::Rcx)); + instructions.extend(load_absolute(engine, DataDef::VmCodeEnd, VMReg::Rax)); + instructions.extend(load_absolute(engine, DataDef::VmAttestation, VMReg::Rcx)); + instructions.extend(load_absolute(engine, DataDef::VmCodeStart, VMReg::R8)); - instructions.extend(sub(Some(VMReg::Vg0), Some(VMReg::Rax))); - instructions.extend(reload_register(VMReg::Rdx)); + instructions.extend(sub(Some(VMReg::Vg0), Some(VMReg::R8))); + instructions.extend(store_register(VMReg::Rdx)); instructions.extend(skip( engine, VMReg::Rdx, VMCondition::cmp(Flag::Zero, 0), - |_| { + |engine| { let mut b = Vec::new(); b.extend(sub(Some(VMReg::Rax), Some(VMReg::Rcx))); b.extend(immediate((crypt::HEADER_SIZE + crypt::TRAILER_SIZE) as u64)); b.extend(sub(None, None)); - b.extend(reload_register(VMReg::R9)); + b.extend(store_register(VMReg::R9)); b.extend(set_register(VMReg::R10, 0)); - b.extend(foreach(VMReg::R8, Bound::Register(VMReg::R9), 8, || { - let mut outer = Vec::new(); - - outer.extend(spill_register(VMReg::R10)); - outer.extend(load_memory( - VMReg::Rcx, - VMReg::R8, - 1, - crypt::HEADER_SIZE as i32, - VMSeg::None, - VMWidth::Lower64, - )); - outer.extend(xor(None, None)); - outer.extend(reload_register(VMReg::R10)); - - outer - })); - - b.extend(spill_register(VMReg::R10)); + b.extend(foreach( + engine, + VMReg::R8, + Bound::Register(VMReg::R9), + 8, + |engine| { + let mut outer = Vec::new(); + + outer.extend(load_register(VMReg::R10)); + outer.extend(load_memory( + VMReg::Rcx, + VMReg::R8, + 1, + crypt::HEADER_SIZE as i32, + VMSeg::None, + VMWidth::Lower64, + )); + outer.extend(xor(None, None)); + outer.extend(register_lcg(engine, None)); + outer.extend(store_register(VMReg::R10)); + + outer + }, + )); + + b.extend(load_register(VMReg::R10)); b }, @@ -168,23 +237,71 @@ fn correct( let combined = combine_operation(operation, vp0, vp1); let correction = combined ^ key; - instructions.extend(spill_register(VMReg::Vp0)); - instructions.extend(spill_register(VMReg::Vt0)); - instructions.extend(register_operation(mix0.invert())); - instructions.extend(spill_register(VMReg::Vp1)); - instructions.extend(spill_register(VMReg::Vt0)); - instructions.extend(register_operation(mix1.invert())); - instructions.extend(register_operation(operation)); + instructions.extend(register_lcg_inverse(engine, Some(VMReg::Vp0))); + instructions.extend(register_operation(mix0.inverse(), None, Some(VMReg::Vt1))); + + instructions.extend(register_lcg_inverse(engine, Some(VMReg::Vp1))); + instructions.extend(register_operation(mix1.inverse(), None, Some(VMReg::Vt1))); + + instructions.extend(register_operation(operation, None, None)); instructions.extend(immediate(correction)); instructions.extend(xor(None, None)); instructions.extend(xor(None, None)); - instructions.extend(reload_register(VMReg::Vg0)); + instructions.extend(register_lcg(engine, None)); + + instructions.extend(store_register(VMReg::Vg0)); + + instructions +} + +fn register_lcg(engine: &mut Engine, register: Option) -> Vec> { + let mut instructions = Vec::>::new(); + + if let Some(register) = register { + instructions.extend(load_register(register)); + } + + instructions.extend(load_data( + engine, + DataDef::VmKeyMultiplier, + VMWidth::Lower64, + )); + instructions.extend(mul(None, None)); + + instructions.extend(load_data(engine, DataDef::VmKeyAddend, VMWidth::Lower64)); + instructions.extend(add(None, None)); instructions } +fn register_lcg_inverse(engine: &mut Engine, register: Option) -> Vec> { + let mut instructions = Vec::>::new(); + + if let Some(register) = register { + instructions.extend(load_register(register)); + } + + instructions.extend(load_data(engine, DataDef::VmKeyAddend, VMWidth::Lower64)); + instructions.extend(sub(None, None)); + + instructions.extend(load_data( + engine, + DataDef::VmKeyMultiplierInverse, + VMWidth::Lower64, + )); + instructions.extend(mul(None, None)); + + instructions +} + +fn apply_lcg(engine: &Engine, value: u64) -> u64 { + value + .wrapping_mul(engine.rt.keys.multiplier) + .wrapping_add(engine.rt.keys.addend) +} + fn combine_operation(operation: Operation, a: u64, b: u64) -> u64 { let mut result = a; apply_operation(operation, b, &mut result); @@ -199,40 +316,22 @@ fn apply_operation(operation: Operation, value: u64, expected: &mut u64) { } } -fn register_operation(operation: Operation) -> Vec> { - let mut instructions = Vec::>::new(); - - match operation { - Operation::Add => instructions.extend(add(None, None)), - Operation::Sub => instructions.extend(sub(None, None)), - Operation::Xor => instructions.extend(xor(None, None)), - } - - instructions -} - -fn vector_operation(operation: Operation) -> Box { +fn register_operation( + operation: Operation, + a: Option, + b: Option, +) -> Vec> { match operation { - Operation::Add => Box::new(VectorAdd { - width: VMWidth::Lower128, - stride: VMWidth::Lower64, - precision: VMPrecision::Integer, - }), - Operation::Sub => Box::new(VectorSub { - width: VMWidth::Lower128, - stride: VMWidth::Lower64, - precision: VMPrecision::Integer, - }), - Operation::Xor => Box::new(VectorXor { - width: VMWidth::Lower128, - }), + Operation::Add => add(a, b), + Operation::Sub => sub(a, b), + Operation::Xor => xor(a, b), } } fn accumulate( rng: &mut R, accumulator: VMReg, - source: Option, + source: Vec>, value: u64, expected: &mut u64, ) -> Vec> { @@ -242,24 +341,33 @@ fn accumulate( let mix = rng.gen::(); - instructions.extend(spill_register(accumulator)); - - if let Some(source) = source { - instructions.extend(spill_register(source)); - } + instructions.extend(load_register(accumulator)); + instructions.extend(source); instructions.extend(immediate(mix)); instructions.extend(xor(None, None)); - - instructions.extend(register_operation(operation)); - - instructions.extend(reload_register(accumulator)); + instructions.extend(register_operation(operation, None, None)); + instructions.extend(store_register(accumulator)); apply_operation(operation, value ^ mix, expected); instructions } -fn accumulate_memory( +pub fn accumulate_immediate( + rng: &mut R, + accumulator: VMReg, + source: Option, + value: u64, + expected: &mut u64, +) -> Vec> { + let source = match source { + Some(register) => load_register(register), + None => Vec::new(), + }; + accumulate(rng, accumulator, source, value, expected) +} + +pub fn accumulate_memory( rng: &mut R, accumulator: VMReg, base: VMReg, @@ -268,25 +376,8 @@ fn accumulate_memory( value: u64, expected: &mut u64, ) -> Vec> { - let mut instructions = Vec::>::new(); - - let operation = Operation::random(rng); - - instructions.extend(spill_register(accumulator)); - instructions.extend(load_memory( - base, - VMReg::None, - 1, - displacement, - VMSeg::None, - width, - )); - instructions.extend(register_operation(operation)); - instructions.extend(reload_register(accumulator)); - - apply_operation(operation, value, expected); - - instructions + let source = load_memory(base, VMReg::None, 1, displacement, VMSeg::None, width); + accumulate(rng, accumulator, source, value, expected) } fn accumulate_byte( diff --git a/obfuscator/src/protections/virtualization/crypt.rs b/obfuscator/src/protections/virtualization/crypt.rs index bfef86da..a5afda06 100644 --- a/obfuscator/src/protections/virtualization/crypt.rs +++ b/obfuscator/src/protections/virtualization/crypt.rs @@ -1,5 +1,5 @@ use rand::Rng; -use runtime::VM_INTEGRITY_QWORD; +use runtime::{VM_CIPHER_ROUNDS, VM_INTEGRITY_QWORD}; pub const HEADER_SIZE: usize = size_of::(); pub const TRAILER_SIZE: usize = size_of::() + size_of::(); @@ -7,12 +7,97 @@ pub const TRAILER_SIZE: usize = size_of::() + size_of::(); const ENCRYPTED: u8 = 0; const DECRYPTED: u8 = 1; -pub fn derive_hash(bytes: &[u8]) -> u64 { +fn keystream(key: u64, counter: u64) -> u64 { + let mut a = key as u32; + let mut b = (key >> 32) as u32; + let mut x = (counter >> 32) as u32; + let mut y = counter as u32; + + for round in 0..VM_CIPHER_ROUNDS { + x = x.rotate_right(8).wrapping_add(y) ^ a; + y = y.rotate_left(3) ^ x; + b = a.wrapping_add(b.rotate_right(8)) ^ round; + a = a.rotate_left(3) ^ b; + } + + ((x as u64) << 32) | (y as u64) +} + +pub struct Cipher { + multiplier: u64, + addend: u64, +} + +impl Cipher { + pub fn new(multiplier: u64, addend: u64) -> Self { + Self { multiplier, addend } + } + + pub fn encrypt_block(&self, block: &mut Vec, key: u64, secret: u64) { + let length = prepare_block(block); + finalize_encrypt(block, length); + self.encrypt_payload(block, key, secret); + } + + pub fn decrypt_block(&self, block: &mut Vec, key: u64, secret: u64) { + let length = u16::from_le_bytes(block[..HEADER_SIZE].try_into().unwrap()) as usize; + self.decrypt_payload(block, key, secret); + unprepare_block(block, length); + } + + pub fn encrypt_payload(&self, block: &mut [u8], key: u64, secret: u64) { + let length = u16::from_le_bytes(block[..HEADER_SIZE].try_into().unwrap()) as usize; + let payload = &mut block[HEADER_SIZE..HEADER_SIZE + ((length + 8 + 7) & !7)]; + + let key = (key ^ secret) + .wrapping_mul(self.multiplier) + .wrapping_add(self.addend); + + for (counter, chunk) in payload.chunks_exact_mut(8).enumerate() { + let qword = u64::from_le_bytes(chunk.try_into().unwrap()); + let cipher = qword ^ keystream(key, counter as u64); + chunk.copy_from_slice(&cipher.to_le_bytes()); + } + + // byte - state + block[block.len() - TRAILER_SIZE] = ENCRYPTED; + } + + pub fn decrypt_payload(&self, block: &mut [u8], key: u64, secret: u64) { + let length = u16::from_le_bytes(block[..HEADER_SIZE].try_into().unwrap()) as usize; + let payload = &mut block[HEADER_SIZE..HEADER_SIZE + ((length + 8 + 7) & !7)]; + + let key = (key ^ secret) + .wrapping_mul(self.multiplier) + .wrapping_add(self.addend); + + for (counter, chunk) in payload.chunks_exact_mut(8).enumerate() { + let cipher = u64::from_le_bytes(chunk.try_into().unwrap()); + let original = cipher ^ keystream(key, counter as u64); + chunk.copy_from_slice(&original.to_le_bytes()); + } + + assert_eq!( + u64::from_le_bytes( + payload[length..length + size_of::()] + .try_into() + .unwrap() + ), + VM_INTEGRITY_QWORD + ); + + finalize_decrypt(block); + } +} + +pub fn derive_hash(bytes: &[u8], multiplier: u64, addend: u64) -> u64 { let start = HEADER_SIZE; let end = bytes.len() - TRAILER_SIZE; let payload = &bytes[start..end]; - payload.chunks_exact(8).fold(0, |key, chunk| { - key ^ u64::from_le_bytes(chunk.try_into().unwrap()) + payload.chunks_exact(8).fold(0, |hash, chunk| { + (hash ^ u64::from_le_bytes(chunk.try_into().unwrap())) + .wrapping_mul(multiplier) + .wrapping_add(addend) }) } @@ -22,18 +107,6 @@ pub fn derive_key(bytes: &[u8]) -> u64 { u64::from_le_bytes(bytes[start..end].try_into().unwrap()) } -pub fn encrypt_block(block: &mut Vec, key: u64, mul: u64, add: u64, att: u64) { - let length = prepare_block(block); - encrypt_payload(block, key, mul, add, att); - finalize_encrypt(block, length); -} - -pub fn decrypt_block(block: &mut Vec, key: u64, mul: u64, add: u64, att: u64) { - let length = u16::from_le_bytes(block[..HEADER_SIZE].try_into().unwrap()) as usize; - decrypt_payload(block, key, mul, add, att); - unprepare_block(block, length); -} - fn align_payload(block: &mut Vec) { let mut rng = rand::thread_rng(); @@ -44,43 +117,7 @@ fn align_payload(block: &mut Vec) { } } -fn encrypt_payload(block: &mut [u8], key: u64, mul: u64, add: u64, att: u64) { - let mut key = key ^ att; - - for chunk in block.chunks_exact_mut(8) { - let mut qword = u64::from_le_bytes(chunk.try_into().unwrap()); - qword ^= key; - chunk.copy_from_slice(&qword.to_le_bytes()); - key ^= qword; - key = key.wrapping_mul(mul).wrapping_add(add); - } -} - -pub fn decrypt_payload(block: &mut [u8], mut key: u64, mul: u64, add: u64, att: u64) { - let length = u16::from_le_bytes(block[..HEADER_SIZE].try_into().unwrap()) as usize; - let payload = &mut block[HEADER_SIZE..HEADER_SIZE + ((length + 8 + 7) & !7)]; - - for chunk in payload.chunks_exact_mut(8) { - let qword = u64::from_le_bytes(chunk.try_into().unwrap()); - let original = qword ^ key; - chunk.copy_from_slice(&original.to_le_bytes()); - key ^= qword ^ att; - key = key.wrapping_mul(mul).wrapping_add(add); - } - - assert_eq!( - u64::from_le_bytes( - payload[length..length + size_of::()] - .try_into() - .unwrap() - ), - VM_INTEGRITY_QWORD - ); - - finalize_decrypt(block); -} - -pub fn finalize_encrypt(block: &mut Vec, length: u16) { +fn finalize_encrypt(block: &mut Vec, length: u16) { // word - length block.splice(0..0, length.to_le_bytes()); // byte - state diff --git a/obfuscator/src/protections/virtualization/language.rs b/obfuscator/src/protections/virtualization/language.rs index 3f874dcc..dbcc0acf 100644 --- a/obfuscator/src/protections/virtualization/language.rs +++ b/obfuscator/src/protections/virtualization/language.rs @@ -1,6 +1,6 @@ use crate::engine::Engine; use runtime::runtime::{DataDef, FnDef, ImportDef}; -use runtime::vm::bytecode::{VMCondition, Flag, VMLogic, VMMem, VMReg, VMSeg, VMVec, VMWidth}; +use runtime::vm::bytecode::{Flag, VMCondition, VMLogic, VMMem, VMReg, VMSeg, VMWidth}; use runtime::vm::encoders::add::Add; use runtime::vm::encoders::and::And; use runtime::vm::encoders::block::{Block, Jump, Target}; @@ -11,13 +11,13 @@ use runtime::vm::encoders::load_address::LoadAddress; use runtime::vm::encoders::load_immediate::LoadImmediate; use runtime::vm::encoders::load_memory::LoadMemory; use runtime::vm::encoders::load_register::LoadRegister; -use runtime::vm::encoders::load_vector::LoadVector; use runtime::vm::encoders::mul::Mul; use runtime::vm::encoders::or::Or; +use runtime::vm::encoders::pop::Pop; +use runtime::vm::encoders::push::Push; use runtime::vm::encoders::shl::Shl; use runtime::vm::encoders::shr::Shr; use runtime::vm::encoders::store_memory::StoreMemory; -use runtime::vm::encoders::store_merge::StoreMerge; use runtime::vm::encoders::store_register::StoreRegister; use runtime::vm::encoders::sub::Sub; use runtime::vm::encoders::timestamp::Timestamp; @@ -60,7 +60,8 @@ pub fn skip Vec>>( instructions } -pub fn foreach Vec>>( +pub fn foreach Vec>>( + engine: &mut Engine, counter: VMReg, bound: Bound, step: u64, @@ -76,9 +77,10 @@ pub fn foreach Vec>>( operations.push(Box::new(destination)); - operations.extend(body()); + operations.extend(body(engine)); operations.extend(increment(counter, step)); - operations.extend(spill_register(counter)); + operations.extend(load_register(counter)); + match bound { Bound::Immediate(value) => { operations.push(Box::new(LoadImmediate { @@ -93,6 +95,7 @@ pub fn foreach Vec>>( })); } } + operations.push(Box::new(Sub { width: VMWidth::Lower64, })); @@ -101,7 +104,6 @@ pub fn foreach Vec>>( let source = Label::source(); operations.push(Box::new(source)); - operations.push(Box::new(LoadImmediate { width: VMWidth::SLower16, source: vec![0, 0], @@ -116,7 +118,16 @@ pub fn foreach Vec>>( destination: Target::Label(destination), }); - vec![Box::new(Block::new(operations, jumps))] + let block = Box::new(Block::new(operations, jumps)); + + match bound { + Bound::Register(register) => { + skip(engine, register, VMCondition::cmp(Flag::Zero, 1), |_| { + vec![block] + }) + } + Bound::Immediate(_) => vec![block], + } } pub fn compute_data(engine: &mut Engine, def: DataDef) -> Vec> { @@ -140,9 +151,9 @@ pub fn load_absolute(engine: &mut Engine, def: DataDef, register: VMReg) -> Vec< let mut operations = Vec::>::new(); operations.extend(compute_data(engine, def)); - operations.extend(reload_register(register)); + operations.extend(store_register(register)); - operations.extend(spill_register(register)); + operations.extend(load_register(register)); operations.extend(load_memory( register, VMReg::None, @@ -152,7 +163,7 @@ pub fn load_absolute(engine: &mut Engine, def: DataDef, register: VMReg) -> Vec< VMWidth::SLower32, )); operations.extend(add(None, None)); - operations.extend(reload_register(register)); + operations.extend(store_register(register)); operations } @@ -186,24 +197,6 @@ pub fn set_register(register: VMReg, value: u64) -> Vec> { ] } -pub fn set_vector(destination: VMVec, lo: u64, hi: u64) -> Vec> { - let mut instructions: Vec> = Vec::>::new(); - - instructions.push(Box::new(LoadImmediate { - width: VMWidth::Lower64, - source: lo.to_le_bytes().to_vec(), - })); - instructions.push(Box::new(LoadImmediate { - width: VMWidth::Lower64, - source: hi.to_le_bytes().to_vec(), - })); - instructions.push(Box::new(StoreMerge { - width: VMWidth::Lower128, - destination, - })); - instructions -} - pub fn copy(source: VMReg, destination: VMReg) -> Vec> { vec![ Box::new(LoadRegister { @@ -310,28 +303,20 @@ pub fn call(engine: &mut Engine, def: FnDef) -> Vec> { ] } -pub fn spill_register(source: VMReg) -> Vec> { +pub fn load_register(source: VMReg) -> Vec> { vec![Box::new(LoadRegister { width: VMWidth::Lower64, source, })] } -pub fn spill_vector(source: VMVec, width: VMWidth) -> Vec> { - vec![Box::new(LoadVector { width, source })] -} - -pub fn reload_register(destination: VMReg) -> Vec> { +pub fn store_register(destination: VMReg) -> Vec> { vec![Box::new(StoreRegister { width: VMWidth::Lower64, destination, })] } -pub fn reload_vector(destination: VMVec, width: VMWidth) -> Vec> { - vec![Box::new(StoreMerge { width, destination })] -} - pub fn mask(source: Option, mask: u64) -> Vec> { let mut instructions = Vec::>::new(); @@ -409,9 +394,12 @@ pub fn mul(a: Option, b: Option) -> Vec> { })); } instructions.push(Box::new(Mul { - width: VMWidth::Lower64, + width: VMWidth::SLower64, })); + instructions.push(Box::new(Push::new())); instructions.push(Box::new(Discard::new())); + instructions.push(Box::new(Pop::new())); + instructions } @@ -489,7 +477,7 @@ pub fn load_memory( instructions } -pub fn store_memory( +pub fn store_immediate( base: VMReg, index: VMReg, scale: u8, @@ -515,3 +503,23 @@ pub fn store_memory( }), ] } + +pub fn flag(flag: Flag) -> Vec> { + vec![ + Box::new(LoadRegister { + width: VMWidth::Lower64, + source: VMReg::Flags, + }), + Box::new(LoadImmediate { + width: VMWidth::Lower64, + source: flag.bit64().to_le_bytes().to_vec(), + }), + Box::new(And { + width: VMWidth::Lower64, + }), + ] +} + +pub fn discard() -> Vec> { + vec![Box::new(Discard::new())] +} diff --git a/obfuscator/src/protections/virtualization/mod.rs b/obfuscator/src/protections/virtualization/mod.rs index 53ea95c7..d8b72b06 100644 --- a/obfuscator/src/protections/virtualization/mod.rs +++ b/obfuscator/src/protections/virtualization/mod.rs @@ -4,124 +4,162 @@ use std::hash::{DefaultHasher, Hash, Hasher}; use std::{i32, slice}; use crate::engine::Engine; +use crate::protections::virtualization::crypt::Cipher; use crate::protections::Protection; use exe::{Buffer, SectionCharacteristics}; use exe::{PE, RVA}; -use iced_x86::code_asm::CodeAssembler; +use iced_x86::code_asm::{dword_ptr, rsp, CodeAssembler}; use iced_x86::Mnemonic; -use logger::info; +use logger::{debug, info}; use rand::Rng; use runtime::runtime::{DataDef, FnDef}; use runtime::vm::bytecode::{self}; -use runtime::vm::encoders::Encode; -use runtime::{VM_DISPATCH_SIZE, VM_TRAMPOLINE_SIZE}; +use runtime::{VM_DISPATCH_SIZE, VM_REDIRECT_SIZE, VM_TRAMPOLINE_SIZE}; mod attestation; pub mod crypt; mod language; -struct Keys { - seed: u64, - mul: u64, - add: u64, - att: u64, +#[derive(Clone, Copy, Default)] +struct TableEntry { + exit: u32, + entry: u32, + offset: u32, } -impl Default for Keys { - fn default() -> Self { - let mut rng = rand::thread_rng(); - +impl TableEntry { + fn new(rva: u32, size: usize, return_address: i32, offset: u32) -> Self { Self { - seed: rng.gen::(), - mul: rng.gen::(), - add: rng.gen::(), - att: rng.gen::(), + exit: (rva as i64 + size as i64 - return_address as i64) as u32, + entry: (rva as i64 - return_address as i64) as u32, + offset, } } } +impl From for [u8; size_of::()] { + fn from(entry: TableEntry) -> Self { + let mut bytes = [0u8; size_of::()]; + bytes[0..4].copy_from_slice(&entry.exit.to_le_bytes()); + bytes[4..8].copy_from_slice(&entry.entry.to_le_bytes()); + bytes[8..12].copy_from_slice(&entry.offset.to_le_bytes()); + bytes + } +} + +fn resolve(engine: &Engine, label: DataDef) -> (u32, usize) { + let rva = engine.rt.lookup(engine.rt.data_labels[&label]) as u32; + let offset = engine.pe.translate(RVA(rva).into()).unwrap(); + (rva, offset) +} + +fn write_displacement(engine: &mut Engine, label: DataDef, target: u32) { + let (rva, offset) = resolve(engine, label); + let displacement = (target as i64 - rva as i64) as i32; + engine + .pe + .write(offset, &displacement.to_le_bytes()) + .unwrap(); +} + +fn write_entry(engine: &mut Engine, index: usize, entry: TableEntry) { + let (_, offset) = resolve(engine, DataDef::VmTable); + let bytes = <[u8; size_of::()]>::from(entry); + engine + .pe + .write(offset + index * size_of::(), &bytes) + .unwrap(); +} + +fn write_trampoline(engine: &mut Engine, redirect: usize, bytes: &[u8]) { + let (_, offset) = resolve(engine, DataDef::VmTrampolines); + engine + .pe + .write(offset + redirect * VM_REDIRECT_SIZE, bytes) + .unwrap(); +} + #[derive(Default)] pub struct Virtualization { - keys: Keys, - programs: Vec>>, + programs: Vec>, groups: Vec>, virtualized: HashMap, - trampolines: HashMap, + redirects: HashMap, duplicates: usize, blocked: usize, missing: HashMap, } impl Virtualization { - fn attestation(&self, engine: &mut Engine) -> Vec { - let mut vcode = Vec::new(); + fn attestation(&self, engine: &mut Engine, base: u64) -> Vec { + let mut code = Vec::new(); - #[cfg(debug_assertions)] let mut log = Vec::new(); - let blocks = attestation::generate(engine, self.keys.att); + let blocks = attestation::generate(engine, engine.rt.keys.secret); + + let cipher = Cipher::new(engine.rt.keys.multiplier, engine.rt.keys.addend); - for (_index, operations) in blocks.into_iter().enumerate() { + for (index, operations) in blocks.into_iter().enumerate() { let mut rng = rand::thread_rng(); - #[cfg(debug_assertions)] - let (transformed, snapshots) = - bytecode::transform_with_snapshots(&mut engine.rt.mapper, operations, |ready| { - rng.gen_range(0..ready.len()) - }); + let offset = base + code.len() as u64; - #[cfg(not(debug_assertions))] - let transformed = bytecode::transform(&mut engine.rt.mapper, operations, |ready| { - rng.gen_range(0..ready.len()) - }); + let transformed = if engine.args.verbose { + let (transformed, snapshots) = bytecode::transform_with_snapshots( + &mut engine.rt.mapper, + operations, + offset, + |ready| rng.gen_range(0..ready.len()), + ); - #[cfg(debug_assertions)] - { - let index = _index; - log.push(format!(" BLOCK {}:\n{}", index, snapshots)); - } + log.push(format!("{}BLOCK {}:\n{}", " ".repeat(4), index, snapshots)); + + transformed + } else { + bytecode::transform(&mut engine.rt.mapper, operations, offset, |ready| { + rng.gen_range(0..ready.len()) + }) + }; let mut bytes = bytecode::assemble(&mut engine.rt.mapper, &transformed); - let key = if vcode.is_empty() { - self.keys.seed + let key = if code.is_empty() { + engine.rt.keys.initializer } else { - crypt::derive_key(&vcode) + crypt::derive_key(&code) }; - crypt::encrypt_block(&mut bytes, key, self.keys.mul, self.keys.add, 0); - crypt::decrypt_payload(&mut bytes, key, self.keys.mul, self.keys.add, 0); + cipher.encrypt_block(&mut bytes, key, 0); + cipher.decrypt_payload(&mut bytes, key, 0); - vcode.extend_from_slice(&bytes); + code.extend_from_slice(&bytes); } - #[cfg(debug_assertions)] - { - use logger::debug; - + if engine.args.verbose { debug!( "ATTESTATION @ 0x{:016X}:\n{}", - self.keys.att, + engine.rt.keys.secret, log.join("\n") ); } - vcode + code } } impl Protection for Virtualization { fn initialize(&mut self, engine: &mut Engine) { - #[cfg(debug_assertions)] - const MAX_LOGGING: usize = 16; - - let mut vtable = Vec::new(); + let mut log = Vec::new(); - #[cfg(debug_assertions)] - let mut logs = Vec::new(); + let mut table = Vec::new(); let mut lookup = HashMap::new(); + let mut code = Vec::new(); + + let cipher = Cipher::new(engine.rt.keys.multiplier, engine.rt.keys.addend); + 'outer: for block in &mut engine.blocks { if block.size < VM_TRAMPOLINE_SIZE { continue; @@ -156,28 +194,34 @@ impl Protection for Virtualization { if lookup.get(&hash).is_none() { let mut rng = rand::thread_rng(); - #[cfg(debug_assertions)] - let transformed = if logs.len() < MAX_LOGGING { + let offset = code.len() as u64; + + let transformed = if engine.args.verbose { let (transformed, snapshots) = bytecode::transform_with_snapshots( &mut engine.rt.mapper, lifted, + offset, |ready| rng.gen_range(0..ready.len()), ); - logs.push((block.rva, format!("{}", snapshots))); + + log.push((block.rva, format!("{}", snapshots))); + transformed } else { - bytecode::transform(&mut engine.rt.mapper, lifted, |ready| { + bytecode::transform(&mut engine.rt.mapper, lifted, offset, |ready| { rng.gen_range(0..ready.len()) }) }; - #[cfg(not(debug_assertions))] - let transformed = bytecode::transform(&mut engine.rt.mapper, lifted, |ready| { - rng.gen_range(0..ready.len()) - }); + let mut bytes = bytecode::assemble(&mut engine.rt.mapper, &transformed); + + cipher.encrypt_block(&mut bytes, 0, 0); + cipher.decrypt_payload(&mut bytes, 0, 0); + + code.extend_from_slice(&bytes); let index = self.programs.len(); - self.programs.push(transformed); + self.programs.push(bytes); self.groups.push(vec![block.rva]); @@ -191,131 +235,115 @@ impl Protection for Virtualization { for (_, group) in self.programs.iter().zip(&self.groups) { for &rva in group { - // Store the index of this VM-block's VM-table entry - let index = vtable.len() / 8; + let index = table.len() / size_of::(); self.virtualized.insert(rva, index); - // Store placeholders for stub displacement and bytecode offset - vtable.extend_from_slice(&0u32.to_le_bytes()); - vtable.extend_from_slice(&0u32.to_le_bytes()); + table.extend_from_slice(&<[u8; size_of::()]>::from( + TableEntry::default(), + )); } } // Reserve a trampoline slot for each block too small for an inline stub for block in &engine.blocks { if self.virtualized.contains_key(&block.rva) && block.size < VM_DISPATCH_SIZE { - self.trampolines.insert(block.rva, self.trampolines.len()); + self.redirects.insert(block.rva, self.redirects.len()); } } engine.rt.define_data_bytes( DataDef::VmTrampolines, - &vec![0u8; self.trampolines.len() * VM_DISPATCH_SIZE], + &vec![0u8; self.redirects.len() * VM_REDIRECT_SIZE], ); - #[cfg(debug_assertions)] - { - use logger::debug; - - for (rva, log) in logs { + if engine.args.verbose { + for (rva, log) in log { debug!("VIRTUALIZED @ 0x{:08X}:\n{}", rva, log); } } - engine.rt.define_data_bytes(DataDef::VmTable, &vtable); + engine.rt.define_data_bytes(DataDef::VmTable, &table); - engine.rt.define_data_dword(DataDef::VmCode, 0); + engine.rt.define_data_dword(DataDef::VmCodeStart, 0); + engine.rt.define_data_dword(DataDef::VmCodeEnd, 0); engine.rt.define_data_dword(DataDef::VmAttestation, 0); engine .rt - .define_data_qword(DataDef::VmKeySeed, self.keys.seed); + .define_data_qword(DataDef::VmKeyInitializer, engine.rt.keys.initializer); engine .rt - .define_data_qword(DataDef::VmKeyMul, self.keys.mul); + .define_data_qword(DataDef::VmKeyMultiplier, engine.rt.keys.multiplier); engine .rt - .define_data_qword(DataDef::VmKeyAdd, self.keys.add); + .define_data_qword(DataDef::VmKeyAddend, engine.rt.keys.addend); } fn apply(&self, engine: &mut Engine) { - let attestation = self.attestation(engine); + let mut code = Vec::new(); + let mut offsets = vec![0u32; self.virtualized.len()]; - let mut vcode = Vec::new(); - vcode.extend_from_slice(&attestation); + for (bytes, group) in self.programs.iter().zip(&self.groups) { + let offset = TryInto::::try_into(code.len()).unwrap(); + code.extend_from_slice(bytes); - let vtable_rva = engine.rt.lookup(engine.rt.data_labels[&DataDef::VmTable]) as u32; - let vtable_offset = engine.pe.translate(RVA(vtable_rva).into()).unwrap(); - let vtable = unsafe { engine.pe.as_ptr().add(vtable_offset) as *mut u8 }; + for &rva in group { + offsets[self.virtualized[&rva]] = offset; + } + } - for (program, group) in self.programs.iter().zip(&self.groups) { - let mut bytes = bytecode::assemble(&mut engine.rt.mapper, program); + let attestation = self.attestation(engine, code.len() as u64); - let key = if vcode.len() == attestation.len() { - crypt::derive_hash(&vcode) - } else { - crypt::derive_key(&vcode) - }; + let cipher = Cipher::new(engine.rt.keys.multiplier, engine.rt.keys.addend); + + let secret = engine.rt.keys.secret; - crypt::encrypt_block(&mut bytes, key, self.keys.mul, self.keys.add, self.keys.att); + let mut key = crypt::derive_hash( + &attestation, + engine.rt.keys.multiplier, + engine.rt.keys.addend, + ); - let offset = TryInto::::try_into(vcode.len()).unwrap(); - vcode.extend_from_slice(&bytes); + let mut position = 0; - for &rva in group { - let index = self.virtualized[&rva]; - unsafe { - vtable - .add(index * 8 + size_of::()) - .copy_from(offset.to_le_bytes().as_ptr(), size_of::()); - } - } + for bytes in &self.programs { + let block = &mut code[position..position + bytes.len()]; + + cipher.encrypt_payload(block, key, secret); + + key = crypt::derive_key(block); + + position += bytes.len(); } + code.extend_from_slice(&attestation); + let section = engine.create_section( - Some("🏴‍☠️"), - &vcode, + Some("☠️"), + &code, SectionCharacteristics::CNT_INITIALIZED_DATA | SectionCharacteristics::MEM_READ | SectionCharacteristics::MEM_WRITE, ); + let base = section.virtual_address.0; + + write_displacement(engine, DataDef::VmCodeStart, base); + write_displacement(engine, DataDef::VmCodeEnd, base + code.len() as u32); + write_displacement( + engine, + DataDef::VmAttestation, + base + (code.len() - attestation.len()) as u32, + ); - let vtable_rva = engine.rt.lookup(engine.rt.data_labels[&DataDef::VmTable]) as u32; - let vtable_offset = engine.pe.translate(RVA(vtable_rva).into()).unwrap(); - let vtable = unsafe { engine.pe.as_ptr().add(vtable_offset) as *mut u8 }; - - let vcode_rva = engine.rt.lookup(engine.rt.data_labels[&DataDef::VmCode]) as u32; - let vcode_offset = engine.pe.translate(RVA(vcode_rva).into()).unwrap(); - let vcode_displacement = (section.virtual_address.0 as i64 - vcode_rva as i64) as i32; - engine - .pe - .write(vcode_offset, &vcode_displacement.to_le_bytes()) - .unwrap(); - - let vattestation_rva = engine - .rt - .lookup(engine.rt.data_labels[&DataDef::VmAttestation]) - as u32; - let vattestation_offset = engine.pe.translate(RVA(vattestation_rva).into()).unwrap(); - let vattestation_displacement = ((section.virtual_address.0 as i64 - + attestation.len() as i64) - - vattestation_rva as i64) as i32; - engine - .pe - .write( - vattestation_offset, - &vattestation_displacement.to_le_bytes(), - ) - .unwrap(); - - let ventry_rva = engine.rt.lookup(engine.rt.function_labels[&FnDef::VmEntry]); + let entry_rva = engine.rt.lookup(engine.rt.function_labels[&FnDef::VmEntry]); - let trampolines_rva = engine + let redirects_rva = engine .rt .lookup(engine.rt.data_labels[&DataDef::VmTrampolines]) as u32; - let trampolines_offset = engine.pe.translate(RVA(trampolines_rva).into()).unwrap(); - let trampolines = unsafe { engine.pe.as_ptr().add(trampolines_offset) as *mut u8 }; + + let multiplier = engine.rt.keys.multiplier; + let addend = engine.rt.keys.addend; for i in 0..engine.blocks.len() { let rva = engine.blocks[i].rva; @@ -325,73 +353,61 @@ impl Protection for Virtualization { continue; } - let vtable_index = self.virtualized[&rva]; + let index = self.virtualized[&rva]; - if size >= VM_DISPATCH_SIZE { - let mut asm = CodeAssembler::new(engine.bitness).unwrap(); + let token = ((index as u64).wrapping_mul(multiplier).wrapping_add(addend) & 0x0FFFFFFF) + as i32 + | 0x10000000; - asm.push(vtable_index as i32 | 0x10000000).unwrap(); - asm.call(ventry_rva).unwrap(); - let dispatch1 = asm.assemble(rva as u64).unwrap(); + let mut asm = CodeAssembler::new(engine.bitness).unwrap(); + + if size >= VM_DISPATCH_SIZE { + asm.push(token).unwrap(); + asm.call(entry_rva).unwrap(); + let first = asm.assemble(rva as u64).unwrap(); - assert!(dispatch1.len() <= VM_DISPATCH_SIZE); + assert!(first.len() <= VM_DISPATCH_SIZE); asm.reset(); // Stub has to be assembled twice so that the runtime return address can be calculated - let return_address = rva as i32 + dispatch1.len() as i32; + let return_address = rva as i32 + first.len() as i32; - asm.push((vtable_index as i32 | 0x10000000) ^ return_address) - .unwrap(); - asm.call(ventry_rva).unwrap(); + asm.push(token ^ return_address).unwrap(); + asm.call(entry_rva).unwrap(); + let second = asm.assemble(rva as u64).unwrap(); - let dispatch2 = asm.assemble(rva as u64).unwrap(); + assert_eq!(first.len(), second.len()); - assert_eq!(dispatch1.len(), dispatch2.len()); + write_entry( + engine, + index, + TableEntry::new(rva, size, return_address, offsets[index]), + ); - // Patch the stub displacement placeholder in the VM-table - unsafe { - let displacement = (size - dispatch2.len()) as u32; - vtable - .add(vtable_index * 8) - .copy_from(displacement.to_le_bytes().as_ptr(), size_of::()); - } - - engine.replace(i, &dispatch2); + engine.replace(i, &second); } else { - let trampoline = self.trampolines[&rva]; - let trampoline_rva = trampolines_rva + (trampoline * VM_DISPATCH_SIZE) as u32; - - let mut asm = CodeAssembler::new(engine.bitness).unwrap(); - - let return_address = trampoline_rva + VM_DISPATCH_SIZE as u32; + let redirect = self.redirects[&rva]; + let redirect_rva = redirects_rva + (redirect * VM_REDIRECT_SIZE) as u32; - asm.push((vtable_index as i32 | 0x10000000) ^ return_address as i32) - .unwrap(); - asm.call(ventry_rva).unwrap(); - let dispatch = asm.assemble(trampoline_rva as u64).unwrap(); + let return_address = (redirect_rva + VM_REDIRECT_SIZE as u32) as i32; - assert_eq!(dispatch.len(), VM_DISPATCH_SIZE); + asm.mov(dword_ptr(rsp), token ^ return_address).unwrap(); + asm.call(entry_rva).unwrap(); + let dispatch = asm.assemble(redirect_rva as u64).unwrap(); - unsafe { - trampolines - .add(trampoline * VM_DISPATCH_SIZE) - .copy_from(dispatch.as_ptr(), VM_DISPATCH_SIZE); - } + assert_eq!(dispatch.len(), VM_REDIRECT_SIZE); - // Patch the stub displacement placeholder in the VM-table to redirect to original block - unsafe { - let displacement = (rva as i64 + size as i64 - return_address as i64) as i32; - vtable.add(vtable_index * 8).copy_from( - (displacement as u32).to_le_bytes().as_ptr(), - size_of::(), - ); - } + write_trampoline(engine, redirect, &dispatch); + write_entry( + engine, + index, + TableEntry::new(rva, size, return_address, offsets[index]), + ); asm.reset(); - asm.jmp(trampoline_rva as u64).unwrap(); - + asm.call(redirect_rva as u64).unwrap(); let branch = asm.assemble(rva as u64).unwrap(); assert!(branch.len() <= size); diff --git a/runtime/src/debug.rs b/runtime/src/debug.rs index 21a78b57..5d06c397 100644 --- a/runtime/src/debug.rs +++ b/runtime/src/debug.rs @@ -328,8 +328,6 @@ pub fn print_thread_message( // pop rbp rt.asm.pop(rbp).unwrap(); - lock::release_debug(rt); - restore(rt); } @@ -363,77 +361,46 @@ fn load(rt: &mut Runtime, dst: AsmRegister64) { rt.asm.mov(ptr(0x1480 + r10 * 8).gs(), r11).unwrap(); } -pub fn start_profiling(rt: &mut Runtime, _message: &str) { - // pushfq +pub fn start_profiling(rt: &mut Runtime) { rt.asm.pushfq().unwrap(); - // push rdx rt.asm.push(rdx).unwrap(); - // push rax rt.asm.push(rax).unwrap(); - - // push r10 rt.asm.push(r10).unwrap(); - // push r11 rt.asm.push(r11).unwrap(); - // rdtsc rt.asm.rdtsc().unwrap(); - // shl rdx, 0x20 rt.asm.shl(rdx, 0x20).unwrap(); - // or rdx, rax rt.asm.or(rdx, rax).unwrap(); store(rt, rdx); - // pop r11 rt.asm.pop(r11).unwrap(); - // pop r10 rt.asm.pop(r10).unwrap(); - - // pop rax rt.asm.pop(rax).unwrap(); - // pop rdx rt.asm.pop(rdx).unwrap(); - // popfq rt.asm.popfq().unwrap(); } -pub fn stop_profiling(rt: &mut Runtime, message: &str) { - // pushfq +pub fn stop_profiling(rt: &mut Runtime, def: FnDef, message: &str) { rt.asm.pushfq().unwrap(); - // push rdx rt.asm.push(rdx).unwrap(); - // push rax rt.asm.push(rax).unwrap(); - - // push r10 rt.asm.push(r10).unwrap(); - // push r11 rt.asm.push(r11).unwrap(); - // rdtsc rt.asm.rdtsc().unwrap(); - // shl rdx, 0x20 rt.asm.shl(rdx, 0x20).unwrap(); - // or rdx, rax rt.asm.or(rdx, rax).unwrap(); load(rt, rax); - // sub rdx, rax rt.asm.sub(rdx, rax).unwrap(); - print_thread_message(rt, &format!("cycles for {}", message), Some(rdx), None); + print_thread_message(rt, &format!("{:?} - {}", def, message), Some(rdx), None); - // pop r11 rt.asm.pop(r11).unwrap(); - // pop r10 rt.asm.pop(r10).unwrap(); - - // pop rax rt.asm.pop(rax).unwrap(); - // pop rdx rt.asm.pop(rdx).unwrap(); - // popfq rt.asm.popfq().unwrap(); } diff --git a/runtime/src/functions/format.rs b/runtime/src/functions/format.rs index 0812d87c..89f1ae88 100644 --- a/runtime/src/functions/format.rs +++ b/runtime/src/functions/format.rs @@ -46,6 +46,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.jge(convert_loop).unwrap(); } } + // mov [rax], 0x0 rt.asm.mov(byte_ptr(rax), 0x0).unwrap(); // ret diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 874892ab..1f066667 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -2,8 +2,8 @@ pub mod debug; pub mod functions; pub mod mapper; -mod obfuscation; pub mod runtime; +pub mod utils; pub mod vm; macro_rules! stack { @@ -23,7 +23,12 @@ pub const VM_DEBUG_SIZE: u64 = 0x100; // PUSH imm32 + CALL rel32 pub const VM_DISPATCH_SIZE: usize = 10; -// JMP rel32 +// MOV dword [rsp], imm32 + CALL rel32 +pub const VM_REDIRECT_SIZE: usize = 12; + +// CALL rel32 pub const VM_TRAMPOLINE_SIZE: usize = 5; -pub const VM_INTEGRITY_QWORD: u64 = 0xFA11ED175001FA11; +pub const VM_INTEGRITY_QWORD: u64 = 0xF4113D17501F411; + +pub const VM_CIPHER_ROUNDS: u32 = 27; diff --git a/runtime/src/mapper.rs b/runtime/src/mapper.rs index 2d1a476a..46e4b31d 100644 --- a/runtime/src/mapper.rs +++ b/runtime/src/mapper.rs @@ -138,4 +138,5 @@ macro_rules! mapped { } }; } + pub(crate) use mapped; diff --git a/runtime/src/obfuscation/immediate.rs b/runtime/src/obfuscation/immediate.rs deleted file mode 100644 index 0fd9d977..00000000 --- a/runtime/src/obfuscation/immediate.rs +++ /dev/null @@ -1,362 +0,0 @@ -use std::collections::HashSet; - -use iced_x86::{ - code_asm::{get_gpr16, get_gpr32, get_gpr64, get_gpr8, AsmRegister8, CodeAssembler}, - Code, FlowControl, Instruction, InstructionInfoFactory, OpAccess, OpCodeOperandKind, OpKind, - Register, RflagsBits, -}; -use rand::{seq::SliceRandom, Rng}; - -use crate::obfuscation::{flags, scratch, sized, Operation}; - -#[derive(Clone, Copy)] -enum Condition { - Carry, - Overflow, - Sign, - Zero, - Parity, -} - -pub fn obfuscate( - instructions: &[Instruction], - asm: &mut CodeAssembler, - rng: &mut R, -) -> Option { - let instruction = &instructions[0]; - - let mut factory = InstructionInfoFactory::new(); - let info = factory.info(instruction); - - let registers = info - .used_registers() - .iter() - .map(|r| r.register().full_register()) - .collect::>(); - - if registers.contains(&Register::RSP) { - return None; - } - - let count = instruction.op_count(); - - let immediate = (0..count).find(|&i| is_immediate(instruction.op_kind(i))); - let memory = (0..count).find(|&i| instruction.op_kind(i) == OpKind::Memory); - - let (value, size, memory) = if let Some(i) = immediate { - if !matches!(instruction.op0_kind(), OpKind::Register) { - return None; - } - - ( - instruction.immediate(i), - instruction.op_register(0).size(), - false, - ) - } else if memory.is_some() { - if instruction.memory_base() == Register::None || instruction.memory_base() == Register::RIP - { - return None; - } - - if instruction.memory_index() != Register::None { - return None; - } - - let displacement = instruction.memory_displacement64(); - - let size = if displacement <= 0xFF { - 1 - } else if displacement <= 0xFFFF { - 2 - } else if displacement <= 0xFFFF_FFFF { - 4 - } else { - 8 - }; - - (displacement, size, true) - } else { - return None; - }; - - let mut excluded = registers.clone(); - excluded.push(Register::RSP); - - let (register1, preserve1) = match dead_register(instructions, &excluded) { - Some(register) => (register, false), - None => (scratch(&excluded, rng), true), - }; - - excluded.push(register1); - - let (register2, preserve2) = if size == 8 { - match dead_register(instructions, &excluded) { - Some(register) => (register, false), - None => (scratch(&excluded, rng), true), - } - } else { - (register1, false) - }; - - let rewritten = if memory { - rewrite_memory(instruction, register1)? - } else { - rewrite_immediate(instruction, register1)? - }; - - let reads = instruction.rflags_read() != RflagsBits::NONE; - let writes = instruction.rflags_written() != RflagsBits::NONE; - let dead = dead_flags(instructions); - - if preserve1 { - asm.push(get_gpr64(register1).unwrap()).unwrap(); - } - - if preserve2 { - asm.push(get_gpr64(register2).unwrap()).unwrap(); - } - - if !writes && !dead { - asm.pushfq().unwrap(); - } - - if reads { - asm.pushfq().unwrap(); - } - - reconstruct(value, size, register1, register2, asm, rng); - - if reads { - asm.popfq().unwrap(); - } - - asm.add_instruction(rewritten).unwrap(); - - if !writes && !dead { - asm.popfq().unwrap(); - } - - if preserve2 { - asm.pop(get_gpr64(register2).unwrap()).unwrap(); - } - - if preserve1 { - asm.pop(get_gpr64(register1).unwrap()).unwrap(); - } - - Some(1) -} - -fn dead_register(instructions: &[Instruction], excluded: &[Register]) -> Option { - let mut factory = InstructionInfoFactory::new(); - let mut reads = HashSet::new(); - - for instruction in instructions.iter().skip(1) { - if instruction.flow_control() != FlowControl::Next { - break; - } - - let info = factory.info(instruction); - - for register in info.used_registers() { - let access = register.access(); - - let register = register.register().full_register(); - - if !register.is_gpr() { - continue; - } - - if excluded.contains(®ister) { - continue; - } - - match access { - OpAccess::Read - | OpAccess::CondRead - | OpAccess::ReadWrite - | OpAccess::ReadCondWrite => { - reads.insert(register); - } - OpAccess::Write => { - if !reads.contains(®ister) { - return Some(register); - } - } - _ => {} - } - } - } - - None -} - -fn dead_flags(instructions: &[Instruction]) -> bool { - // This is a potentially unsafe implementation which assumes that after branches the flags are dead. - for instruction in instructions.iter().skip(1) { - if instruction.rflags_read() != RflagsBits::NONE { - return false; - } - - if instruction.rflags_modified() != RflagsBits::NONE { - return true; - } - } - true -} - -fn rewrite_immediate(instruction: &Instruction, scratch: Register) -> Option { - let mnemonic = instruction.mnemonic(); - let count = instruction.op_count(); - let index = (0..count).find(|&i| is_immediate(instruction.op_kind(i)))?; - let destination = instruction.op_register(0); - - let kind = match destination.size() { - 1 => OpCodeOperandKind::r8_reg, - 2 => OpCodeOperandKind::r16_reg, - 4 => OpCodeOperandKind::r32_reg, - 8 => OpCodeOperandKind::r64_reg, - _ => return None, - }; - - let code = Code::values().find(|&c| { - let info = c.op_code(); - - info.mnemonic() == mnemonic && info.op_count() == count && info.op_kind(index) == kind - })?; - - let scratch = sized(scratch, destination.size())?; - - Instruction::with2(code, destination, scratch).ok() -} - -fn rewrite_memory(instruction: &Instruction, scratch: Register) -> Option { - let mut rewritten = instruction.clone(); - - rewritten.set_memory_index(sized(scratch, 8)?); - rewritten.set_memory_index_scale(1); - rewritten.set_memory_displacement64(0); - rewritten.set_memory_displ_size(0); - - Some(rewritten) -} - -fn is_immediate(kind: OpKind) -> bool { - matches!( - kind, - OpKind::Immediate8 - | OpKind::Immediate16 - | OpKind::Immediate32 - | OpKind::Immediate64 - | OpKind::Immediate8to16 - | OpKind::Immediate8to32 - | OpKind::Immediate8to64 - | OpKind::Immediate32to64 - ) -} - -fn setcc(assembler: &mut CodeAssembler, destination: AsmRegister8, condition: Condition) { - match condition { - Condition::Carry => assembler.setc(destination), - Condition::Overflow => assembler.seto(destination), - Condition::Sign => assembler.sets(destination), - Condition::Zero => assembler.setz(destination), - Condition::Parity => assembler.setp(destination), - } - .unwrap(); -} - -fn reconstruct( - immediate: u64, - size: usize, - register1: Register, - register2: Register, - assembler: &mut CodeAssembler, - rng: &mut impl Rng, -) { - let r1_8 = get_gpr8(sized(register1, 1).unwrap()).unwrap(); - let r1_16 = get_gpr16(sized(register1, 2).unwrap()).unwrap(); - let r1_32 = get_gpr32(sized(register1, 4).unwrap()).unwrap(); - let r1_64 = get_gpr64(sized(register1, 8).unwrap()).unwrap(); - - let r2_64 = get_gpr64(sized(register2, 8).unwrap()).unwrap(); - - let operation = Operation::random(rng); - - let limit = match size { - 1 => 0xFF, - 2 => 0xFFFF, - 4 => 0xFFFF_FFFF, - 8 => u64::MAX, - _ => unreachable!(), - }; - - let first = rng.gen::() as u64; - let second = rng.gen::() as u64; - - let flags = flags(operation, first, second, 4); - - let options = [ - (Condition::Carry, flags.carry), - (Condition::Overflow, flags.overflow), - (Condition::Sign, flags.sign), - (Condition::Zero, flags.zero), - (Condition::Parity, flags.parity), - ]; - - let &(condition, flag) = options.choose(rng).unwrap(); - - let arithmetic = match operation { - Operation::Add => first.wrapping_add(second) & 0xFFFF_FFFF, - Operation::Sub => first.wrapping_sub(second) & 0xFFFF_FFFF, - Operation::Xor => (first ^ second) & 0xFFFF_FFFF, - }; - - let shift = rng.gen_range(0..16u32); - - let state = ((arithmetic as u16 as u64) | ((flag as u64) << shift)) - .wrapping_mul((arithmetic as u16 as u64) | ((flag as u64) << shift)); - - assembler.mov(r1_32, first as i32).unwrap(); - - match operation { - Operation::Add => assembler.add(r1_32, second as i32), - Operation::Sub => assembler.sub(r1_32, second as i32), - Operation::Xor => assembler.xor(r1_32, second as i32), - } - .unwrap(); - - setcc(assembler, r1_8, condition); - assembler.movzx(r1_32, r1_8).unwrap(); - - if shift > 0 { - assembler.shl(r1_16, shift as i32).unwrap(); - } - - assembler.or(r1_16, arithmetic as u16 as i32).unwrap(); - - if size == 8 { - assembler.imul_2(r1_64, r1_64).unwrap(); - - let xor1 = rng.gen::(); - let noise = rng.gen::(); - let xor2 = immediate ^ state ^ xor1 ^ noise; - let xor3 = noise; - - assembler.mov(r2_64, xor1 as i64).unwrap(); - assembler.xor(r1_64, r2_64).unwrap(); - assembler.mov(r2_64, xor2 as i64).unwrap(); - assembler.xor(r1_64, r2_64).unwrap(); - assembler.mov(r2_64, xor3 as i64).unwrap(); - assembler.xor(r1_64, r2_64).unwrap(); - } else { - assembler.imul_2(r1_32, r1_32).unwrap(); - - let xor1 = rng.gen::(); - let xor2 = ((immediate & limit) as u32) ^ (state as u32) ^ xor1; - - assembler.xor(r1_32, xor1 as i32).unwrap(); - assembler.xor(r1_32, xor2 as i32).unwrap(); - } -} diff --git a/runtime/src/obfuscation/mod.rs b/runtime/src/obfuscation/mod.rs deleted file mode 100644 index 14ca5bb6..00000000 --- a/runtime/src/obfuscation/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -use iced_x86::{ - code_asm::{CodeAssembler, CodeAssemblerResult}, - BlockEncoderOptions, Instruction, Register, -}; -use rand::{seq::SliceRandom, Rng}; - -mod immediate; - -#[derive(Debug, Clone, Copy)] -pub enum Operation { - Add, - Sub, - Xor, -} - -impl Operation { - pub fn random(rng: &mut impl Rng) -> Self { - match rng.gen_range(0..3) { - 0 => Operation::Add, - 1 => Operation::Sub, - _ => Operation::Xor, - } - } -} - -pub struct Flags { - pub carry: u64, - pub overflow: u64, - pub sign: u64, - pub zero: u64, - pub parity: u64, -} - -pub fn obfuscate(asm: &mut CodeAssembler, ip: u64) -> (Vec, CodeAssemblerResult) { - let mut rng = rand::thread_rng(); - - let mut instructions = asm.take_instructions(); - - let mut chunks = Vec::with_capacity(instructions.len()); - - let mut i = instructions.len(); - - while i > 0 { - i -= 1; - - let mut asm = CodeAssembler::new(64).unwrap(); - - let ip = instructions[i].ip(); - instructions[i].set_ip(0); - - let consumed = - None.or_else(|| immediate::obfuscate(&instructions[i..], &mut asm, &mut rng)); - - if let Some(consumed) = consumed { - let mut chunk = asm.take_instructions(); - - if let Some(instruction) = chunk.first_mut() { - instruction.set_ip(ip); - } - - chunks.push((i, chunk)); - - i = i.saturating_sub(consumed - 1); - } else { - let mut instruction = instructions[i].clone(); - instruction.set_ip(ip); - chunks.push((i, vec![instruction])); - } - } - - chunks.reverse(); - - let mut obfuscated = Vec::new(); - let mut map = vec![0; instructions.len()]; - - for (i, chunk) in chunks { - map[i] = obfuscated.len(); - obfuscated.extend(chunk); - } - - let mut asm = CodeAssembler::new(64).unwrap(); - - for &instruction in &obfuscated { - asm.add_instruction(instruction).unwrap(); - } - - let mut result = asm - .assemble_options(ip, BlockEncoderOptions::RETURN_NEW_INSTRUCTION_OFFSETS) - .unwrap(); - - let offsets = result.inner.new_instruction_offsets.clone(); - - result.inner.new_instruction_offsets = map.iter().map(|&slot| offsets[slot]).collect(); - - (obfuscated, result) -} - -pub fn flags(operation: Operation, left: u64, right: u64, size: usize) -> Flags { - let mask = match size { - 1 => 0xFF, - 2 => 0xFFFF, - 4 => 0xFFFF_FFFF, - 8 => u64::MAX, - _ => unreachable!(), - }; - - let left = left & mask; - let right = right & mask; - - let result = match operation { - Operation::Add => left.wrapping_add(right) & mask, - Operation::Sub => left.wrapping_sub(right) & mask, - Operation::Xor => left ^ right, - }; - - let bit = size * 8 - 1; - - let (carry, overflow) = match operation { - Operation::Xor => (0, 0), - Operation::Add => ( - ((left as u128 + right as u128) > mask as u128) as u64, - ((!(left ^ right) & (left ^ result)) >> bit) & 1, - ), - Operation::Sub => ( - (left < right) as u64, - (((left ^ right) & (left ^ result)) >> bit) & 1, - ), - }; - - Flags { - carry, - overflow, - sign: (result >> bit) & 1, - zero: (result == 0) as u64, - parity: ((result as u8).count_ones() % 2 == 0) as u64, - } -} - -fn sized(register: Register, size: usize) -> Option { - let number = register.number() as usize; - - match size { - 1 => match number { - 0 => Some(Register::AL), - 1 => Some(Register::CL), - 2 => Some(Register::DL), - 3 => Some(Register::BL), - 4 => Some(Register::SPL), - 5 => Some(Register::BPL), - 6 => Some(Register::SIL), - 7 => Some(Register::DIL), - 8 => Some(Register::R8L), - 9 => Some(Register::R9L), - 10 => Some(Register::R10L), - 11 => Some(Register::R11L), - 12 => Some(Register::R12L), - 13 => Some(Register::R13L), - 14 => Some(Register::R14L), - 15 => Some(Register::R15L), - _ => None, - }, - 2 => { - let base = register.full_register().number() as u32; - Some(Register::AX + base) - } - 4 => { - let base = register.full_register().number() as u32; - Some(Register::EAX + base) - } - 8 => { - let base = register.full_register().number() as u32; - Some(Register::RAX + base) - } - _ => None, - } -} - -pub fn scratch(excluded: &[Register], rng: &mut impl Rng) -> Register { - const REGISTERS: [Register; 12] = [ - Register::RAX, - Register::RCX, - Register::RDX, - Register::RSI, - Register::RDI, - Register::R8, - Register::R9, - Register::R11, - Register::R12, - Register::R13, - Register::R14, - Register::R15, - ]; - - let excluded = excluded - .iter() - .map(|r| r.full_register()) - .collect::>(); - - let registers = REGISTERS - .iter() - .cloned() - .filter(|r| !excluded.contains(r)) - .collect::>(); - - *registers.choose(rng).unwrap() -} diff --git a/runtime/src/runtime.rs b/runtime/src/runtime.rs index ee8296b6..57d383a6 100644 --- a/runtime/src/runtime.rs +++ b/runtime/src/runtime.rs @@ -1,12 +1,14 @@ use std::collections::HashMap; -use iced_x86::code_asm::{ptr, r10, r11, rcx, AsmRegister64, CodeAssembler, CodeLabel}; +use iced_x86::{ + code_asm::{ptr, r10, r11, rcx, AsmRegister64, CodeAssembler, CodeLabel}, + BlockEncoderOptions, +}; use rand::{seq::SliceRandom, Rng}; use crate::{ functions, mapper::{mapped, Mappable, Mapper}, - obfuscation, vm::{ self, bytecode::{VMReg, VMVec}, @@ -36,7 +38,6 @@ mapped! { VmVectorsCapture, VmVectorsRestore, VmVectorsCopy, - VmFlags, /* VM HANDLERS */ VmHandlerJcc, VmHandlerRet, @@ -51,6 +52,8 @@ mapped! { VmHandlerStoreExtend, VmHandlerAdd, VmHandlerSub, + VmHandlerAdc, + VmHandlerSbb, VmHandlerAnd, VmHandlerOr, VmHandlerXor, @@ -85,6 +88,7 @@ mapped! { VmHandlerVectorMul, VmHandlerVectorDiv, VmHandlerTimestamp, + VmHandlerDispatch, /* VM VEH */ VmVehInitialize, VmVehHandler, @@ -111,12 +115,14 @@ mapped! { VmDebugTlsIndex, VmCleanupFlsIndex, VmTable, - VmCode, + VmCodeStart, + VmCodeEnd, VmAttestation, VmTrampolines, - VmKeySeed, - VmKeyMul, - VmKeyAdd, + VmKeyInitializer, + VmKeyMultiplier, + VmKeyMultiplierInverse, + VmKeyAddend, VehEnd, ImportAddresses, ImportNames, @@ -237,6 +243,14 @@ enum EmissionTask { DispatchStub(usize, usize), } +pub type Handler = Box; + +struct DispatchCase { + label: CodeLabel, + epilogue: CodeLabel, + handler: Handler, +} + struct Dispatch { table: CodeLabel, stubs: Vec<(u8, CodeLabel, CodeLabel)>, @@ -253,11 +267,33 @@ impl Dispatch { } } +pub struct Keys { + pub initializer: u64, + pub multiplier: u64, + pub addend: u64, + pub secret: u64, +} + +impl Default for Keys { + fn default() -> Self { + let mut rng = rand::thread_rng(); + + Self { + initializer: rng.gen::(), + multiplier: rng.gen::() | 1, + addend: rng.gen::(), + secret: rng.gen::(), + } + } +} + pub struct Runtime { pub asm: CodeAssembler, pub nonce: u64, + pub keys: Keys, + pub function_labels: HashMap, pub data_labels: HashMap, pub bool_labels: HashMap, @@ -273,6 +309,8 @@ pub struct Runtime { dispatches: Vec, + cases: Vec, + functions: HashMap, addresses: HashMap, @@ -328,6 +366,8 @@ impl Runtime { nonce, + keys: Keys::default(), + function_labels, data_labels, bool_labels, @@ -343,6 +383,8 @@ impl Runtime { dispatches: Vec::new(), + cases: Vec::new(), + functions: HashMap::new(), addresses: HashMap::new(), @@ -450,6 +492,31 @@ impl Runtime { .unwrap(); } + pub fn switch( + &mut self, + key: AsmRegister64, + epilogue: CodeLabel, + handlers: Vec<(Vec, Handler)>, + ) { + let mut cases = Vec::new(); + + for (indices, handler) in handlers { + let label = self.asm.create_label(); + + for index in indices { + cases.push((index, label)); + } + + self.cases.push(DispatchCase { + label, + epilogue, + handler, + }); + } + + self.dispatch(key, cases); + } + pub fn jumps(&mut self, key: AsmRegister64, cases: Vec<(u8, CodeLabel)>) { self.dispatch(key, cases); } @@ -612,6 +679,8 @@ impl Runtime { ), (FnDef::VmHandlerAdd, vm::handlers::add::build), (FnDef::VmHandlerSub, vm::handlers::sub::build), + (FnDef::VmHandlerAdc, vm::handlers::adc::build), + (FnDef::VmHandlerSbb, vm::handlers::sbb::build), (FnDef::VmHandlerAnd, vm::handlers::and::build), (FnDef::VmHandlerOr, vm::handlers::or::build), (FnDef::VmHandlerXor, vm::handlers::xor::build), @@ -676,7 +745,7 @@ impl Runtime { (FnDef::VmHandlerVectorMul, vm::handlers::vector_mul::build), (FnDef::VmHandlerVectorDiv, vm::handlers::vector_div::build), (FnDef::VmHandlerTimestamp, vm::handlers::timestamp::build), - (FnDef::VmFlags, vm::handlers::flags::build), + (FnDef::VmHandlerDispatch, vm::handlers::dispatch::build), (FnDef::VmVehInitialize, vm::functions::veh::initialize), (FnDef::Hash, functions::hash::build), (FnDef::Resolve, functions::resolve::build), @@ -705,6 +774,14 @@ impl Runtime { self.define_data_bytes(DataDef::Functions, &vec![0u8; FnDef::COUNT * 8]); + self.define_data_qword(DataDef::VmKeyInitializer, self.keys.initializer); + self.define_data_qword(DataDef::VmKeyAddend, self.keys.addend); + self.define_data_qword(DataDef::VmKeyMultiplier, self.keys.multiplier); + self.define_data_qword( + DataDef::VmKeyMultiplierInverse, + crate::utils::invert_multiplier(self.keys.multiplier), + ); + self.define_bool(BoolDef::IsLocked, false); self.define_bool(BoolDef::HasAvx, false); self.define_bool(BoolDef::HasVeh, false); @@ -806,22 +883,50 @@ impl Runtime { let mut phase_two = Vec::new(); - for i in 0..self.dispatches.len() { - phase_two.push(EmissionTask::DispatchTable(i)); + phase_two.extend(data_phase_two.iter().cloned()); + + // A case handler may register further dispatches (nested width/precision + // dispatch), so drain in shuffled waves until no new work remains. + let mut dispatched = 0; - for j in 0..self.dispatches[i].stubs.len() { - phase_two.push(EmissionTask::DispatchStub(i, j)); + loop { + for i in dispatched..self.dispatches.len() { + phase_two.push(EmissionTask::DispatchTable(i)); + + for j in 0..self.dispatches[i].stubs.len() { + phase_two.push(EmissionTask::DispatchStub(i, j)); + } } - } + dispatched = self.dispatches.len(); - phase_two.extend(data_phase_two.iter().cloned()); - phase_two.shuffle(&mut rng); + let mut cases = std::mem::take(&mut self.cases); + + if phase_two.is_empty() && cases.is_empty() { + break; + } + + phase_two.shuffle(&mut rng); + self.emit(&phase_two); + phase_two = Vec::new(); - self.emit(&phase_two); + cases.shuffle(&mut rng); + + for case in cases { + let mut label = case.label; + + self.asm.set_label(&mut label).unwrap(); + (case.handler)(self); + // jmp ... + self.asm.jmp(case.epilogue).unwrap(); + } + } self.emit(&[EmissionTask::Data(DataDef::VehEnd)]); - let (instructions, result) = obfuscation::obfuscate(&mut self.asm, ip); + let result = self + .asm + .assemble_options(ip, BlockEncoderOptions::RETURN_NEW_INSTRUCTION_OFFSETS) + .unwrap(); let labels = self .function_labels @@ -838,11 +943,11 @@ impl Runtime { let offsets = &result.inner.new_instruction_offsets; - for (def, (_, end)) in &self.functions { + for (def, &(_, end)) in &self.functions { let label = self.function_labels[def]; let start = result.label_ip(&label).unwrap(); let offset = offsets[end - 1] as u64; - let size = instructions[end - 1].len() as u64; + let size = (offsets[end] - offsets[end - 1]) as u64; self.sizes.insert(label, ip + offset + size - start); } diff --git a/runtime/src/utils.rs b/runtime/src/utils.rs new file mode 100644 index 00000000..2fd24cb0 --- /dev/null +++ b/runtime/src/utils.rs @@ -0,0 +1,52 @@ +use iced_x86::Register; + +/// Computes the multiplicative inverse of odd `x` modulo 2^64. +pub fn invert_multiplier(x: u64) -> u64 { + let mut inverse = x; + + for _ in 0..5 { + inverse = inverse.wrapping_mul(2u64.wrapping_sub(x.wrapping_mul(inverse))); + } + + inverse +} + +// Converts `register` to the same register of `size`. +pub fn register_of_size(register: Register, size: usize) -> Option { + let number = register.number() as usize; + + match size { + 1 => match number { + 0 => Some(Register::AL), + 1 => Some(Register::CL), + 2 => Some(Register::DL), + 3 => Some(Register::BL), + 4 => Some(Register::SPL), + 5 => Some(Register::BPL), + 6 => Some(Register::SIL), + 7 => Some(Register::DIL), + 8 => Some(Register::R8L), + 9 => Some(Register::R9L), + 10 => Some(Register::R10L), + 11 => Some(Register::R11L), + 12 => Some(Register::R12L), + 13 => Some(Register::R13L), + 14 => Some(Register::R14L), + 15 => Some(Register::R15L), + _ => None, + }, + 2 => { + let base = register.full_register().number() as u32; + Some(Register::AX + base) + } + 4 => { + let base = register.full_register().number() as u32; + Some(Register::EAX + base) + } + 8 => { + let base = register.full_register().number() as u32; + Some(Register::RAX + base) + } + _ => None, + } +} diff --git a/runtime/src/vm/bytecode.rs b/runtime/src/vm/bytecode.rs index 6685e3f7..56fde05d 100644 --- a/runtime/src/vm/bytecode.rs +++ b/runtime/src/vm/bytecode.rs @@ -1,21 +1,22 @@ -use core::panic; -use std::any::Any; - -use iced_x86::{Instruction, Mnemonic, Register}; -use strum_macros::EnumIter; - use crate::mapper::{mapped, Mapper}; use crate::vm::encoders::Encode; use crate::vm::lifters::{ arithmetic, branch, bsr, bswap, bt, cmov, cmpxchg, div, extend, integer, lea, multiply, pcmpeqb, pmovskb, rdtsc, scalar, set, stack, transfer, tzcnt, xadd, xchg, }; -#[cfg(debug_assertions)] use crate::vm::snapshot::Snapshots; use crate::vm::transform::encrypt::Encrypt; +use crate::vm::transform::indirect::Indirect; use crate::vm::transform::mutation::Mutation; use crate::vm::transform::peephole::Peephole; -use crate::vm::transform::{permute, scramble, Transform}; +use crate::vm::transform::permute::Permute; +use crate::vm::transform::scramble::Scramble; +use crate::vm::transform::Transform; +use core::panic; +use iced_x86::{Instruction, Mnemonic, Register}; +use std::any::Any; +use std::cell::RefCell; +use strum_macros::EnumIter; mapped! { VMOp { @@ -35,6 +36,8 @@ mapped! { // Arithmetic Add, Sub, + Adc, + Sbb, And, Or, Xor, @@ -72,7 +75,8 @@ mapped! { VectorMul, VectorDiv, // Special - Timestamp + Timestamp, + Dispatch } } @@ -269,7 +273,17 @@ impl VMWidth { VMWidth::Lower16 | VMWidth::SLower16 => VMWidth::SLower16, VMWidth::Lower32 | VMWidth::SLower32 => VMWidth::SLower32, VMWidth::Lower64 | VMWidth::SLower64 => VMWidth::SLower64, - other => other, + _ => unreachable!(), + } + } + + pub fn mask(self) -> u64 { + let bits = (self.size() * 8).min(64); + + if bits == 64 { + u64::MAX + } else { + (1u64 << bits) - 1 } } } @@ -332,6 +346,10 @@ impl Encode for VMMem { self } + fn op(&self) -> Option { + None + } + fn encode(&self, mapper: &mut Mapper) -> Vec { let mut bytes = Vec::new(); bytes.push(mapper.index(self.base)); @@ -429,6 +447,10 @@ impl Encode for VMCondition { self } + fn op(&self) -> Option { + None + } + fn encode(&self, mapper: &mut Mapper) -> Vec { vec![mapper.index(self.test), self.lhs, self.rhs] } @@ -442,6 +464,7 @@ pub enum Phase { Scramble, Encrypt, Peephole, + Indirect, } impl Phase { @@ -453,6 +476,7 @@ impl Phase { Self::Scramble => "scramble", Self::Encrypt => "encrypt", Self::Peephole => "peephole", + Self::Indirect => "indirect", } } } @@ -618,66 +642,67 @@ pub fn assemble(mapper: &mut Mapper, operations: &[Box]) -> Vec let mut bytes = Vec::new(); for operation in operations { + if let Some(op) = operation.op() { + bytes.push(mapper.index(op)); + } bytes.extend(operation.encode(mapper)); } bytes } +fn transforms<'a>( + picker: &'a RefCell<&'a mut dyn FnMut(&[usize]) -> usize>, + offset: u64, +) -> Vec> { + vec![ + Box::new(Peephole), + Box::new(Permute { picker }), + Box::new(Scramble), + Box::new(Mutation), + Box::new(Indirect), + Box::new(Encrypt { offset }), + Box::new(Permute { picker }), + Box::new(Peephole), + ] +} + pub fn transform( mapper: &mut Mapper, - operations: Vec>, + mut operations: Vec>, + offset: u64, mut picker: F, ) -> Vec> where F: FnMut(&[usize]) -> usize, { - let mut operations = operations; + let picker = RefCell::<&mut dyn FnMut(&[usize]) -> usize>::new(&mut picker); - operations = Peephole.run(mapper, operations); - operations = permute::permute(operations, &mut picker); - operations = scramble::scramble(operations); - operations = Mutation.run(mapper, operations); - operations = Encrypt.run(mapper, operations); - operations = permute::permute(operations, &mut picker); - operations = Peephole.run(mapper, operations); + for transform in &transforms(&picker, offset) { + operations = transform.run(mapper, operations); + } operations } -#[cfg(debug_assertions)] pub fn transform_with_snapshots( mapper: &mut Mapper, - operations: Vec>, + mut operations: Vec>, + offset: u64, mut picker: F, ) -> (Vec>, Snapshots) where F: FnMut(&[usize]) -> usize, { - let mut operations = operations; + let picker = RefCell::<&mut dyn FnMut(&[usize]) -> usize>::new(&mut picker); let mut snapshots = Snapshots::new(); snapshots.record(Phase::Lift, &operations); - operations = Peephole.run(mapper, operations); - snapshots.record(Phase::Peephole, &operations); - - operations = permute::permute(operations, &mut picker); - snapshots.record(Phase::Permute, &operations); - - operations = scramble::scramble(operations); - snapshots.record(Phase::Scramble, &operations); - - operations = Mutation.run(mapper, operations); - snapshots.record(Mutation.phase(), &operations); + for transform in &transforms(&picker, offset) { + operations = transform.run(mapper, operations); - operations = Encrypt.run(mapper, operations); - snapshots.record(Encrypt.phase(), &operations); - - operations = permute::permute(operations, &mut picker); - snapshots.record(Phase::Permute, &operations); - - operations = Peephole.run(mapper, operations); - snapshots.record(Phase::Peephole, &operations); + snapshots.record(transform.phase(), &operations); + } (operations, snapshots) } diff --git a/runtime/src/vm/encoders/adc.rs b/runtime/src/vm/encoders/adc.rs new file mode 100644 index 00000000..f73bea43 --- /dev/null +++ b/runtime/src/vm/encoders/adc.rs @@ -0,0 +1,43 @@ +use crate::mapper::Mapper; +use crate::vm::bytecode::{VMOp, VMReg, VMWidth}; +use crate::vm::encoders::{Effect, Encode}; +use std::any::Any; + +#[derive(Debug)] +pub struct Adc { + pub width: VMWidth, +} + +impl Encode for Adc { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn op(&self) -> Option { + Some(VMOp::Adc) + } + + fn encode(&self, mapper: &mut Mapper) -> Vec { + vec![mapper.index(self.width)] + } + + fn reads(&self) -> Vec { + vec![Effect::Register(VMReg::Flags)] + } + + fn writes(&self) -> Vec { + vec![Effect::Register(VMReg::Flags)] + } + + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 + } +} diff --git a/runtime/src/vm/encoders/add.rs b/runtime/src/vm/encoders/add.rs index 6486f6ee..55c4fa5b 100644 --- a/runtime/src/vm/encoders/add.rs +++ b/runtime/src/vm/encoders/add.rs @@ -17,15 +17,23 @@ impl Encode for Add { self } + fn op(&self) -> Option { + Some(VMOp::Add) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Add), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/and.rs b/runtime/src/vm/encoders/and.rs index 1aeea69e..22aeec32 100644 --- a/runtime/src/vm/encoders/and.rs +++ b/runtime/src/vm/encoders/and.rs @@ -17,15 +17,23 @@ impl Encode for And { self } + fn op(&self) -> Option { + Some(VMOp::And) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::And), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/bit_scan_reverse.rs b/runtime/src/vm/encoders/bit_scan_reverse.rs index 2841d4ec..665cb5a8 100644 --- a/runtime/src/vm/encoders/bit_scan_reverse.rs +++ b/runtime/src/vm/encoders/bit_scan_reverse.rs @@ -17,15 +17,23 @@ impl Encode for BitScanReverse { self } + fn op(&self) -> Option { + Some(VMOp::BitScanReverse) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::BitScanReverse), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/bit_test.rs b/runtime/src/vm/encoders/bit_test.rs index 3705cd79..e632f689 100644 --- a/runtime/src/vm/encoders/bit_test.rs +++ b/runtime/src/vm/encoders/bit_test.rs @@ -17,15 +17,23 @@ impl Encode for BitTest { self } + fn op(&self) -> Option { + Some(VMOp::BitTest) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::BitTest), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/bit_test_complement.rs b/runtime/src/vm/encoders/bit_test_complement.rs index 3ea991e1..ff7a3c3d 100644 --- a/runtime/src/vm/encoders/bit_test_complement.rs +++ b/runtime/src/vm/encoders/bit_test_complement.rs @@ -17,18 +17,23 @@ impl Encode for BitTestComplement { self } + fn op(&self) -> Option { + Some(VMOp::BitTestComplement) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::BitTestComplement), - mapper.index(self.width), - ] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/bit_test_reset.rs b/runtime/src/vm/encoders/bit_test_reset.rs index 7aacefa8..d7fbf278 100644 --- a/runtime/src/vm/encoders/bit_test_reset.rs +++ b/runtime/src/vm/encoders/bit_test_reset.rs @@ -17,15 +17,23 @@ impl Encode for BitTestReset { self } + fn op(&self) -> Option { + Some(VMOp::BitTestReset) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::BitTestReset), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/bit_test_set.rs b/runtime/src/vm/encoders/bit_test_set.rs index d569a7e0..f232b724 100644 --- a/runtime/src/vm/encoders/bit_test_set.rs +++ b/runtime/src/vm/encoders/bit_test_set.rs @@ -17,15 +17,23 @@ impl Encode for BitTestSet { self } + fn op(&self) -> Option { + Some(VMOp::BitTestSet) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::BitTestSet), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/block.rs b/runtime/src/vm/encoders/block.rs index 0acff4dc..27e76c64 100644 --- a/runtime/src/vm/encoders/block.rs +++ b/runtime/src/vm/encoders/block.rs @@ -1,7 +1,7 @@ use std::any::Any; use crate::mapper::Mapper; -use crate::vm::bytecode::{self, VMCondition, VMLogic, VMWidth}; +use crate::vm::bytecode::{self, VMCondition, VMLogic, VMOp, VMWidth}; use crate::vm::encoders::jcc::Jcc; use crate::vm::encoders::label::Label; use crate::vm::encoders::load_immediate::LoadImmediate; @@ -89,22 +89,18 @@ impl Block { for i in index + 1..self.body.len() { let operation = &self.body[i]; + let consumes = operation.consumes() as usize; + if operation.as_any().downcast_ref::().is_some() { - if stack.len() == 1 { - return Some((stack[0], i)); - } + return Some((stack[stack.len() - consumes], i)); } - let delta = operation.depth(); + for _ in 0..consumes { + stack.pop(); + } - if delta > 0 { - for _ in 0..delta { - stack.push(i); - } - } else { - for _ in 0..(-delta) { - stack.pop(); - } + for _ in 0..operation.produces() { + stack.push(i); } } @@ -121,6 +117,10 @@ impl Encode for Block { self } + fn op(&self) -> Option { + None + } + fn encode(&self, mapper: &mut Mapper) -> Vec { bytecode::assemble(mapper, &self.body) } @@ -137,8 +137,12 @@ impl Encode for Block { self.body.iter().flat_map(|op| op.writes()).collect() } - fn depth(&self) -> i32 { - self.body.iter().map(|op| op.depth()).sum() + fn consumes(&self) -> i32 { + self.body.iter().map(|op| op.consumes()).sum() + } + + fn produces(&self) -> i32 { + self.body.iter().map(|op| op.produces()).sum() } fn children_ref(&self) -> Option<&[Box]> { diff --git a/runtime/src/vm/encoders/byte_swap.rs b/runtime/src/vm/encoders/byte_swap.rs index 55e42b64..e772c8dd 100644 --- a/runtime/src/vm/encoders/byte_swap.rs +++ b/runtime/src/vm/encoders/byte_swap.rs @@ -17,7 +17,19 @@ impl Encode for ByteSwap { self } + fn op(&self) -> Option { + Some(VMOp::ByteSwap) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::ByteSwap), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/compare_exchange.rs b/runtime/src/vm/encoders/compare_exchange.rs index f83f1b12..852cf8f8 100644 --- a/runtime/src/vm/encoders/compare_exchange.rs +++ b/runtime/src/vm/encoders/compare_exchange.rs @@ -17,18 +17,23 @@ impl Encode for CompareExchange { self } + fn op(&self) -> Option { + Some(VMOp::CompareExchange) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::CompareExchange), - mapper.index(self.width), - ] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Memory, Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -2 + fn consumes(&self) -> i32 { + 3 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/debug.rs b/runtime/src/vm/encoders/debug.rs deleted file mode 100644 index f25feb71..00000000 --- a/runtime/src/vm/encoders/debug.rs +++ /dev/null @@ -1,148 +0,0 @@ -#![allow(unused)] - -use crate::vm::encoders::Encode; -use std::fmt; - -impl fmt::Display for dyn Encode { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let s = strip_fields(&format!("{self:?}")) - .replace(" { ", "(") - .replace(" }", ")"); - let s = hex_bytes(&s); - let s = hex_decimals(&s); - write!(f, "{s}") - } -} - -fn strip_fields(input: &str) -> String { - let mut output = String::with_capacity(input.len()); - - let mut characters = input.chars().peekable(); - - while let Some(c) = characters.next() { - if c == ':' && characters.peek() == Some(&' ') { - characters.next(); - - while output.ends_with(|c: char| c.is_alphanumeric() || c == '_') { - output.pop(); - } - } else { - output.push(c); - } - } - output -} - -fn hex_bytes(input: &str) -> String { - let mut output = String::with_capacity(input.len()); - - let mut characters = input.char_indices().peekable(); - - while let Some((i, c)) = characters.next() { - if c == '[' { - if let Some(end) = input[i + 1..].find(']') { - let bytes = input[i + 1..i + 1 + end] - .split(',') - .map(|b| b.trim().parse::().ok()) - .collect::>>(); - if let Some(bytes) = bytes { - let hex = match bytes.len() { - 1 => Some(format!( - "0x{:02X}", - u8::from_le_bytes(bytes.try_into().unwrap()) - )), - 2 => Some(format!( - "0x{:04X}", - u16::from_le_bytes(bytes.try_into().unwrap()) - )), - 4 => Some(format!( - "0x{:08X}", - u32::from_le_bytes(bytes.try_into().unwrap()) - )), - 8 => Some(format!( - "0x{:016X}", - u64::from_le_bytes(bytes.try_into().unwrap()) - )), - _ => None, - }; - if let Some(hex) = hex { - output.push_str(&hex); - characters.nth(end); - continue; - } - } - } - } - output.push(c); - } - output -} - -fn hex_decimals(input: &str) -> String { - let mut output = String::with_capacity(input.len()); - - let characters = input.chars().collect::>(); - - let mut i = 0; - - while i < characters.len() { - let c = characters[i]; - - if c == '0' && matches!(characters.get(i + 1), Some('x') | Some('X')) { - output.extend(characters[i..i + 2].iter()); - i += 2; - while matches!(characters.get(i), Some(c) if c.is_ascii_hexdigit()) { - output.push(characters[i]); - i += 1; - } - continue; - } - - let previous = - i > 0 && (characters[i - 1].is_ascii_alphanumeric() || characters[i - 1] == '_'); - - let (start, negative) = match c { - '-' if !previous && matches!(characters.get(i + 1), Some(c) if c.is_ascii_digit()) => { - (i + 1, true) - } - c if c.is_ascii_digit() && !previous => (i, false), - _ => { - output.push(c); - i += 1; - continue; - } - }; - - let mut end = start; - - while matches!(characters.get(end), Some(c) if c.is_ascii_digit()) { - end += 1; - } - - let digits = characters[start..end].iter().collect::(); - - match digits.parse::() { - Ok(value) => { - let value = if negative { -value } else { value }; - - if value >= i8::MIN as i64 && value <= i8::MAX as i64 { - output.push_str(&format!("0x{:02X}", value as u8)); - } else if value >= i16::MIN as i64 && value <= i16::MAX as i64 { - output.push_str(&format!("0x{:04X}", value as u16)); - } else if value >= i32::MIN as i64 && value <= i32::MAX as i64 { - output.push_str(&format!("0x{:08X}", value as u32)); - } else { - output.push_str(&format!("0x{:016X}", value as u64)); - } - } - Err(_) => { - if negative { - output.push('-'); - } - output.push_str(&digits); - } - } - i = end; - } - output -} diff --git a/runtime/src/vm/encoders/discard.rs b/runtime/src/vm/encoders/discard.rs index e4263294..c1a01f47 100644 --- a/runtime/src/vm/encoders/discard.rs +++ b/runtime/src/vm/encoders/discard.rs @@ -30,11 +30,19 @@ impl Encode for Discard { self } - fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Discard)] + fn op(&self) -> Option { + Some(VMOp::Discard) } - fn depth(&self) -> i32 { - -1 + fn encode(&self, _mapper: &mut Mapper) -> Vec { + vec![] + } + + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/dispatch.rs b/runtime/src/vm/encoders/dispatch.rs new file mode 100644 index 00000000..ab3ec6db --- /dev/null +++ b/runtime/src/vm/encoders/dispatch.rs @@ -0,0 +1,71 @@ +use crate::mapper::Mapper; +use crate::vm::bytecode::VMOp; +use crate::vm::encoders::{Effect, Encode}; +use std::any::Any; + +#[derive(Debug)] +pub struct Dispatch { + pub operation: Box, +} + +impl Encode for Dispatch { + fn as_any(&self) -> &dyn Any { + self.operation.as_any() + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self.operation.as_any_mut() + } + + fn op(&self) -> Option { + Some(VMOp::Dispatch) + } + + fn encode(&self, mapper: &mut Mapper) -> Vec { + self.operation.encode(mapper) + } + + fn size(&self, mapper: &mut Mapper) -> usize { + self.operation.size(mapper) + } + + fn reads(&self) -> Vec { + self.operation.reads() + } + + fn writes(&self) -> Vec { + self.operation.writes() + } + + fn consumes(&self) -> i32 { + 1 + self.operation.consumes() + } + + fn produces(&self) -> i32 { + self.operation.produces() + } + + fn is_source(&self) -> bool { + self.operation.is_source() + } + + fn is_destination(&self) -> bool { + self.operation.is_destination() + } + + fn is_branch(&self) -> bool { + self.operation.is_branch() + } + + fn children_ref(&self) -> Option<&[Box]> { + self.operation.children_ref() + } + + fn children_mut(&mut self) -> Option<&mut Vec>> { + self.operation.children_mut() + } + + fn seal(&mut self, mapper: &mut Mapper, transform: &mut dyn FnMut(&mut [u8], usize)) { + self.operation.seal(mapper, transform); + } +} diff --git a/runtime/src/vm/encoders/div.rs b/runtime/src/vm/encoders/div.rs index 2c44804d..731625cf 100644 --- a/runtime/src/vm/encoders/div.rs +++ b/runtime/src/vm/encoders/div.rs @@ -17,11 +17,19 @@ impl Encode for Div { self } + fn op(&self) -> Option { + Some(VMOp::Div) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Div), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 3 } - fn depth(&self) -> i32 { - -1 + fn produces(&self) -> i32 { + 2 } } diff --git a/runtime/src/vm/encoders/exchange.rs b/runtime/src/vm/encoders/exchange.rs index cbde10e8..0755490a 100644 --- a/runtime/src/vm/encoders/exchange.rs +++ b/runtime/src/vm/encoders/exchange.rs @@ -17,15 +17,23 @@ impl Encode for Exchange { self } + fn op(&self) -> Option { + Some(VMOp::Exchange) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Exchange), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Memory] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/exchange_add.rs b/runtime/src/vm/encoders/exchange_add.rs index 6f768dbc..38cf9023 100644 --- a/runtime/src/vm/encoders/exchange_add.rs +++ b/runtime/src/vm/encoders/exchange_add.rs @@ -17,15 +17,23 @@ impl Encode for ExchangeAdd { self } + fn op(&self) -> Option { + Some(VMOp::ExchangeAdd) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::ExchangeAdd), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Memory, Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/jcc.rs b/runtime/src/vm/encoders/jcc.rs index 28b651c6..d0d31c46 100644 --- a/runtime/src/vm/encoders/jcc.rs +++ b/runtime/src/vm/encoders/jcc.rs @@ -52,12 +52,12 @@ impl Encode for Jcc { self } + fn op(&self) -> Option { + Some(VMOp::Jcc) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - let mut bytes = vec![ - mapper.index(VMOp::Jcc), - mapper.index(self.logic), - self.conditions.len() as u8, - ]; + let mut bytes = vec![mapper.index(self.logic), self.conditions.len() as u8]; for condition in &self.conditions { bytes.extend_from_slice(&condition.encode(mapper)); @@ -80,8 +80,12 @@ impl Encode for Jcc { } } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 0 } fn is_branch(&self) -> bool { diff --git a/runtime/src/vm/encoders/label.rs b/runtime/src/vm/encoders/label.rs index 6747740b..a382d3c4 100644 --- a/runtime/src/vm/encoders/label.rs +++ b/runtime/src/vm/encoders/label.rs @@ -1,4 +1,7 @@ -use crate::{mapper::Mapper, vm::encoders::Encode}; +use crate::{ + mapper::Mapper, + vm::{bytecode::VMOp, encoders::Encode}, +}; use std::{ any::Any, sync::atomic::{AtomicUsize, Ordering}, @@ -47,6 +50,10 @@ impl Encode for Label { self } + fn op(&self) -> Option { + None + } + fn encode(&self, _mapper: &mut Mapper) -> Vec { vec![] } diff --git a/runtime/src/vm/encoders/load_address.rs b/runtime/src/vm/encoders/load_address.rs index edbd5e9a..39c9ea3c 100644 --- a/runtime/src/vm/encoders/load_address.rs +++ b/runtime/src/vm/encoders/load_address.rs @@ -17,10 +17,12 @@ impl Encode for LoadAddress { self } + fn op(&self) -> Option { + Some(VMOp::LoadAddress) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - let mut bytes = vec![mapper.index(VMOp::LoadAddress)]; - bytes.extend(self.source.encode(mapper)); - bytes + self.source.encode(mapper) } fn reads(&self) -> Vec { @@ -32,7 +34,11 @@ impl Encode for LoadAddress { ] } - fn depth(&self) -> i32 { + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 1 } } diff --git a/runtime/src/vm/encoders/load_immediate.rs b/runtime/src/vm/encoders/load_immediate.rs index 45bf4c18..a86ebde3 100644 --- a/runtime/src/vm/encoders/load_immediate.rs +++ b/runtime/src/vm/encoders/load_immediate.rs @@ -18,8 +18,12 @@ impl Encode for LoadImmediate { self } + fn op(&self) -> Option { + Some(VMOp::LoadImmediate) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - let mut bytes = vec![mapper.index(VMOp::LoadImmediate), mapper.index(self.width)]; + let mut bytes = vec![mapper.index(self.width)]; bytes.extend_from_slice(&self.source); bytes } @@ -31,7 +35,11 @@ impl Encode for LoadImmediate { ] } - fn depth(&self) -> i32 { + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 1 } } diff --git a/runtime/src/vm/encoders/load_memory.rs b/runtime/src/vm/encoders/load_memory.rs index f7c2d125..c27c9997 100644 --- a/runtime/src/vm/encoders/load_memory.rs +++ b/runtime/src/vm/encoders/load_memory.rs @@ -17,15 +17,23 @@ impl Encode for LoadMemory { self } + fn op(&self) -> Option { + Some(VMOp::LoadMemory) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::LoadMemory), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn reads(&self) -> Vec { vec![Effect::Memory] } - fn depth(&self) -> i32 { - self.width.slots() - 1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/load_register.rs b/runtime/src/vm/encoders/load_register.rs index 4ea4edef..12b89b06 100644 --- a/runtime/src/vm/encoders/load_register.rs +++ b/runtime/src/vm/encoders/load_register.rs @@ -18,19 +18,23 @@ impl Encode for LoadRegister { self } + fn op(&self) -> Option { + Some(VMOp::LoadRegister) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::LoadRegister), - mapper.index(self.width), - mapper.index(self.source), - ] + vec![mapper.index(self.width), mapper.index(self.source)] } fn reads(&self) -> Vec { vec![Effect::Register(self.source)] } - fn depth(&self) -> i32 { + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 1 } } diff --git a/runtime/src/vm/encoders/load_vector.rs b/runtime/src/vm/encoders/load_vector.rs index 9eb1bbb9..42195f87 100644 --- a/runtime/src/vm/encoders/load_vector.rs +++ b/runtime/src/vm/encoders/load_vector.rs @@ -18,19 +18,23 @@ impl Encode for LoadVector { self } + fn op(&self) -> Option { + Some(VMOp::LoadVector) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::LoadVector), - mapper.index(self.width), - mapper.index(self.source), - ] + vec![mapper.index(self.width), mapper.index(self.source)] } fn reads(&self) -> Vec { vec![Effect::Vector(self.source)] } - fn depth(&self) -> i32 { - self.width.slots() + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/mod.rs b/runtime/src/vm/encoders/mod.rs index 43a72646..1acb6ab4 100644 --- a/runtime/src/vm/encoders/mod.rs +++ b/runtime/src/vm/encoders/mod.rs @@ -1,11 +1,60 @@ use std::any::{type_name, Any}; -use std::fmt::Debug; +use std::fmt::{self, Debug}; use crate::mapper::Mapper; -use crate::vm::bytecode::{VMReg, VMVec}; +use crate::vm::bytecode::{VMOp, VMReg, VMVec}; -#[cfg(debug_assertions)] -mod debug; +pub mod adc; +pub mod add; +pub mod and; +pub mod bit_scan_reverse; +pub mod bit_test; +pub mod bit_test_complement; +pub mod bit_test_reset; +pub mod bit_test_set; +pub mod block; +pub mod byte_swap; +pub mod compare_exchange; +pub mod discard; +pub mod dispatch; +pub mod div; +pub mod exchange; +pub mod exchange_add; +pub mod jcc; +pub mod label; +pub mod load_address; +pub mod load_immediate; +pub mod load_memory; +pub mod load_register; +pub mod load_vector; +pub mod mul; +pub mod or; +pub mod packed_byte_equal; +pub mod packed_byte_mask; +pub mod pop; +pub mod push; +pub mod ret; +pub mod rol; +pub mod ror; +pub mod sar; +pub mod sbb; +pub mod shl; +pub mod shr; +pub mod store_extend; +pub mod store_memory; +pub mod store_merge; +pub mod store_register; +pub mod sub; +pub mod timestamp; +pub mod trailing_zeros; +pub mod vector_add; +pub mod vector_and; +pub mod vector_and_not; +pub mod vector_div; +pub mod vector_mul; +pub mod vector_or; +pub mod vector_sub; +pub mod vector_xor; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Effect { @@ -18,6 +67,16 @@ pub fn identity(operation: &Box) -> usize { operation.as_ref() as *const dyn Encode as *const () as usize } +pub trait Depth { + fn depth(&self) -> i32; +} + +impl Depth for T { + fn depth(&self) -> i32 { + self.produces() - self.consumes() + } +} + pub trait Encode: Debug + Any { fn as_any(&self) -> &dyn Any; @@ -27,10 +86,12 @@ pub trait Encode: Debug + Any { type_name::().rsplit("::").next().unwrap() } + fn op(&self) -> Option; + fn encode(&self, mapper: &mut Mapper) -> Vec; fn size(&self, mapper: &mut Mapper) -> usize { - self.encode(mapper).len() + self.op().map_or(0, |_| 1) + self.encode(mapper).len() } fn reads(&self) -> Vec { @@ -41,7 +102,11 @@ pub trait Encode: Debug + Any { vec![] } - fn depth(&self) -> i32 { + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 0 } @@ -68,52 +133,147 @@ pub trait Encode: Debug + Any { fn seal(&mut self, _mapper: &mut Mapper, _transform: &mut dyn FnMut(&mut [u8], usize)) {} } -pub mod add; -pub mod and; -pub mod bit_scan_reverse; -pub mod bit_test; -pub mod bit_test_complement; -pub mod bit_test_reset; -pub mod bit_test_set; -pub mod block; -pub mod byte_swap; -pub mod compare_exchange; -pub mod discard; -pub mod div; -pub mod exchange; -pub mod exchange_add; -pub mod jcc; -pub mod label; -pub mod load_address; -pub mod load_immediate; -pub mod load_memory; -pub mod load_register; -pub mod load_vector; -pub mod mul; -pub mod or; -pub mod packed_byte_equal; -pub mod packed_byte_mask; -pub mod pop; -pub mod push; -pub mod ret; -pub mod rol; -pub mod ror; -pub mod sar; -pub mod shl; -pub mod shr; -pub mod store_extend; -pub mod store_memory; -pub mod store_merge; -pub mod store_register; -pub mod sub; -pub mod timestamp; -pub mod trailing_zeros; -pub mod vector_add; -pub mod vector_and; -pub mod vector_and_not; -pub mod vector_div; -pub mod vector_mul; -pub mod vector_or; -pub mod vector_sub; -pub mod vector_xor; +impl fmt::Display for dyn Encode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = strip_fields(&format!("{self:?}")) + .replace(" { ", "(") + .replace(" }", ")"); + let s = hex_bytes(&s); + let s = hex_decimals(&s); + write!(f, "{s}") + } +} + +fn strip_fields(input: &str) -> String { + let mut output = String::with_capacity(input.len()); + + let mut characters = input.chars().peekable(); + + while let Some(c) = characters.next() { + if c == ':' && characters.peek() == Some(&' ') { + characters.next(); + + while output.ends_with(|c: char| c.is_alphanumeric() || c == '_') { + output.pop(); + } + } else { + output.push(c); + } + } + output +} + +fn hex_bytes(input: &str) -> String { + let mut output = String::with_capacity(input.len()); + + let mut characters = input.char_indices().peekable(); + + while let Some((i, c)) = characters.next() { + if c == '[' { + if let Some(end) = input[i + 1..].find(']') { + let bytes = input[i + 1..i + 1 + end] + .split(',') + .map(|b| b.trim().parse::().ok()) + .collect::>>(); + if let Some(bytes) = bytes { + let hex = match bytes.len() { + 1 => Some(format!( + "0x{:02X}", + u8::from_le_bytes(bytes.try_into().unwrap()) + )), + 2 => Some(format!( + "0x{:04X}", + u16::from_le_bytes(bytes.try_into().unwrap()) + )), + 4 => Some(format!( + "0x{:08X}", + u32::from_le_bytes(bytes.try_into().unwrap()) + )), + 8 => Some(format!( + "0x{:016X}", + u64::from_le_bytes(bytes.try_into().unwrap()) + )), + _ => None, + }; + if let Some(hex) = hex { + output.push_str(&hex); + characters.nth(end); + continue; + } + } + } + } + output.push(c); + } + output +} + +fn hex_decimals(input: &str) -> String { + let mut output = String::with_capacity(input.len()); + + let characters = input.chars().collect::>(); + + let mut i = 0; + + while i < characters.len() { + let c = characters[i]; + + if c == '0' && matches!(characters.get(i + 1), Some('x') | Some('X')) { + output.extend(characters[i..i + 2].iter()); + i += 2; + while matches!(characters.get(i), Some(c) if c.is_ascii_hexdigit()) { + output.push(characters[i]); + i += 1; + } + continue; + } + + let previous = + i > 0 && (characters[i - 1].is_ascii_alphanumeric() || characters[i - 1] == '_'); + + let (start, negative) = match c { + '-' if !previous && matches!(characters.get(i + 1), Some(c) if c.is_ascii_digit()) => { + (i + 1, true) + } + c if c.is_ascii_digit() && !previous => (i, false), + _ => { + output.push(c); + i += 1; + continue; + } + }; + + let mut end = start; + + while matches!(characters.get(end), Some(c) if c.is_ascii_digit()) { + end += 1; + } + + let digits = characters[start..end].iter().collect::(); + + match digits.parse::() { + Ok(value) => { + let value = if negative { -value } else { value }; + + if value >= i8::MIN as i64 && value <= i8::MAX as i64 { + output.push_str(&format!("0x{:02X}", value as u8)); + } else if value >= i16::MIN as i64 && value <= i16::MAX as i64 { + output.push_str(&format!("0x{:04X}", value as u16)); + } else if value >= i32::MIN as i64 && value <= i32::MAX as i64 { + output.push_str(&format!("0x{:08X}", value as u32)); + } else { + output.push_str(&format!("0x{:016X}", value as u64)); + } + } + Err(_) => { + if negative { + output.push('-'); + } + output.push_str(&digits); + } + } + i = end; + } + output +} pub mod xor; diff --git a/runtime/src/vm/encoders/mul.rs b/runtime/src/vm/encoders/mul.rs index 5a87b5bf..dac49edf 100644 --- a/runtime/src/vm/encoders/mul.rs +++ b/runtime/src/vm/encoders/mul.rs @@ -17,15 +17,23 @@ impl Encode for Mul { self } + fn op(&self) -> Option { + Some(VMOp::Mul) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Mul), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - 0 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 2 } } diff --git a/runtime/src/vm/encoders/or.rs b/runtime/src/vm/encoders/or.rs index 15e6c4fc..8d59154c 100644 --- a/runtime/src/vm/encoders/or.rs +++ b/runtime/src/vm/encoders/or.rs @@ -17,15 +17,23 @@ impl Encode for Or { self } + fn op(&self) -> Option { + Some(VMOp::Or) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Or), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/packed_byte_equal.rs b/runtime/src/vm/encoders/packed_byte_equal.rs index 3fed8d28..e3e3baab 100644 --- a/runtime/src/vm/encoders/packed_byte_equal.rs +++ b/runtime/src/vm/encoders/packed_byte_equal.rs @@ -17,14 +17,19 @@ impl Encode for PackedByteEqual { self } + fn op(&self) -> Option { + Some(VMOp::PackedByteEqual) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::PackedByteEqual), - mapper.index(self.width), - ] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/packed_byte_mask.rs b/runtime/src/vm/encoders/packed_byte_mask.rs index e7f73862..d338cb11 100644 --- a/runtime/src/vm/encoders/packed_byte_mask.rs +++ b/runtime/src/vm/encoders/packed_byte_mask.rs @@ -17,11 +17,19 @@ impl Encode for PackedByteMask { self } + fn op(&self) -> Option { + Some(VMOp::PackedByteMask) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::PackedByteMask), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 1 } - fn depth(&self) -> i32 { - 1 - self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/pop.rs b/runtime/src/vm/encoders/pop.rs index 5f3bce9b..668db529 100644 --- a/runtime/src/vm/encoders/pop.rs +++ b/runtime/src/vm/encoders/pop.rs @@ -30,8 +30,12 @@ impl Encode for Pop { self } - fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Pop)] + fn op(&self) -> Option { + Some(VMOp::Pop) + } + + fn encode(&self, _mapper: &mut Mapper) -> Vec { + vec![] } fn reads(&self) -> Vec { @@ -42,7 +46,11 @@ impl Encode for Pop { vec![Effect::Register(VMReg::Rsp)] } - fn depth(&self) -> i32 { + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 1 } } diff --git a/runtime/src/vm/encoders/push.rs b/runtime/src/vm/encoders/push.rs index 0daa9253..b1a7dfcf 100644 --- a/runtime/src/vm/encoders/push.rs +++ b/runtime/src/vm/encoders/push.rs @@ -30,8 +30,12 @@ impl Encode for Push { self } - fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Push)] + fn op(&self) -> Option { + Some(VMOp::Push) + } + + fn encode(&self, _mapper: &mut Mapper) -> Vec { + vec![] } fn reads(&self) -> Vec { @@ -42,7 +46,11 @@ impl Encode for Push { vec![Effect::Memory, Effect::Register(VMReg::Rsp)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/ret.rs b/runtime/src/vm/encoders/ret.rs index b4776963..8d20f80b 100644 --- a/runtime/src/vm/encoders/ret.rs +++ b/runtime/src/vm/encoders/ret.rs @@ -15,8 +15,12 @@ impl Encode for Ret { self } - fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Ret)] + fn op(&self) -> Option { + Some(VMOp::Ret) + } + + fn encode(&self, _mapper: &mut Mapper) -> Vec { + vec![] } fn reads(&self) -> Vec { @@ -30,8 +34,12 @@ impl Encode for Ret { ] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 0 } fn is_branch(&self) -> bool { diff --git a/runtime/src/vm/encoders/rol.rs b/runtime/src/vm/encoders/rol.rs index 08cac5d0..bb9a57c3 100644 --- a/runtime/src/vm/encoders/rol.rs +++ b/runtime/src/vm/encoders/rol.rs @@ -17,15 +17,23 @@ impl Encode for Rol { self } + fn op(&self) -> Option { + Some(VMOp::Rol) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Rol), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/ror.rs b/runtime/src/vm/encoders/ror.rs index 9fb51e60..aa942895 100644 --- a/runtime/src/vm/encoders/ror.rs +++ b/runtime/src/vm/encoders/ror.rs @@ -17,15 +17,23 @@ impl Encode for Ror { self } + fn op(&self) -> Option { + Some(VMOp::Ror) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Ror), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/sar.rs b/runtime/src/vm/encoders/sar.rs index d9c1ae4f..1b781101 100644 --- a/runtime/src/vm/encoders/sar.rs +++ b/runtime/src/vm/encoders/sar.rs @@ -17,15 +17,23 @@ impl Encode for Sar { self } + fn op(&self) -> Option { + Some(VMOp::Sar) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Sar), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/sbb.rs b/runtime/src/vm/encoders/sbb.rs new file mode 100644 index 00000000..6490e974 --- /dev/null +++ b/runtime/src/vm/encoders/sbb.rs @@ -0,0 +1,43 @@ +use crate::mapper::Mapper; +use crate::vm::bytecode::{VMOp, VMReg, VMWidth}; +use crate::vm::encoders::{Effect, Encode}; +use std::any::Any; + +#[derive(Debug)] +pub struct Sbb { + pub width: VMWidth, +} + +impl Encode for Sbb { + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } + + fn op(&self) -> Option { + Some(VMOp::Sbb) + } + + fn encode(&self, mapper: &mut Mapper) -> Vec { + vec![mapper.index(self.width)] + } + + fn reads(&self) -> Vec { + vec![Effect::Register(VMReg::Flags)] + } + + fn writes(&self) -> Vec { + vec![Effect::Register(VMReg::Flags)] + } + + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 + } +} diff --git a/runtime/src/vm/encoders/shl.rs b/runtime/src/vm/encoders/shl.rs index 85e81856..7858b0b2 100644 --- a/runtime/src/vm/encoders/shl.rs +++ b/runtime/src/vm/encoders/shl.rs @@ -17,15 +17,23 @@ impl Encode for Shl { self } + fn op(&self) -> Option { + Some(VMOp::Shl) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Shl), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/shr.rs b/runtime/src/vm/encoders/shr.rs index d1394318..376899a6 100644 --- a/runtime/src/vm/encoders/shr.rs +++ b/runtime/src/vm/encoders/shr.rs @@ -17,15 +17,23 @@ impl Encode for Shr { self } + fn op(&self) -> Option { + Some(VMOp::Shr) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Shr), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/store_extend.rs b/runtime/src/vm/encoders/store_extend.rs index ad69f00b..dccce633 100644 --- a/runtime/src/vm/encoders/store_extend.rs +++ b/runtime/src/vm/encoders/store_extend.rs @@ -18,19 +18,23 @@ impl Encode for StoreExtend { self } + fn op(&self) -> Option { + Some(VMOp::StoreExtend) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::StoreExtend), - mapper.index(self.width), - mapper.index(self.destination), - ] + vec![mapper.index(self.width), mapper.index(self.destination)] } fn writes(&self) -> Vec { vec![Effect::Vector(self.destination)] } - fn depth(&self) -> i32 { - -self.width.slots() + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/store_memory.rs b/runtime/src/vm/encoders/store_memory.rs index a220cbc7..84050f3f 100644 --- a/runtime/src/vm/encoders/store_memory.rs +++ b/runtime/src/vm/encoders/store_memory.rs @@ -17,15 +17,23 @@ impl Encode for StoreMemory { self } + fn op(&self) -> Option { + Some(VMOp::StoreMemory) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::StoreMemory), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Memory] } - fn depth(&self) -> i32 { - -(self.width.slots() + 1) + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/store_merge.rs b/runtime/src/vm/encoders/store_merge.rs index 5da277c7..2ee89c61 100644 --- a/runtime/src/vm/encoders/store_merge.rs +++ b/runtime/src/vm/encoders/store_merge.rs @@ -18,19 +18,23 @@ impl Encode for StoreMerge { self } + fn op(&self) -> Option { + Some(VMOp::StoreMerge) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::StoreMerge), - mapper.index(self.width), - mapper.index(self.destination), - ] + vec![mapper.index(self.width), mapper.index(self.destination)] } fn writes(&self) -> Vec { vec![Effect::Vector(self.destination)] } - fn depth(&self) -> i32 { - -self.width.slots() + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/store_register.rs b/runtime/src/vm/encoders/store_register.rs index 82b5b587..8e60b989 100644 --- a/runtime/src/vm/encoders/store_register.rs +++ b/runtime/src/vm/encoders/store_register.rs @@ -18,19 +18,23 @@ impl Encode for StoreRegister { self } + fn op(&self) -> Option { + Some(VMOp::StoreRegister) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::StoreRegister), - mapper.index(self.width), - mapper.index(self.destination), - ] + vec![mapper.index(self.width), mapper.index(self.destination)] } fn writes(&self) -> Vec { vec![Effect::Register(self.destination)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 0 } } diff --git a/runtime/src/vm/encoders/sub.rs b/runtime/src/vm/encoders/sub.rs index 82e276a6..4c289f66 100644 --- a/runtime/src/vm/encoders/sub.rs +++ b/runtime/src/vm/encoders/sub.rs @@ -17,15 +17,23 @@ impl Encode for Sub { self } + fn op(&self) -> Option { + Some(VMOp::Sub) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Sub), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/timestamp.rs b/runtime/src/vm/encoders/timestamp.rs index 36aff9c4..a256b96e 100644 --- a/runtime/src/vm/encoders/timestamp.rs +++ b/runtime/src/vm/encoders/timestamp.rs @@ -30,11 +30,19 @@ impl Encode for Timestamp { self } - fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Timestamp)] + fn op(&self) -> Option { + Some(VMOp::Timestamp) } - fn depth(&self) -> i32 { + fn encode(&self, _mapper: &mut Mapper) -> Vec { + vec![] + } + + fn consumes(&self) -> i32 { + 0 + } + + fn produces(&self) -> i32 { 2 } } diff --git a/runtime/src/vm/encoders/trailing_zeros.rs b/runtime/src/vm/encoders/trailing_zeros.rs index 34895e54..9dc6a5f7 100644 --- a/runtime/src/vm/encoders/trailing_zeros.rs +++ b/runtime/src/vm/encoders/trailing_zeros.rs @@ -17,15 +17,23 @@ impl Encode for TrailingZeros { self } + fn op(&self) -> Option { + Some(VMOp::TrailingZeros) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::TrailingZeros), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 1 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_add.rs b/runtime/src/vm/encoders/vector_add.rs index 647493b0..d705ca74 100644 --- a/runtime/src/vm/encoders/vector_add.rs +++ b/runtime/src/vm/encoders/vector_add.rs @@ -19,16 +19,23 @@ impl Encode for VectorAdd { self } + fn op(&self) -> Option { + Some(VMOp::VectorAdd) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { vec![ - mapper.index(VMOp::VectorAdd), mapper.index(self.width), mapper.index(self.stride), mapper.index(self.precision), ] } - fn depth(&self) -> i32 { - -self.width.slots() + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_and.rs b/runtime/src/vm/encoders/vector_and.rs index f4061c25..67ff5ba2 100644 --- a/runtime/src/vm/encoders/vector_and.rs +++ b/runtime/src/vm/encoders/vector_and.rs @@ -17,11 +17,19 @@ impl Encode for VectorAnd { self } + fn op(&self) -> Option { + Some(VMOp::VectorAnd) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::VectorAnd), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_and_not.rs b/runtime/src/vm/encoders/vector_and_not.rs index f1fac5ef..4ea2a697 100644 --- a/runtime/src/vm/encoders/vector_and_not.rs +++ b/runtime/src/vm/encoders/vector_and_not.rs @@ -17,11 +17,19 @@ impl Encode for VectorAndNot { self } + fn op(&self) -> Option { + Some(VMOp::VectorAndNot) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::VectorAndNot), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_div.rs b/runtime/src/vm/encoders/vector_div.rs index aa6f82e4..20f5d530 100644 --- a/runtime/src/vm/encoders/vector_div.rs +++ b/runtime/src/vm/encoders/vector_div.rs @@ -18,15 +18,19 @@ impl Encode for VectorDiv { self } + fn op(&self) -> Option { + Some(VMOp::VectorDiv) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![ - mapper.index(VMOp::VectorDiv), - mapper.index(self.width), - mapper.index(self.stride), - ] + vec![mapper.index(self.width), mapper.index(self.stride)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_mul.rs b/runtime/src/vm/encoders/vector_mul.rs index eec60369..24880a80 100644 --- a/runtime/src/vm/encoders/vector_mul.rs +++ b/runtime/src/vm/encoders/vector_mul.rs @@ -19,16 +19,23 @@ impl Encode for VectorMul { self } + fn op(&self) -> Option { + Some(VMOp::VectorMul) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { vec![ - mapper.index(VMOp::VectorMul), mapper.index(self.width), mapper.index(self.stride), mapper.index(self.precision), ] } - fn depth(&self) -> i32 { - -self.width.slots() + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_or.rs b/runtime/src/vm/encoders/vector_or.rs index 855f01ff..b8b16a2c 100644 --- a/runtime/src/vm/encoders/vector_or.rs +++ b/runtime/src/vm/encoders/vector_or.rs @@ -17,11 +17,19 @@ impl Encode for VectorOr { self } + fn op(&self) -> Option { + Some(VMOp::VectorOr) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::VectorOr), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_sub.rs b/runtime/src/vm/encoders/vector_sub.rs index 5693dac1..c5172ebb 100644 --- a/runtime/src/vm/encoders/vector_sub.rs +++ b/runtime/src/vm/encoders/vector_sub.rs @@ -19,16 +19,23 @@ impl Encode for VectorSub { self } + fn op(&self) -> Option { + Some(VMOp::VectorSub) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { vec![ - mapper.index(VMOp::VectorSub), mapper.index(self.width), mapper.index(self.stride), mapper.index(self.precision), ] } - fn depth(&self) -> i32 { - -self.width.slots() + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/vector_xor.rs b/runtime/src/vm/encoders/vector_xor.rs index 63d02974..bad28bfc 100644 --- a/runtime/src/vm/encoders/vector_xor.rs +++ b/runtime/src/vm/encoders/vector_xor.rs @@ -17,11 +17,19 @@ impl Encode for VectorXor { self } + fn op(&self) -> Option { + Some(VMOp::VectorXor) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::VectorXor), mapper.index(self.width)] + vec![mapper.index(self.width)] + } + + fn consumes(&self) -> i32 { + 2 } - fn depth(&self) -> i32 { - -self.width.slots() + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/encoders/xor.rs b/runtime/src/vm/encoders/xor.rs index 87f6a299..1a4434aa 100644 --- a/runtime/src/vm/encoders/xor.rs +++ b/runtime/src/vm/encoders/xor.rs @@ -17,15 +17,23 @@ impl Encode for Xor { self } + fn op(&self) -> Option { + Some(VMOp::Xor) + } + fn encode(&self, mapper: &mut Mapper) -> Vec { - vec![mapper.index(VMOp::Xor), mapper.index(self.width)] + vec![mapper.index(self.width)] } fn writes(&self) -> Vec { vec![Effect::Register(VMReg::Flags)] } - fn depth(&self) -> i32 { - -1 + fn consumes(&self) -> i32 { + 2 + } + + fn produces(&self) -> i32 { + 1 } } diff --git a/runtime/src/vm/functions/crypt.rs b/runtime/src/vm/functions/crypt.rs index 85ad99c7..4898cfae 100644 --- a/runtime/src/vm/functions/crypt.rs +++ b/runtime/src/vm/functions/crypt.rs @@ -1,5 +1,6 @@ use iced_x86::code_asm::{ - al, byte_ptr, cl, eax, ecx, ptr, qword_ptr, r12, r13, r14, r15, rax, rbx, rcx, rdx, word_ptr, + al, byte_ptr, cl, eax, ecx, edx, ptr, qword_ptr, r10, r10d, r11, r11d, r12, r13, r14, r15, r8, + r8d, r9d, rax, rbx, rcx, rdx, word_ptr, }; use crate::{ @@ -8,6 +9,7 @@ use crate::{ bytecode::VMReg, utils::{self}, }, + VM_CIPHER_ROUNDS, }; // void (bool) @@ -26,8 +28,9 @@ pub fn build(rt: &mut Runtime) { let mut derive_key = rt.asm.create_label(); let mut start_key = rt.asm.create_label(); let mut save_key = rt.asm.create_label(); + let mut stream = rt.asm.create_label(); let mut crypt_loop = rt.asm.create_label(); - let mut continue_loop = rt.asm.create_label(); + let mut round_loop = rt.asm.create_label(); let mut unlock = rt.asm.create_label(); let mut finish_encrypt = rt.asm.create_label(); let mut finish_decrypt = rt.asm.create_label(); @@ -119,7 +122,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.mov(rcx, ptr(0x1480 + rcx * 8).gs()).unwrap(); // jmp ... - rt.asm.jmp(crypt_loop).unwrap(); + rt.asm.jmp(stream).unwrap(); } // DECRYPTION @@ -169,7 +172,7 @@ pub fn build(rt: &mut Runtime) { { // lea rax, [...] rt.asm - .lea(rax, ptr(rt.data_labels[&DataDef::VmCode])) + .lea(rax, ptr(rt.data_labels[&DataDef::VmCodeStart])) .unwrap(); // movsxd rcx, [rax] rt.asm.movsxd(rcx, ptr(rax)).unwrap(); @@ -213,7 +216,7 @@ pub fn build(rt: &mut Runtime) { { // lea rcx, [...] rt.asm - .lea(rcx, ptr(rt.data_labels[&DataDef::VmCode])) + .lea(rcx, ptr(rt.data_labels[&DataDef::VmAttestation])) .unwrap(); // movsxd rax, [rcx] rt.asm.movsxd(rax, ptr(rcx)).unwrap(); @@ -242,10 +245,21 @@ pub fn build(rt: &mut Runtime) { rt.asm.set_label(&mut start_key).unwrap(); { - // mov rcx, [...] + // lea rcx, [...] rt.asm - .mov(rcx, ptr(rt.data_labels[&DataDef::VmKeySeed])) + .lea(rcx, ptr(rt.data_labels[&DataDef::VmAttestation])) .unwrap(); + // movsxd rax, [rcx] + rt.asm.movsxd(rax, ptr(rcx)).unwrap(); + // add rcx, rax + rt.asm.add(rcx, rax).unwrap(); + // mov rdx, r14 + rt.asm.mov(rdx, r14).unwrap(); + // call ... + rt.asm.call(rt.function_labels[&FnDef::VmInvoke]).unwrap(); + + // mov rcx, rax + rt.asm.mov(rcx, rax).unwrap(); } rt.asm.set_label(&mut save_key).unwrap(); @@ -259,6 +273,12 @@ pub fn build(rt: &mut Runtime) { } } + rt.asm.set_label(&mut stream).unwrap(); + { + // xor r8, r8 + rt.asm.xor(r8, r8).unwrap(); + } + rt.asm.set_label(&mut crypt_loop).unwrap(); { // cmp rbx, r15 @@ -266,42 +286,69 @@ pub fn build(rt: &mut Runtime) { // je ... rt.asm.je(unlock).unwrap(); - // mov rax, [rbx] - rt.asm.mov(rax, ptr(rbx)).unwrap(); - // xor [rbx], rcx - rt.asm.xor(ptr(rbx), rcx).unwrap(); - - // test r13, r13 - rt.asm.test(r13, r13).unwrap(); - // jnz ... - rt.asm.jnz(continue_loop).unwrap(); - - // mov rax, [rbx] - rt.asm.mov(rax, ptr(rbx)).unwrap(); - - rt.asm.set_label(&mut continue_loop).unwrap(); + // SPECK-64/128 keystream of the counter, keyed by the block key: + // mov r9d, ecx + rt.asm.mov(r9d, ecx).unwrap(); + // mov r10, rcx + rt.asm.mov(r10, rcx).unwrap(); + // shr r10, 0x20 + rt.asm.shr(r10, 0x20).unwrap(); + // mov r11, r8 + rt.asm.mov(r11, r8).unwrap(); + // shr r11, 0x20 + rt.asm.shr(r11, 0x20).unwrap(); + // mov edx, r8d + rt.asm.mov(edx, r8d).unwrap(); + // xor eax, eax + rt.asm.xor(eax, eax).unwrap(); + + rt.asm.set_label(&mut round_loop).unwrap(); { - // xor rcx, rax - rt.asm.xor(rcx, rax).unwrap(); - - // mov rax, [...] - rt.asm - .mov(rax, ptr(rt.data_labels[&DataDef::VmKeyMul])) - .unwrap(); - // imul rcx, rax - rt.asm.imul_2(rcx, rax).unwrap(); - // mov rax, [...] - rt.asm - .mov(rax, ptr(rt.data_labels[&DataDef::VmKeyAdd])) - .unwrap(); - // add rcx, rax - rt.asm.add(rcx, rax).unwrap(); - - // add rbx, 0x8 - rt.asm.add(rbx, 0x8).unwrap(); - // jmp ... - rt.asm.jmp(crypt_loop).unwrap(); + // ror r11d, 0x8 + rt.asm.ror(r11d, 8u32).unwrap(); + // add r11d, edx + rt.asm.add(r11d, edx).unwrap(); + // xor r11d, r9d + rt.asm.xor(r11d, r9d).unwrap(); + // rol edx, 0x3 + rt.asm.rol(edx, 3u32).unwrap(); + // xor edx, r11d + rt.asm.xor(edx, r11d).unwrap(); + + // ror r10d, 0x8 + rt.asm.ror(r10d, 8u32).unwrap(); + // add r10d, r9d + rt.asm.add(r10d, r9d).unwrap(); + // xor r10d, eax + rt.asm.xor(r10d, eax).unwrap(); + // rol r9d, 0x3 + rt.asm.rol(r9d, 3u32).unwrap(); + // xor r9d, r10d + rt.asm.xor(r9d, r10d).unwrap(); + + // inc eax + rt.asm.inc(eax).unwrap(); + // cmp eax, ... + rt.asm.cmp(eax, VM_CIPHER_ROUNDS as i32).unwrap(); + // jb ... + rt.asm.jb(round_loop).unwrap(); } + + // shl r11, 0x20 + rt.asm.shl(r11, 0x20).unwrap(); + // mov eax, edx + rt.asm.mov(eax, edx).unwrap(); + // or r11, rax + rt.asm.or(r11, rax).unwrap(); + // xor [rbx], r11 + rt.asm.xor(ptr(rbx), r11).unwrap(); + + // add rbx, 0x8 + rt.asm.add(rbx, 0x8).unwrap(); + // inc r8 + rt.asm.inc(r8).unwrap(); + // jmp ... + rt.asm.jmp(crypt_loop).unwrap(); } rt.asm.set_label(&mut unlock).unwrap(); diff --git a/runtime/src/vm/functions/dispatch.rs b/runtime/src/vm/functions/dispatch.rs index 19bce8af..e532d6e9 100644 --- a/runtime/src/vm/functions/dispatch.rs +++ b/runtime/src/vm/functions/dispatch.rs @@ -5,18 +5,18 @@ use iced_x86::code_asm::{ use crate::{ mapper::Mappable, - runtime::{FnDef, ImportDef, Runtime, StringDef}, + runtime::{DataDef, FnDef, ImportDef, Runtime, StringDef}, vm::{ bytecode::{VMOp, VMReg}, utils::{self, lock}, }, - VM_DISPATCH_SIZE, VM_INTEGRITY_QWORD, VM_TRAMPOLINE_SIZE, + VM_DISPATCH_SIZE, VM_INTEGRITY_QWORD, VM_REDIRECT_SIZE, VM_TRAMPOLINE_SIZE, }; #[cfg(feature = "profile")] use crate::debug::{start_profiling, stop_profiling}; -const HANDLERS: [(VMOp, FnDef); VMOp::COUNT] = [ +pub const HANDLERS: [(VMOp, FnDef); VMOp::COUNT] = [ (VMOp::Jcc, FnDef::VmHandlerJcc), (VMOp::Ret, FnDef::VmHandlerRet), (VMOp::LoadImmediate, FnDef::VmHandlerLoadImmediate), @@ -30,6 +30,8 @@ const HANDLERS: [(VMOp, FnDef); VMOp::COUNT] = [ (VMOp::StoreExtend, FnDef::VmHandlerStoreExtend), (VMOp::Add, FnDef::VmHandlerAdd), (VMOp::Sub, FnDef::VmHandlerSub), + (VMOp::Adc, FnDef::VmHandlerAdc), + (VMOp::Sbb, FnDef::VmHandlerSbb), (VMOp::Exchange, FnDef::VmHandlerExchange), (VMOp::ExchangeAdd, FnDef::VmHandlerExchangeAdd), (VMOp::CompareExchange, FnDef::VmHandlerCompareExchange), @@ -64,6 +66,7 @@ const HANDLERS: [(VMOp, FnDef); VMOp::COUNT] = [ (VMOp::VectorMul, FnDef::VmHandlerVectorMul), (VMOp::VectorDiv, FnDef::VmHandlerVectorDiv), (VMOp::Timestamp, FnDef::VmHandlerTimestamp), + (VMOp::Dispatch, FnDef::VmHandlerDispatch), ]; pub fn build(rt: &mut Runtime) { @@ -76,6 +79,8 @@ pub fn build(rt: &mut Runtime) { let mut check_suspend = rt.asm.create_label(); let mut check_exit = rt.asm.create_label(); let mut resolved = rt.asm.create_label(); + let mut trampoline = rt.asm.create_label(); + let mut lookup = rt.asm.create_label(); let mut tamper = rt.asm.create_label(); let mut terminate = rt.asm.create_label(); let mut epilogue = rt.asm.create_label(); @@ -126,7 +131,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.set_label(&mut decrypt_block).unwrap(); { #[cfg(feature = "profile")] - start_profiling(rt, "vm_crypt_decrypt"); + start_profiling(rt); // Decrypt the block: // mov rcx, 0x1 @@ -135,7 +140,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.call(rt.function_labels[&FnDef::VmCrypt]).unwrap(); #[cfg(feature = "profile")] - stop_profiling(rt, "vm_crypt_decrypt"); + stop_profiling(rt, FnDef::VmCrypt, "decrypt"); // mov rax, ... rt.asm.mov(rax, VM_INTEGRITY_QWORD).unwrap(); @@ -149,10 +154,27 @@ pub fn build(rt: &mut Runtime) { { // mov [r12 + ...], 0x0 utils::vreg::store_imm(rt, r12, 0x0, VMReg::NBranch); - // mov [r12 + ...], 0x0 - utils::vreg::store_imm(rt, r12, 0x0, VMReg::VImmAdd); - // mov [r12 + ...], 0x1 - utils::vreg::store_imm(rt, r12, 0x1, VMReg::VImmMul); + + // lea rax, [...] + rt.asm + .lea(rax, ptr(rt.data_labels[&DataDef::VmCodeStart])) + .unwrap(); + // movsxd rcx, [rax] + rt.asm.movsxd(rcx, ptr(rax)).unwrap(); + // add rax, rcx + rt.asm.add(rax, rcx).unwrap(); + // mov rcx, r13 + rt.asm.mov(rcx, r13).unwrap(); + // sub rcx, 0x2 + rt.asm.sub(rcx, 0x2).unwrap(); + // sub rcx, rax + rt.asm.sub(rcx, rax).unwrap(); + // mov [r12 + ...], rcx + utils::vreg::store_reg(rt, r12, rcx, VMReg::VImmAdd); + // or rcx, 0x1 + rt.asm.or(rcx, 0x1).unwrap(); + // mov [r12 + ...], rcx + utils::vreg::store_reg(rt, r12, rcx, VMReg::VImmMul); } rt.asm.set_label(&mut execute_loop).unwrap(); @@ -170,16 +192,30 @@ pub fn build(rt: &mut Runtime) { // r8d -> operation utils::bytecode::read_byte_zx(rt, r13, r8d); - // println!( - // "{}", - // VMOp::VARIANTS - // .iter() - // .map(|op| format!("{:?}={:016X}", op, rt.mapper.index(*op))) - // .collect::>() - // .join("\n") - // ); + #[cfg(feature = "profile")] + { + use crate::debug::print_thread_message; + + let mut epilogue = rt.asm.create_label(); + + let mut cases = Vec::new(); + + for op in VMOp::VARIANTS { + cases.push((rt.mapper.index(*op), rt.asm.create_label())); + } + + rt.jumps(r8, cases.clone()); + + for (op, (_, mut label)) in VMOp::VARIANTS.iter().zip(cases) { + rt.asm.set_label(&mut label).unwrap(); + + print_thread_message(rt, &format!("{:?}", op), None, None); - // crate::debug::print_thread_message(rt, "Executing", Some(r8), None); + rt.asm.jmp(epilogue).unwrap(); + } + + rt.asm.set_label(&mut epilogue).unwrap(); + } // mov rcx, r13 rt.asm.mov(rcx, r13).unwrap(); @@ -241,7 +277,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.set_label(&mut check_exit).unwrap(); { #[cfg(feature = "profile")] - start_profiling(rt, "vm_crypt_encrypt"); + start_profiling(rt); // Re-encrypt the current block: // xor rcx, rcx @@ -250,7 +286,7 @@ pub fn build(rt: &mut Runtime) { rt.asm.call(rt.function_labels[&FnDef::VmCrypt]).unwrap(); #[cfg(feature = "profile")] - stop_profiling(rt, "vm_crypt_encrypt"); + stop_profiling(rt, FnDef::VmCrypt, "encrypt"); // Compute the address where execution will continue: // mov rax, [r12 + ...] @@ -266,9 +302,9 @@ pub fn build(rt: &mut Runtime) { // je ... rt.asm.je(epilogue).unwrap(); - // Follow an indirect JMP rel32 entry into its trampoline: - // cmp [rax], 0xE9 - rt.asm.cmp(byte_ptr(rax), 0xE9).unwrap(); + // Follow an indirect CALL rel32 entry into its trampoline: + // cmp [rax], 0xE8 + rt.asm.cmp(byte_ptr(rax), 0xE8).unwrap(); // jne ... rt.asm.jne(resolved).unwrap(); // movsxd r9, [rax + 0x1] @@ -283,14 +319,31 @@ pub fn build(rt: &mut Runtime) { // cmp [rax], 0x68 rt.asm.cmp(byte_ptr(rax), 0x68).unwrap(); // jne ... - rt.asm.jne(epilogue).unwrap(); + rt.asm.jne(trampoline).unwrap(); // mov edx, [rax + 0x1] rt.asm.mov(edx, ptr(rax + 0x1)).unwrap(); - // add rax, ... rt.asm.add(rax, VM_DISPATCH_SIZE as i32).unwrap(); + // jmp ... + rt.asm.jmp(lookup).unwrap(); + } + + rt.asm.set_label(&mut trampoline).unwrap(); + { + // cmp [rax], 0xC7 + rt.asm.cmp(byte_ptr(rax), 0xC7).unwrap(); + // jne ... + rt.asm.jne(epilogue).unwrap(); + // mov edx, [rax + 0x3] + rt.asm.mov(edx, ptr(rax + 0x3)).unwrap(); + // add rax, ... + rt.asm.add(rax, VM_REDIRECT_SIZE as i32).unwrap(); + } + + rt.asm.set_label(&mut lookup).unwrap(); + { // mov rcx, rax rt.asm.mov(rcx, rax).unwrap(); // call ... diff --git a/runtime/src/vm/functions/entry.rs b/runtime/src/vm/functions/entry.rs index 457b99e3..6ab6857c 100644 --- a/runtime/src/vm/functions/entry.rs +++ b/runtime/src/vm/functions/entry.rs @@ -166,13 +166,13 @@ pub fn build(rt: &mut Runtime) { } #[cfg(feature = "profile")] - start_profiling(rt, "vm_dispatch"); + start_profiling(rt); // call ... rt.asm.call(rt.function_labels[&FnDef::VmDispatch]).unwrap(); #[cfg(feature = "profile")] - stop_profiling(rt, "vm_dispatch"); + stop_profiling(rt, FnDef::VmDispatch, "dispatch"); // jmp ... rt.asm.jmp(rt.function_labels[&FnDef::VmExit]).unwrap(); diff --git a/runtime/src/vm/functions/invoke.rs b/runtime/src/vm/functions/invoke.rs index 027af008..091ca761 100644 --- a/runtime/src/vm/functions/invoke.rs +++ b/runtime/src/vm/functions/invoke.rs @@ -1,7 +1,7 @@ -use iced_x86::code_asm::{ptr, r12, r13, r14, r15, rax, rbp, rbx, rcx, rdi, rdx, rsi, rsp}; +use iced_x86::code_asm::{ptr, r12, r13, r13d, r14, r15, rax, rbp, rbx, rcx, rdi, rdx, rsi, rsp}; use crate::{ - runtime::{FnDef, Runtime}, + runtime::{DataDef, FnDef, Runtime}, vm::{ bytecode::VMReg, utils::{self, scratch}, @@ -80,8 +80,15 @@ pub fn build(rt: &mut Runtime) { rt.asm.set_label(&mut execute_loop).unwrap(); { + // mov r13d, [...] + rt.asm + .mov(r13d, ptr(rt.data_labels[&DataDef::VmRegistersTlsIndex])) + .unwrap(); + // mov r13, gs:[0x1480 + r13*8] + rt.asm.mov(r13, ptr(0x1480 + r13 * 8).gs()).unwrap(); + // store rsp - scratch::store(rt, r12, rsp); + scratch::store(rt, r13, rsp); // call ... rt.asm.call(rt.function_labels[&FnDef::VmDispatch]).unwrap(); @@ -91,16 +98,23 @@ pub fn build(rt: &mut Runtime) { rt.asm.set_label(&mut execute_continue).unwrap(); + // call ... + rt.asm + .call(rt.function_labels[&FnDef::VmRegistersCaptureVolatile]) + .unwrap(); + // mov [r12 + ...], rsp utils::vreg::store_reg(rt, r12, rsp, VMReg::Rsp); - // load rsp - scratch::load(rt, r12, rsp); - - // call ... + // mov r13d, [...] rt.asm - .call(rt.function_labels[&FnDef::VmRegistersCaptureVolatile]) + .mov(r13d, ptr(rt.data_labels[&DataDef::VmRegistersTlsIndex])) .unwrap(); + // mov r13, gs:[0x1480 + r13*8] + rt.asm.mov(r13, ptr(0x1480 + r13 * 8).gs()).unwrap(); + + // load rsp + scratch::load(rt, r13, rsp); // cmp [r12 + ...], 0x0 utils::vreg::cmp_imm(rt, r12, VMReg::BResume, 0x0); diff --git a/runtime/src/vm/functions/lookup.rs b/runtime/src/vm/functions/lookup.rs index 2d5830e5..b570c810 100644 --- a/runtime/src/vm/functions/lookup.rs +++ b/runtime/src/vm/functions/lookup.rs @@ -6,7 +6,6 @@ use crate::{ bytecode::VMReg, utils::{self}, }, - VM_DISPATCH_SIZE, }; // unsigned char* (unsigned char*, unsigned int) @@ -22,40 +21,52 @@ pub fn build(rt: &mut Runtime) { rt.asm.xor(edx, r8d).unwrap(); // and edx, 0x0FFFFFFF rt.asm.and(edx, 0x0FFFFFFF).unwrap(); + // sub edx, ... + rt.asm.sub(edx, rt.keys.addend as u32 as i32).unwrap(); + // imul edx, edx, ... + rt.asm + .imul_3( + edx, + edx, + crate::utils::invert_multiplier(rt.keys.multiplier) as u32 as i32, + ) + .unwrap(); + // and edx, 0x0FFFFFFF + rt.asm.and(edx, 0x0FFFFFFF).unwrap(); // lea r8, [...] rt.asm .lea(r8, ptr(rt.data_labels[&DataDef::VmTable])) .unwrap(); - // lea r8, [r8 + rdx*8] - rt.asm.lea(r8, ptr(r8 + rdx * 8)).unwrap(); + // lea rax, [rdx + rdx*2] + rt.asm.lea(rax, ptr(rdx + rdx * 2)).unwrap(); + // lea r8, [r8 + rax*4] + rt.asm.lea(r8, ptr(r8 + rax * 4)).unwrap(); - // Read the displacement and the offset into bytecode from the table: + // Apply the exit displacement of the caller stub to the return address: // movsxd rax, [r8] rt.asm.movsxd(rax, dword_ptr(r8)).unwrap(); - // mov edx, [r8 + 0x4] - rt.asm.mov(edx, ptr(r8 + 0x4)).unwrap(); + // add rax, rcx + rt.asm.add(rax, rcx).unwrap(); + // mov [r12 + ...], rax + utils::vreg::store_reg(rt, r12, rax, VMReg::NExit); - // Apply the displacement of the caller stub to the native exit point: - // mov r8, rcx - rt.asm.mov(r8, rcx).unwrap(); - // add r8, rax - rt.asm.add(r8, rax).unwrap(); - // mov [r12 + ...], r8 - utils::vreg::store_reg(rt, r12, r8, VMReg::NExit); + // Apply the entry displacement of the caller stub to the return address: + // movsxd rax, [r8 + 0x4] + rt.asm.movsxd(rax, dword_ptr(r8 + 0x4)).unwrap(); + // add rax, rcx + rt.asm.add(rax, rcx).unwrap(); + // mov [r12 + ...], rax + utils::vreg::store_reg(rt, r12, rax, VMReg::NEntry); - // Apply the displacement of the caller stub to the native entry point: - // mov r8, rcx - rt.asm.mov(r8, rcx).unwrap(); - // sub r8, ... - rt.asm.sub(r8, VM_DISPATCH_SIZE as i32).unwrap(); - // mov [r12 + ...], r8 - utils::vreg::store_reg(rt, r12, r8, VMReg::NEntry); + // Read the offset into bytecode from the table: + // mov edx, [r8 + 0x8] + rt.asm.mov(edx, ptr(r8 + 0x8)).unwrap(); // Compute the block pointer from the offset into bytecode: // lea rax, [...] rt.asm - .lea(rax, ptr(rt.data_labels[&DataDef::VmCode])) + .lea(rax, ptr(rt.data_labels[&DataDef::VmCodeStart])) .unwrap(); // movsxd rcx, [rax] rt.asm.movsxd(rcx, ptr(rax)).unwrap(); diff --git a/runtime/src/vm/functions/veh.rs b/runtime/src/vm/functions/veh.rs index 1905f180..478d6525 100644 --- a/runtime/src/vm/functions/veh.rs +++ b/runtime/src/vm/functions/veh.rs @@ -60,9 +60,9 @@ const VM_TO_CONTEXT: &[(VMReg, i32)] = &[ (VMReg::R9, 0xC0), (VMReg::R10, 0xC8), (VMReg::R11, 0xD0), - (VMReg::R13, 0xD8), - (VMReg::R14, 0xE0), - (VMReg::R15, 0xE8), + (VMReg::R12, 0xD8), + (VMReg::R13, 0xE0), + (VMReg::R14, 0xE8), (VMReg::R15, 0xF0), (VMReg::NEntry, 0xF8), (VMReg::Flags, 0x44), @@ -99,7 +99,7 @@ pub fn handler(rt: &mut Runtime) { #[cfg(debug_assertions)] { - use crate::debug; + use crate::debug::print_thread_message; let mut software_exception = rt.asm.create_label(); @@ -112,21 +112,21 @@ pub fn handler(rt: &mut Runtime) { // jne ... rt.asm.jne(software_exception).unwrap(); - debug::print_thread_message(rt, "ExceptionCode: ", Some(rcx), None); + print_thread_message(rt, "ExceptionCode: ", Some(rcx), None); // mov rax, [r14 + 0x20] -> ULONG_PTR EXCEPTION_RECORD->ExceptionInformation[0] rt.asm.mov(rax, ptr(r14 + 0x20)).unwrap(); - debug::print_thread_message(rt, "ExceptionInformation[0]: ", Some(rax), None); + print_thread_message(rt, "ExceptionInformation[0]: ", Some(rax), None); // mov rax, [r14 + 0x28] -> ULONG_PTR EXCEPTION_RECORD->ExceptionInformation[1] rt.asm.mov(rax, ptr(r14 + 0x28)).unwrap(); - debug::print_thread_message(rt, "ExceptionInformation[1]: ", Some(rax), None); + print_thread_message(rt, "ExceptionInformation[1]: ", Some(rax), None); // mov rax, [rdx] -> ExceptionAddress[0..8] rt.asm.mov(rax, ptr(rdx)).unwrap(); - debug::print_thread_message(rt, "ExceptionAddress[0..8]: ", Some(rax), None); + print_thread_message(rt, "ExceptionAddress[0..8]: ", Some(rax), None); // mov rax, [rdx + 0x7] -> ExceptionAddress[7..15] rt.asm.mov(rax, ptr(rdx + 0x7)).unwrap(); - debug::print_thread_message(rt, "ExceptionAddress[7..15]: ", Some(rax), None); + print_thread_message(rt, "ExceptionAddress[7..15]: ", Some(rax), None); // mov rax, gs:[0x60] -> PEB *TEB->ProcessEnvironmentBlock rt.asm.mov(rax, ptr(0x60).gs()).unwrap(); @@ -135,7 +135,7 @@ pub fn handler(rt: &mut Runtime) { // sub rdx, rax rt.asm.sub(rdx, rax).unwrap(); - debug::print_thread_message(rt, "ExceptionAddress: ", Some(rdx), None); + print_thread_message(rt, "ExceptionAddress: ", Some(rdx), None); rt.asm.set_label(&mut software_exception).unwrap(); } diff --git a/runtime/src/vm/handlers/adc.rs b/runtime/src/vm/handlers/adc.rs new file mode 100644 index 00000000..11ede6ef --- /dev/null +++ b/runtime/src/vm/handlers/adc.rs @@ -0,0 +1,102 @@ +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Add( + Expression::Operand(Operand::Input(0)), + Expression::Sub( + Box::new(Expression::Sub( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitNot(Box::new(Expression::Flag(Flag::Carry)))), + )), + Box::new(Expression::Constant(1)), + ), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitOr( + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::BitNot(Box::new(Expression::Operand( + Operand::Output(0), + )))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/add.rs b/runtime/src/vm/handlers/add.rs index c087180e..9e762533 100644 --- a/runtime/src/vm/handlers/add.rs +++ b/runtime/src/vm/handlers/add.rs @@ -1,10 +1,82 @@ -crate::vm::handlers::arithmetic!( - add, - r8, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Add( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::LessThan( + Expression::Operand(Operand::Output(0)), + Expression::Operand(Operand::Input(0)), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/and.rs b/runtime/src/vm/handlers/and.rs index abbb6964..d37ea58c 100644 --- a/runtime/src/vm/handlers/and.rs +++ b/runtime/src/vm/handlers/and.rs @@ -1,10 +1,60 @@ -crate::vm::handlers::arithmetic!( - and, - r8, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::And( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/bit_scan_reverse.rs b/runtime/src/vm/handlers/bit_scan_reverse.rs index c52fa5b2..5b8ecd62 100644 --- a/runtime/src/vm/handlers/bit_scan_reverse.rs +++ b/runtime/src/vm/handlers/bit_scan_reverse.rs @@ -1 +1,28 @@ -crate::vm::handlers::arithmetic!(bsr, bitscan, crate::vm::bytecode::Flag::Zero.bit64()); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![ + Effect::Assign(Expression::Operand(Operand::Input(0))), + Effect::Bsr(Expression::Operand(Operand::Input(1))), + ], + flags: Flags::Always(vec![( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Input(1)), + Expression::Constant(0), + ))), + )]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/bit_test.rs b/runtime/src/vm/handlers/bit_test.rs index ad1cfba7..d220eed4 100644 --- a/runtime/src/vm/handlers/bit_test.rs +++ b/runtime/src/vm/handlers/bit_test.rs @@ -1 +1,28 @@ -crate::vm::handlers::arithmetic!(bt, bitscan, crate::vm::bytecode::Flag::Carry.bit64()); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![], + flags: Flags::Always(vec![( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + ), + ))), + )]), + stores: Some(vec![Expression::Operand(Operand::Input(0))]), + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/bit_test_complement.rs b/runtime/src/vm/handlers/bit_test_complement.rs index 3871a04a..c1c88eef 100644 --- a/runtime/src/vm/handlers/bit_test_complement.rs +++ b/runtime/src/vm/handlers/bit_test_complement.rs @@ -1 +1,37 @@ -crate::vm::handlers::arithmetic!(btc, bitscan, crate::vm::bytecode::Flag::Carry.bit64()); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Xor( + Expression::Operand(Operand::Input(0)), + Expression::BitShl( + Box::new(Expression::Constant(1)), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + )), + ), + )], + flags: Flags::Always(vec![( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + ), + ))), + )]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/bit_test_reset.rs b/runtime/src/vm/handlers/bit_test_reset.rs index a99fa749..56009a05 100644 --- a/runtime/src/vm/handlers/bit_test_reset.rs +++ b/runtime/src/vm/handlers/bit_test_reset.rs @@ -1 +1,37 @@ -crate::vm::handlers::arithmetic!(btr, bitscan, crate::vm::bytecode::Flag::Carry.bit64()); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::And( + Expression::Operand(Operand::Input(0)), + Expression::BitNot(Box::new(Expression::BitShl( + Box::new(Expression::Constant(1)), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + )), + ))), + )], + flags: Flags::Always(vec![( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + ), + ))), + )]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/bit_test_set.rs b/runtime/src/vm/handlers/bit_test_set.rs index 5700844f..c8513b0e 100644 --- a/runtime/src/vm/handlers/bit_test_set.rs +++ b/runtime/src/vm/handlers/bit_test_set.rs @@ -1 +1,37 @@ -crate::vm::handlers::arithmetic!(bts, bitscan, crate::vm::bytecode::Flag::Carry.bit64()); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Or( + Expression::Operand(Operand::Input(0)), + Expression::BitShl( + Box::new(Expression::Constant(1)), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + )), + ), + )], + flags: Flags::Always(vec![( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::SignBit), + ), + ))), + )]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/byte_swap.rs b/runtime/src/vm/handlers/byte_swap.rs index 5144dfc9..a966e126 100644 --- a/runtime/src/vm/handlers/byte_swap.rs +++ b/runtime/src/vm/handlers/byte_swap.rs @@ -1,50 +1,121 @@ -use iced_x86::code_asm::{al, eax, r12, r8, r8d, rax, rcx}; - use crate::{ runtime::Runtime, vm::{ bytecode::VMWidth, - utils::{self, scratch}, + handlers::semantic::{self, Effect, Expression, Flags, Operand, Operation}, }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut wide = rt.asm.create_label(); - let mut epilogue = rt.asm.create_label(); - - // eax -> width - utils::bytecode::read_byte_zx(rt, rcx, eax); - - // load r8 - scratch::load(rt, r12, r8); - - // cmp al, ... - rt.asm - .cmp(al, rt.mapper.index(VMWidth::Lower64) as i32) - .unwrap(); - // je ... - rt.asm.je(wide).unwrap(); - - // bswap r8d - rt.asm.bswap(r8d).unwrap(); - // jmp ... - rt.asm.jmp(epilogue).unwrap(); - - rt.asm.set_label(&mut wide).unwrap(); - { - // bswap r8 - rt.asm.bswap(r8).unwrap(); - } - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // store r8 - scratch::store(rt, r12, r8); - - // mov rax, rcx - rt.asm.mov(rax, rcx).unwrap(); - // ret - rt.asm.ret().unwrap(); - } + semantic::compiler::compile( + rt, + &Operation { + effects: vec![ + Effect::Assign(Expression::BitAnd( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(8)), + )), + )), + Box::new(Expression::Constant(0xFF)), + )), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(24)), + )), + )), + Box::new(Expression::Constant(0xFF00)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(40)), + )), + )), + Box::new(Expression::Constant(0xFF_0000)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(56)), + )), + )), + Box::new(Expression::Constant(0xFF_000000)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShl( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(56)), + )), + )), + Box::new(Expression::ByteMask(3)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShl( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(40)), + )), + )), + Box::new(Expression::ByteMask(2)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShl( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(24)), + )), + )), + Box::new(Expression::ByteMask(1)), + ), + ), + Effect::Or( + Expression::Operand(Operand::Output(0)), + Expression::BitAnd( + Box::new(Expression::BitShl( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::Constant(8)), + )), + )), + Box::new(Expression::ByteMask(0)), + ), + ), + ], + flags: Flags::Always(vec![]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32], + }, + ); } diff --git a/runtime/src/vm/handlers/compare_exchange.rs b/runtime/src/vm/handlers/compare_exchange.rs index 750e4fea..76e4b467 100644 --- a/runtime/src/vm/handlers/compare_exchange.rs +++ b/runtime/src/vm/handlers/compare_exchange.rs @@ -1,90 +1,98 @@ -use iced_x86::code_asm::{edx, ptr, r12, r13, r8, r9, r9b, r9d, r9w, rax, rcx, rdx}; - use crate::{ - runtime::{FnDef, Runtime}, + runtime::Runtime, vm::{ - bytecode::Flag, - utils::{self, scratch}, + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // push r13 - rt.asm.push(r13).unwrap(); - // mov r13, rcx - rt.asm.mov(r13, rcx).unwrap(); - - // edx -> width - utils::bytecode::read_byte_zx(rt, r13, edx); - - // load r8 - scratch::load(rt, r12, r8); - // load r9 - scratch::load(rt, r12, r9); - // load rax - scratch::load(rt, r12, rax); - - utils::width::dispatch( + semantic::compiler::compile( rt, - rdx, - &mut epilogue, - Some(Box::new(|rt| { - // lock cmpxchg [r8], r9 - rt.asm.lock().cmpxchg(ptr(r8), r9).unwrap(); - })), - Some(Box::new(|rt| { - // lock cmpxchg [r8], r9d - rt.asm.lock().cmpxchg(ptr(r8), r9d).unwrap(); - })), - None, - Some(Box::new(|rt| { - // lock cmpxchg [r8], r9w - rt.asm.lock().cmpxchg(ptr(r8), r9w).unwrap(); - })), - None, - Some(Box::new(|rt| { - // lock cmpxchg [r8], r9b - rt.asm.lock().cmpxchg(ptr(r8), r9b).unwrap(); - })), - None, - None, - None, - None, - None, - None, + &Operation { + effects: vec![Effect::CompareExchange( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + Expression::Operand(Operand::Input(2)), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Sub( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Sub( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::LessThan( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Output(0)), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Sub( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Sub( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + ))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + Box::new(Expression::Sub( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, ); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // pushfq - rt.asm.pushfq().unwrap(); - - // store rax - scratch::store(rt, r12, rax); - - // mov rcx, ... - rt.asm - .mov( - rcx, - Flag::Carry.bit64() - | Flag::Parity.bit64() - | Flag::Auxiliary.bit64() - | Flag::Zero.bit64() - | Flag::Sign.bit64() - | Flag::Overflow.bit64(), - ) - .unwrap(); - // call ... - rt.asm.call(rt.function_labels[&FnDef::VmFlags]).unwrap(); - - // mov rax, r13 - rt.asm.mov(rax, r13).unwrap(); - // pop r13 - rt.asm.pop(r13).unwrap(); - // ret - rt.asm.ret().unwrap(); - } } diff --git a/runtime/src/vm/handlers/discard.rs b/runtime/src/vm/handlers/discard.rs index ba825590..9c41f51a 100644 --- a/runtime/src/vm/handlers/discard.rs +++ b/runtime/src/vm/handlers/discard.rs @@ -1,11 +1,14 @@ -use iced_x86::code_asm::{r12, r9, rax, rcx}; +use iced_x86::code_asm::{r12, rax, rcx}; -use crate::{runtime::Runtime, vm::utils::scratch}; +use crate::{ + runtime::Runtime, + vm::{bytecode::VMReg, utils::vreg}, +}; // unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - // load r9 - scratch::load(rt, r12, r9); + // add [r12 + ...], 0x8 + vreg::add_imm(rt, r12, 0x8, VMReg::VScratch); // mov rax, rcx rt.asm.mov(rax, rcx).unwrap(); diff --git a/runtime/src/vm/handlers/dispatch.rs b/runtime/src/vm/handlers/dispatch.rs new file mode 100644 index 00000000..ed83032b --- /dev/null +++ b/runtime/src/vm/handlers/dispatch.rs @@ -0,0 +1,48 @@ +use iced_x86::code_asm::{r12, r8, CodeLabel}; + +use crate::{ + runtime::Runtime, + vm::{functions::dispatch::HANDLERS, utils::scratch}, +}; + +pub fn build(rt: &mut Runtime) { + // load r8 + scratch::load(rt, r12, r8); + + #[cfg(feature = "profile")] + { + use crate::debug::print_thread_message; + use crate::mapper::Mappable; + use crate::vm::bytecode::VMOp; + + let mut epilogue = rt.asm.create_label(); + + let mut cases = Vec::new(); + + for op in VMOp::VARIANTS { + cases.push((rt.mapper.index(*op), rt.asm.create_label())); + } + + rt.jumps(r8, cases.clone()); + + for (op, (_, mut label)) in VMOp::VARIANTS.iter().zip(cases) { + rt.asm.set_label(&mut label).unwrap(); + + print_thread_message(rt, &format!("{:?}", op), None, None); + + rt.asm.jmp(epilogue).unwrap(); + } + + rt.asm.set_label(&mut epilogue).unwrap(); + } + + let cases = HANDLERS + .iter() + .map(|&(op, def)| (rt.mapper.index(op), rt.function_labels[&def])) + .collect::>(); + + rt.calls(r8, cases); + + // ret + rt.asm.ret().unwrap(); +} diff --git a/runtime/src/vm/handlers/div.rs b/runtime/src/vm/handlers/div.rs index 8bdd2c77..63874f54 100644 --- a/runtime/src/vm/handlers/div.rs +++ b/runtime/src/vm/handlers/div.rs @@ -1,142 +1,32 @@ -use iced_x86::code_asm::*; - use crate::{ runtime::Runtime, - vm::utils::{self, scratch}, + vm::{ + bytecode::VMWidth, + handlers::semantic::{self, Effect, Expression, Flags, Operand, Operation}, + }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // push r13 - rt.asm.push(r13).unwrap(); - // push r14 - rt.asm.push(r14).unwrap(); - - // mov r13, rcx - rt.asm.mov(r13, rcx).unwrap(); - - // rax -> width - utils::bytecode::read_byte_zx(rt, r13, eax); - - // load r8 - scratch::load(rt, r12, r8); - // load r9 - scratch::load(rt, r12, r9); - // load r14 - scratch::load(rt, r12, r14); - - utils::width::dispatch( + semantic::compiler::compile( rt, - rax, - &mut epilogue, - Some(Box::new(|rt| wide(rt, false))), - Some(Box::new(|rt| dword(rt, false))), - None, - Some(Box::new(|rt| word(rt, false))), - None, - Some(Box::new(|rt| byte(rt, false))), - Some(Box::new(|rt| wide(rt, true))), - Some(Box::new(|rt| dword(rt, true))), - Some(Box::new(|rt| word(rt, true))), - Some(Box::new(|rt| byte(rt, true))), - None, - None, + &Operation { + effects: vec![Effect::Div( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + Expression::Operand(Operand::Input(2)), + )], + flags: Flags::Always(vec![]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + VMWidth::SLower64, + VMWidth::SLower32, + VMWidth::SLower16, + VMWidth::SLower8, + ], + }, ); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // store r8 - scratch::store(rt, r12, r8); - // store r9 - scratch::store(rt, r12, r9); - - // mov rax, r13 - rt.asm.mov(rax, r13).unwrap(); - // pop r14 - rt.asm.pop(r14).unwrap(); - // pop r13 - rt.asm.pop(r13).unwrap(); - // ret - rt.asm.ret().unwrap(); - } -} - -fn wide(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - // mov rdx, r8 - rt.asm.mov(rdx, r8).unwrap(); - if signed { - // rax = rdx:rax / r14, rdx = rdx:rax % r14 - rt.asm.idiv(r14).unwrap(); - } else { - // rax = rdx:rax / r14, rdx = rdx:rax % r14 - rt.asm.div(r14).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn dword(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - // mov rdx, r8 - rt.asm.mov(rdx, r8).unwrap(); - if signed { - // eax = edx:eax / r14d, edx = edx:eax % r14d - rt.asm.idiv(r14d).unwrap(); - } else { - // eax = edx:eax / r14d, edx = edx:eax % r14d - rt.asm.div(r14d).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn word(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - // mov rdx, r8 - rt.asm.mov(rdx, r8).unwrap(); - if signed { - // ax = dx:ax / r14w, dx = dx:ax % r14w - rt.asm.idiv(r14w).unwrap(); - } else { - // ax = dx:ax / r14w, dx = dx:ax % r14w - rt.asm.div(r14w).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn byte(rt: &mut Runtime, signed: bool) { - // movzx ecx, r8b - rt.asm.movzx(ecx, r8b).unwrap(); - // shl ecx, 0x8 - rt.asm.shl(ecx, 0x8i32).unwrap(); - // movzx eax, r9b - rt.asm.movzx(eax, r9b).unwrap(); - // or eax, ecx - rt.asm.or(eax, ecx).unwrap(); - if signed { - // al = ax / r14b, ah = ax % r14b - rt.asm.idiv(r14b).unwrap(); - } else { - // al = ax / r14b, ah = ax % r14b - rt.asm.div(r14b).unwrap(); - } - // movzx ecx, ah - rt.asm.movzx(ecx, ah).unwrap(); - // movzx r9d, al - rt.asm.movzx(r9d, al).unwrap(); - // mov r8, rcx - rt.asm.mov(r8, rcx).unwrap(); } diff --git a/runtime/src/vm/handlers/exchange.rs b/runtime/src/vm/handlers/exchange.rs index a2e6c8bd..94ebe025 100644 --- a/runtime/src/vm/handlers/exchange.rs +++ b/runtime/src/vm/handlers/exchange.rs @@ -1,60 +1,27 @@ -use iced_x86::code_asm::{eax, ptr, r12, r8, r9, r9b, r9d, r9w, rax, rcx}; - use crate::{ runtime::Runtime, - vm::utils::{self, scratch}, + vm::{ + bytecode::VMWidth, + handlers::semantic::{self, Effect, Expression, Flags, Operand, Operation}, + }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // eax -> width - utils::bytecode::read_byte_zx(rt, rcx, eax); - - // load r8 - scratch::load(rt, r12, r8); - // load r9 - scratch::load(rt, r12, r9); - - utils::width::dispatch( + semantic::compiler::compile( rt, - rax, - &mut epilogue, - Some(Box::new(|rt| { - // xchg [r8], r9 - rt.asm.xchg(ptr(r8), r9).unwrap(); - })), - Some(Box::new(|rt| { - // xchg [r8], r9d - rt.asm.xchg(ptr(r8), r9d).unwrap(); - })), - None, - Some(Box::new(|rt| { - // xchg [r8], r9w - rt.asm.xchg(ptr(r8), r9w).unwrap(); - })), - None, - Some(Box::new(|rt| { - // xchg [r8], r9b - rt.asm.xchg(ptr(r8), r9b).unwrap(); - })), - None, - None, - None, - None, - None, - None, + &Operation { + effects: vec![Effect::Exchange( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, ); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // store r9 - scratch::store(rt, r12, r9); - - // mov rax, rcx - rt.asm.mov(rax, rcx).unwrap(); - // ret - rt.asm.ret().unwrap(); - } } diff --git a/runtime/src/vm/handlers/exchange_add.rs b/runtime/src/vm/handlers/exchange_add.rs index d6112058..fb5761cf 100644 --- a/runtime/src/vm/handlers/exchange_add.rs +++ b/runtime/src/vm/handlers/exchange_add.rs @@ -1,87 +1,82 @@ -use iced_x86::code_asm::{eax, ptr, r12, r13, r8, r9, r9b, r9d, r9w, rax, rcx}; - use crate::{ - runtime::{FnDef, Runtime}, + runtime::Runtime, vm::{ - bytecode::Flag, - utils::{self, scratch}, + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // push r13 - rt.asm.push(r13).unwrap(); - // mov r13, rcx - rt.asm.mov(r13, rcx).unwrap(); - - // eax -> width - utils::bytecode::read_byte_zx(rt, r13, eax); - - // load r8 - scratch::load(rt, r12, r8); - // load r9 - scratch::load(rt, r12, r9); - - utils::width::dispatch( + semantic::compiler::compile( rt, - rax, - &mut epilogue, - Some(Box::new(|rt| { - // lock xadd [r8], r9 - rt.asm.lock().xadd(ptr(r8), r9).unwrap(); - })), - Some(Box::new(|rt| { - // lock xadd [r8], r9d - rt.asm.lock().xadd(ptr(r8), r9d).unwrap(); - })), - None, - Some(Box::new(|rt| { - // lock xadd [r8], r9w - rt.asm.lock().xadd(ptr(r8), r9w).unwrap(); - })), - None, - Some(Box::new(|rt| { - // lock xadd [r8], r9b - rt.asm.lock().xadd(ptr(r8), r9b).unwrap(); - })), - None, - None, - None, - None, - None, - None, + &Operation { + effects: vec![Effect::ExchangeAdd( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::LessThan( + Expression::Operand(Operand::Output(0)), + Expression::Operand(Operand::Input(0)), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Output(1))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(1))), + )), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: Some(vec![Expression::Operand(Operand::Output(1))]), + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, ); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // mov rcx, ... - rt.asm - .mov( - rcx, - Flag::Carry.bit64() - | Flag::Parity.bit64() - | Flag::Auxiliary.bit64() - | Flag::Zero.bit64() - | Flag::Sign.bit64() - | Flag::Overflow.bit64(), - ) - .unwrap(); - // pushfq - rt.asm.pushfq().unwrap(); - // call ... - rt.asm.call(rt.function_labels[&FnDef::VmFlags]).unwrap(); - - // store r9 - scratch::store(rt, r12, r9); - - // mov rax, r13 - rt.asm.mov(rax, r13).unwrap(); - // pop r13 - rt.asm.pop(r13).unwrap(); - // ret - rt.asm.ret().unwrap(); - } } diff --git a/runtime/src/vm/handlers/flags.rs b/runtime/src/vm/handlers/flags.rs deleted file mode 100644 index a71eca0d..00000000 --- a/runtime/src/vm/handlers/flags.rs +++ /dev/null @@ -1,36 +0,0 @@ -use iced_x86::code_asm::{eax, r12, r8, rax, rcx, rdx}; - -use crate::{ - runtime::Runtime, - vm::{bytecode::VMReg, utils}, -}; - -pub fn build(rt: &mut Runtime) { - // pop rax - rt.asm.pop(rax).unwrap(); - // pop rdx - rt.asm.pop(rdx).unwrap(); - // push rax - rt.asm.push(rax).unwrap(); - - // mov eax, [r12 + ...] - utils::vreg::load_reg32(rt, r12, VMReg::Flags, eax); - - // mov r8, rcx - rt.asm.mov(r8, rcx).unwrap(); - // not r8 - rt.asm.not(r8).unwrap(); - // and rax, r8 - rt.asm.and(rax, r8).unwrap(); - - // and rdx, rcx - rt.asm.and(rdx, rcx).unwrap(); - // or rax, rdx - rt.asm.or(rax, rdx).unwrap(); - - // mov [r12 + ...], eax - utils::vreg::store_reg32(rt, r12, eax, VMReg::Flags); - - // ret - rt.asm.ret().unwrap(); -} diff --git a/runtime/src/vm/handlers/mod.rs b/runtime/src/vm/handlers/mod.rs index 7f365812..77b3c5a1 100644 --- a/runtime/src/vm/handlers/mod.rs +++ b/runtime/src/vm/handlers/mod.rs @@ -1,178 +1,4 @@ -#[macro_export] -macro_rules! __arithmetic { - ($rt:expr, $operation:ident, r8, $epilogue:expr) => { - $crate::vm::utils::width::dispatch( - $rt, - rax, - $epilogue, - Some(Box::new(|rt| { - rt.asm.$operation(r14, r8).unwrap(); - })), - Some(Box::new(|rt| { - rt.asm.$operation(r14d, r8d).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.$operation(r14w, r8w).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.$operation(r14b, r8b).unwrap(); - })), - None, - None, - None, - None, - None, - None, - ); - }; - ($rt:expr, $operation:ident, shift, $epilogue:expr) => { - $rt.asm.mov(cl, r8b).unwrap(); - $crate::vm::utils::width::dispatch( - $rt, - rax, - $epilogue, - Some(Box::new(|rt| { - rt.asm.$operation(r14, cl).unwrap(); - })), - Some(Box::new(|rt| { - rt.asm.$operation(r14d, cl).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.$operation(r14w, cl).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.$operation(r14b, cl).unwrap(); - })), - None, - None, - None, - None, - None, - None, - ); - }; - ($rt:expr, $operation:ident, bitscan, $epilogue:expr) => { - $crate::vm::utils::width::dispatch( - $rt, - rax, - $epilogue, - Some(Box::new(|rt| { - rt.asm.$operation(r14, r8).unwrap(); - })), - Some(Box::new(|rt| { - rt.asm.$operation(r14d, r8d).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.$operation(r14w, r8w).unwrap(); - })), - None, - None, - None, - None, - None, - None, - None, - None, - ); - }; - ($rt:expr, $operation:ident, carry, $epilogue:expr) => { - // r9d -> flags - $crate::vm::utils::vreg::load_reg32($rt, r12, $crate::vm::bytecode::VMReg::Flags, r9d); - $crate::vm::utils::width::dispatch( - $rt, - rax, - $epilogue, - Some(Box::new(|rt| { - rt.asm.bt(r9d, 0i32).unwrap(); - rt.asm.$operation(r14, r8).unwrap(); - })), - Some(Box::new(|rt| { - rt.asm.bt(r9d, 0i32).unwrap(); - rt.asm.$operation(r14d, r8d).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.bt(r9d, 0i32).unwrap(); - rt.asm.$operation(r14w, r8w).unwrap(); - })), - None, - Some(Box::new(|rt| { - rt.asm.bt(r9d, 0i32).unwrap(); - rt.asm.$operation(r14b, r8b).unwrap(); - })), - None, - None, - None, - None, - None, - None, - ); - }; -} - -#[macro_export] -macro_rules! arithmetic { - ($operation:ident, $register:ident, $mask:expr) => { - use crate::{ - runtime::{FnDef, Runtime}, - vm::utils::{self, scratch}, - }; - use iced_x86::code_asm::*; - - // unsigned char* (unsigned char*) - pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // push r13 - rt.asm.push(r13).unwrap(); - // push r14 - rt.asm.push(r14).unwrap(); - - // mov r13, rcx - rt.asm.mov(r13, rcx).unwrap(); - - // eax -> width - utils::bytecode::read_byte_zx(rt, r13, eax); - - // load r8 - scratch::load(rt, r12, r8); - // load r14 - scratch::load(rt, r12, r14); - - $crate::__arithmetic!(rt, $operation, $register, &mut epilogue); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // mov rcx, ... - rt.asm.mov(rcx, $mask).unwrap(); - // pushfq - rt.asm.pushfq().unwrap(); - // call ... - rt.asm.call(rt.function_labels[&FnDef::VmFlags]).unwrap(); - - // store r14 - scratch::store(rt, r12, r14); - - // mov rax, r13 - rt.asm.mov(rax, r13).unwrap(); - // pop r14 - rt.asm.pop(r14).unwrap(); - // pop r13 - rt.asm.pop(r13).unwrap(); - // ret - rt.asm.ret().unwrap(); - } - } - }; -} - -pub(crate) use arithmetic; - +pub mod adc; pub mod add; pub mod and; pub mod bit_scan_reverse; @@ -183,10 +9,10 @@ pub mod bit_test_set; pub mod byte_swap; pub mod compare_exchange; pub mod discard; +pub mod dispatch; pub mod div; pub mod exchange; pub mod exchange_add; -pub mod flags; pub mod jcc; pub mod load_address; pub mod load_immediate; @@ -203,6 +29,8 @@ pub mod ret; pub mod rol; pub mod ror; pub mod sar; +pub mod sbb; +pub mod semantic; pub mod shl; pub mod shr; pub mod store_extend; diff --git a/runtime/src/vm/handlers/mul.rs b/runtime/src/vm/handlers/mul.rs index c015af9b..4afcd7d0 100644 --- a/runtime/src/vm/handlers/mul.rs +++ b/runtime/src/vm/handlers/mul.rs @@ -1,145 +1,46 @@ -use iced_x86::code_asm::*; - use crate::{ - runtime::{FnDef, Runtime}, + runtime::Runtime, vm::{ - bytecode::Flag, - utils::{self, scratch}, + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, }, }; -// unsigned char* (unsigned char*) pub fn build(rt: &mut Runtime) { - let mut epilogue = rt.asm.create_label(); - - // push r13 - rt.asm.push(r13).unwrap(); - - // mov r13, rcx - rt.asm.mov(r13, rcx).unwrap(); - - // rax -> width - utils::bytecode::read_byte_zx(rt, r13, eax); - - // load r8 - scratch::load(rt, r12, r8); - // load r9 - scratch::load(rt, r12, r9); - - utils::width::dispatch( + semantic::compiler::compile( rt, - rax, - &mut epilogue, - Some(Box::new(|rt| wide(rt, false))), - Some(Box::new(|rt| dword(rt, false))), - None, - Some(Box::new(|rt| word(rt, false))), - None, - Some(Box::new(|rt| byte(rt, false))), - Some(Box::new(|rt| wide(rt, true))), - Some(Box::new(|rt| dword(rt, true))), - Some(Box::new(|rt| word(rt, true))), - Some(Box::new(|rt| byte(rt, true))), - None, - None, + &Operation { + effects: vec![Effect::Mul( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::GreaterThan( + Expression::Operand(Operand::Output(1)), + Expression::Constant(0), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::GreaterThan( + Expression::Operand(Operand::Output(1)), + Expression::Constant(0), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + VMWidth::SLower64, + VMWidth::SLower32, + VMWidth::SLower16, + VMWidth::SLower8, + ], + }, ); - - rt.asm.set_label(&mut epilogue).unwrap(); - { - // pushfq - rt.asm.pushfq().unwrap(); - - // store r8 - scratch::store(rt, r12, r8); - // store r9 - scratch::store(rt, r12, r9); - - // mov rcx, ... - rt.asm - .mov( - rcx, - Flag::Carry.bit64() - | Flag::Parity.bit64() - | Flag::Auxiliary.bit64() - | Flag::Zero.bit64() - | Flag::Sign.bit64() - | Flag::Overflow.bit64(), - ) - .unwrap(); - // call ... - rt.asm.call(rt.function_labels[&FnDef::VmFlags]).unwrap(); - - // mov rax, r13 - rt.asm.mov(rax, r13).unwrap(); - // pop r13 - rt.asm.pop(r13).unwrap(); - // ret - rt.asm.ret().unwrap(); - } -} - -fn wide(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - if signed { - // rdx:rax = rax * r8 - rt.asm.imul(r8).unwrap(); - } else { - // rdx:rax = rax * r8 - rt.asm.mul(r8).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn dword(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - if signed { - // edx:eax = eax * r8d - rt.asm.imul(r8d).unwrap(); - } else { - // edx:eax = eax * r8d - rt.asm.mul(r8d).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn word(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - if signed { - // dx:ax = ax * r8w - rt.asm.imul(r8w).unwrap(); - } else { - // dx:ax = ax * r8w - rt.asm.mul(r8w).unwrap(); - } - // mov r9, rax - rt.asm.mov(r9, rax).unwrap(); - // mov r8, rdx - rt.asm.mov(r8, rdx).unwrap(); -} - -fn byte(rt: &mut Runtime, signed: bool) { - // mov rax, r9 - rt.asm.mov(rax, r9).unwrap(); - if signed { - // ax = al * r8b - rt.asm.imul(r8b).unwrap(); - } else { - // ax = al * r8b - rt.asm.mul(r8b).unwrap(); - } - // movzx ecx, ah - rt.asm.movzx(ecx, ah).unwrap(); - // movzx r9d, al - rt.asm.movzx(r9d, al).unwrap(); - // mov r8, rcx - rt.asm.mov(r8, rcx).unwrap(); } diff --git a/runtime/src/vm/handlers/or.rs b/runtime/src/vm/handlers/or.rs index 14fcfd2d..19d34320 100644 --- a/runtime/src/vm/handlers/or.rs +++ b/runtime/src/vm/handlers/or.rs @@ -1,10 +1,60 @@ -crate::vm::handlers::arithmetic!( - or, - r8, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Or( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/pop.rs b/runtime/src/vm/handlers/pop.rs index bcbc6a88..75fbeb3e 100644 --- a/runtime/src/vm/handlers/pop.rs +++ b/runtime/src/vm/handlers/pop.rs @@ -1,4 +1,4 @@ -use iced_x86::code_asm::{r8, r9, rax, r12, rcx}; +use iced_x86::code_asm::{r12, r8, r9, rax, rcx}; use crate::{ runtime::Runtime, @@ -7,7 +7,7 @@ use crate::{ }; // unsigned char* (unsigned char*) -pub fn build(rt: &mut Runtime) { +pub fn build(rt: &mut Runtime) { // mov r8, [r12 + ...]; mov r9, [r8] utils::vreg::load_mem(rt, r12, VMReg::Rsp, r8, r9); // add [r12 + ...], 0x8 diff --git a/runtime/src/vm/handlers/rol.rs b/runtime/src/vm/handlers/rol.rs index 3b940f27..123bdad2 100644 --- a/runtime/src/vm/handlers/rol.rs +++ b/runtime/src/vm/handlers/rol.rs @@ -1,5 +1,60 @@ -crate::vm::handlers::arithmetic!( - rol, - shift, - crate::vm::bytecode::Flag::Carry.bit64() | crate::vm::bytecode::Flag::Overflow.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Rol( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::When( + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + ), + vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::GreaterThan( + Expression::BitXor( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Output(0))), + Box::new(Expression::SignBit), + )), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Output(0))), + Box::new(Expression::Constant(1)), + )), + ), + Expression::Constant(0), + ))), + ), + ], + ), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/ror.rs b/runtime/src/vm/handlers/ror.rs index 4ae03b21..7e3ed88d 100644 --- a/runtime/src/vm/handlers/ror.rs +++ b/runtime/src/vm/handlers/ror.rs @@ -1,5 +1,66 @@ -crate::vm::handlers::arithmetic!( - ror, - shift, - crate::vm::bytecode::Flag::Carry.bit64() | crate::vm::bytecode::Flag::Overflow.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Ror( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::When( + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + ), + vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::GreaterThan( + Expression::BitXor( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Output(0))), + Box::new(Expression::SignBit), + )), + Box::new(Expression::BitAnd( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Output(0))), + Box::new(Expression::Sub( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(1)), + )), + )), + Box::new(Expression::Constant(1)), + )), + ), + Expression::Constant(0), + ))), + ), + ], + ), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/sar.rs b/runtime/src/vm/handlers/sar.rs index 5473be73..aba44ef4 100644 --- a/runtime/src/vm/handlers/sar.rs +++ b/runtime/src/vm/handlers/sar.rs @@ -1,10 +1,98 @@ -crate::vm::handlers::arithmetic!( - sar, - shift, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Sar( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::When( + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + ), + vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::Sub( + Box::new(Expression::SignBit), + Box::new(Expression::BitAnd( + Box::new(Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + )), + )), + Box::new(Expression::BitShr( + Box::new(Expression::BitNot(Box::new( + Expression::Constant(0), + ))), + Box::new(Expression::Sub( + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + )), + Box::new(Expression::Constant(1)), + )), + )), + )), + ), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ], + ), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/sbb.rs b/runtime/src/vm/handlers/sbb.rs new file mode 100644 index 00000000..21c61e27 --- /dev/null +++ b/runtime/src/vm/handlers/sbb.rs @@ -0,0 +1,102 @@ +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Sub( + Expression::Operand(Operand::Input(0)), + Expression::Sub( + Box::new(Expression::Sub( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitNot(Box::new(Expression::Flag(Flag::Carry)))), + )), + Box::new(Expression::Constant(1)), + ), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitOr( + Box::new(Expression::BitAnd( + Box::new(Expression::BitNot(Box::new(Expression::Operand( + Operand::Input(0), + )))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::BitAnd( + Box::new(Expression::BitNot(Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/semantic/allocator.rs b/runtime/src/vm/handlers/semantic/allocator.rs new file mode 100644 index 00000000..de308768 --- /dev/null +++ b/runtime/src/vm/handlers/semantic/allocator.rs @@ -0,0 +1,224 @@ +use iced_x86::code_asm::{qword_ptr, r8, r9, rax, rbp, rcx, rdx, AsmRegister64}; + +use std::collections::HashSet; + +use crate::{ + runtime::Runtime, + vm::handlers::semantic::{Reference, Value}, +}; + +#[derive(Clone)] +pub struct Allocator { + tracked: Vec<(AsmRegister64, Value)>, + dirty: HashSet, + temporary: usize, + inputs: usize, + outputs: usize, + flags: bool, +} + +impl Allocator { + const REGISTERS: [AsmRegister64; 5] = [rax, rcx, rdx, r8, r9]; + + pub fn new(inputs: usize, outputs: usize, flags: bool) -> Self { + Self { + tracked: Vec::new(), + dirty: HashSet::new(), + temporary: 0, + inputs, + outputs, + flags, + } + } + + fn track(&mut self, register: AsmRegister64, value: Value, dirty: bool) { + assert!( + self.tracked.iter().all(|(r, _)| *r != register), + "register {:?} is already tracked", + register + ); + assert!( + self.tracked.iter().all(|(_, v)| *v != value), + "value {:?} is already tracked", + value + ); + self.tracked.push((register, value)); + + if dirty { + self.dirty.insert(value); + } else { + self.dirty.remove(&value); + } + } + + pub fn untrack(&mut self, register: AsmRegister64) -> Option { + let index = self.tracked.iter().position(|(r, _)| *r == register)?; + Some(self.tracked.swap_remove(index).1) + } + + fn tracked_register(&self, value: Value) -> Option { + self.tracked + .iter() + .find(|(_, v)| *v == value) + .map(|(r, _)| *r) + } + + fn tracked_value(&self, register: AsmRegister64) -> Option { + self.tracked + .iter() + .find(|(r, _)| *r == register) + .map(|(_, v)| *v) + } + + pub fn temporary(&mut self) -> Value { + let value = Value::Temporary(self.temporary); + self.temporary += 1; + value + } + + pub fn offset(&self, value: Value) -> i32 { + let flags = self.flags as usize; + let slot = match value { + Value::Input(index) => index, + Value::Output(index) => self.inputs + index, + Value::Flags => self.inputs + self.outputs, + Value::Temporary(index) => self.inputs + self.outputs + flags + index, + }; + (slot as i32 + 1) * 8 + } + + pub fn dirty(&mut self, value: Value) { + self.dirty.insert(value); + } + + pub fn spill(&mut self, rt: &mut Runtime, register: AsmRegister64) { + let Some(value) = self.tracked_value(register) else { + return; + }; + + if self.dirty.remove(&value) { + rt.asm + .mov(qword_ptr(rbp - self.offset(value)), register) + .unwrap(); + } + + self.untrack(register); + } + + pub fn dump(&mut self, rt: &mut Runtime) { + let registers = self + .tracked + .iter() + .map(|(register, _)| *register) + .collect::>(); + + for register in registers { + self.spill(rt, register); + } + } + + pub fn acquire( + &mut self, + rt: &mut Runtime, + pinned: &[Value], + avoid: &[AsmRegister64], + ) -> AsmRegister64 { + if let Some(register) = Self::REGISTERS + .iter() + .copied() + .find(|register| !avoid.contains(register) && self.tracked_value(*register).is_none()) + { + return register; + } + + if let Some(register) = Self::REGISTERS.iter().copied().find(|register| { + if avoid.contains(register) { + return false; + } + + match self.tracked_value(*register) { + Some(value) => !pinned.contains(&value), + None => true, + } + }) { + self.spill(rt, register); + return register; + } + + let register = Self::REGISTERS + .iter() + .copied() + .find(|register| !avoid.contains(register)) + .unwrap(); + + self.spill(rt, register); + register + } + + pub fn copy(&mut self, rt: &mut Runtime, src: Reference, dst: AsmRegister64) { + self.spill(rt, dst); + + match src { + Reference::Immediate(value) => { + rt.asm.mov(dst, value).unwrap(); + } + Reference::Value(value) => { + if let Some(src) = self.tracked_register(value) { + rt.asm.mov(dst, src).unwrap(); + } else { + rt.asm + .mov(dst, qword_ptr(rbp - self.offset(value))) + .unwrap(); + } + } + } + } + + pub fn mutable( + &mut self, + rt: &mut Runtime, + value: Value, + avoid: &[AsmRegister64], + ) -> AsmRegister64 { + if let Some(register) = self.tracked_register(value) { + if !avoid.contains(®ister) { + return register; + } + } + + let register = self.acquire(rt, &[], avoid); + rt.asm + .mov(register, qword_ptr(rbp - self.offset(value))) + .unwrap(); + self.track(register, value, false); + register + } + + pub fn replace(&mut self, register: AsmRegister64, value: Value, dirty: bool) { + self.untrack(register); + + if let Some(previous) = self + .tracked + .iter() + .find(|(_, v)| *v == value) + .map(|(r, _)| *r) + { + self.untrack(previous); + } + + self.track(register, value, dirty); + } + + pub fn consume(&mut self, value: Reference) { + let Reference::Value(value) = value else { + return; + }; + + if matches!(value, Value::Temporary(_)) { + if let Some(register) = self.tracked_register(value) { + self.untrack(register); + } + self.dirty.remove(&value); + } + } +} diff --git a/runtime/src/vm/handlers/semantic/compiler.rs b/runtime/src/vm/handlers/semantic/compiler.rs new file mode 100644 index 00000000..d5664df0 --- /dev/null +++ b/runtime/src/vm/handlers/semantic/compiler.rs @@ -0,0 +1,1197 @@ +use iced_x86::code_asm::{ + ah, ax, cl, dl, dword_ptr, dx, get_gpr16, get_gpr32, get_gpr8, ptr, qword_ptr, r12, r13, r14, + r14d, rax, rbp, rcx, rdx, rsp, AsmRegister16, AsmRegister32, AsmRegister64, AsmRegister8, + CodeAssembler, CodeLabel, +}; + +use crate::{ + runtime::Runtime, + utils::register_of_size, + vm::{ + bytecode::{VMReg, VMWidth}, + handlers::semantic::{ + allocator::Allocator, Compare, Effect, Expression, Flags, Operand, Operation, + Reference, Value, + }, + utils::{bytecode, scratch, vreg}, + }, +}; + +const BUDGET: u32 = 2; + +pub fn compile(rt: &mut Runtime, operation: &Operation) { + let mut rng = rand::thread_rng(); + + let mut budget = BUDGET; + + let operation = Operation { + effects: operation + .effects + .iter() + .cloned() + .map(|effect| effect.obfuscate(&mut rng, &mut budget)) + .collect(), + flags: operation.flags.clone(), + stores: operation.stores.clone(), + widths: operation.widths, + }; + let operation = &operation; + + let mut epilogue = rt.asm.create_label(); + let operands = operation.operands(); + let outputs = operation.outputs(); + let flags = !operation.flags.is_empty(); + let temporaries = operation.temporaries(); + let slots = operands + outputs + flags as usize + temporaries; + let size = ((slots as i32 * 8) + 15) & !15; + let allocator = Allocator::new(operands, outputs, flags); + + rt.asm.push(r13).unwrap(); + rt.asm.push(r14).unwrap(); + rt.asm.push(rbp).unwrap(); + rt.asm.mov(rbp, rsp).unwrap(); + rt.asm.sub(rsp, size).unwrap(); + + rt.asm.mov(r13, rcx).unwrap(); + + bytecode::read_byte_zx(rt, r13, r14d); + + for index in (0..operands).rev() { + scratch::load(rt, r12, rax); + + rt.asm + .mov(qword_ptr(rbp - allocator.offset(Value::Input(index))), rax) + .unwrap(); + } + + let handlers = operation + .widths + .iter() + .map(|&width| { + let actions = operation.effects.clone(); + let rules = operation.flags.clone(); + + ( + width, + Box::new(move |rt: &mut Runtime, allocator: &mut Allocator| { + compile_effects(rt, allocator, &actions, width); + + for index in 0..outputs { + let output = Value::Output(index); + let register = allocator.mutable(rt, output, &[]); + let mask = allocator.acquire(rt, &[output], &[register]); + + rt.asm.mov(mask, width.mask() as i64).unwrap(); + rt.asm.and(register, mask).unwrap(); + + allocator.spill(rt, mask); + allocator.dirty(output); + } + + if !rules.is_empty() { + compile_flags(rt, allocator, &rules, width); + } + }) as Box, + ) + }) + .collect::)>>(); + + let cases = handlers + .iter() + .map( + |(width, _function): &(VMWidth, Box)| { + (rt.mapper.index(*width) as u8, rt.asm.create_label()) + }, + ) + .collect::>(); + + rt.jumps( + r14, + cases + .iter() + .map(|(key, label): &(u8, CodeLabel)| (*key, *label)) + .collect::>(), + ); + + for ((_key, mut label), (_width, handler)) in cases.into_iter().zip(handlers.into_iter()) { + rt.asm.set_label(&mut label).unwrap(); + + let mut local = Allocator::new(operands, outputs, flags); + + handler(rt, &mut local); + + local.dump(rt); + + rt.asm.jmp(epilogue).unwrap(); + } + + rt.asm.set_label(&mut epilogue).unwrap(); + + match &operation.stores { + Some(stores) => { + let mut allocator = Allocator::new(operands, outputs, flags); + + for expression in stores { + let src = compile_expression(rt, &mut allocator, expression, VMWidth::Lower64); + + let dst = allocator.acquire(rt, &[], &[]); + + allocator.copy(rt, src, dst); + + scratch::store(rt, r12, dst); + + allocator.spill(rt, dst); + allocator.consume(src); + } + } + None => { + let mut allocator = Allocator::new(operands, outputs, flags); + + for index in (0..outputs).rev() { + let dst = allocator.acquire(rt, &[], &[]); + allocator.copy(rt, Reference::Value(Value::Output(index)), dst); + scratch::store(rt, r12, dst); + allocator.spill(rt, dst); + } + } + } + + rt.asm.mov(rax, r13).unwrap(); + + rt.asm.add(rsp, size).unwrap(); + rt.asm.pop(rbp).unwrap(); + rt.asm.pop(r14).unwrap(); + rt.asm.pop(r13).unwrap(); + rt.asm.ret().unwrap(); +} + +fn compile_effects( + rt: &mut Runtime, + allocator: &mut Allocator, + effects: &[Effect], + width: VMWidth, +) { + for effect in effects { + compile_effect(rt, allocator, effect, width); + } +} + +fn mask(rt: &mut Runtime, register: AsmRegister64, width: VMWidth) { + match width.size() { + 1 => { + let byte = get_gpr8(register_of_size(register.into(), 1).unwrap()).unwrap(); + rt.asm.movzx(register, byte).unwrap(); + } + 2 => { + let word = get_gpr16(register_of_size(register.into(), 2).unwrap()).unwrap(); + rt.asm.movzx(register, word).unwrap(); + } + 4 => { + let dword = get_gpr32(register_of_size(register.into(), 4).unwrap()).unwrap(); + rt.asm.mov(dword, dword).unwrap(); + } + 8 => {} + _ => unreachable!(), + } +} + +fn compile_binary( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + width: VMWidth, + operation: F, +) where + F: FnOnce(&mut Runtime, AsmRegister64, AsmRegister64), +{ + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, first, dst); + mask(rt, dst, width); + + let src = allocator.acquire(rt, &pinned, &[dst]); + allocator.copy(rt, second, src); + mask(rt, src, width); + + operation(rt, dst, src); + + allocator.replace(dst, Value::Output(0), true); + allocator.consume(first); + allocator.consume(second); +} + +fn compile_mul( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + width: VMWidth, +) { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let src = allocator.acquire(rt, &pinned, &[rax]); + allocator.copy(rt, second, src); + allocator.copy(rt, first, rax); + + compile_operation( + rt, + width, + src, + |asm, byte| asm.mul(byte).unwrap(), + |asm, word| asm.mul(word).unwrap(), + |asm, dword| asm.mul(dword).unwrap(), + |asm, qword| asm.mul(qword).unwrap(), + |asm, byte| asm.imul(byte).unwrap(), + |asm, word| asm.imul(word).unwrap(), + |asm, dword| asm.imul(dword).unwrap(), + |asm, qword| asm.imul(qword).unwrap(), + ); + + if width.size() == 1 { + rt.asm.movzx(rdx, ax).unwrap(); + rt.asm.shr(rdx, 0x8).unwrap(); + } + + allocator.replace(rax, Value::Output(0), true); + allocator.replace(rdx, Value::Output(1), true); + + allocator.consume(first); + allocator.consume(second); +} + +fn compile_div( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + third: &Expression, + width: VMWidth, +) { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + let third = compile_expression(rt, allocator, third, width); + + let pinned = [first, second, third] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let src = allocator.acquire(rt, &pinned, &[rax, rdx]); + + allocator.copy(rt, first, src); + allocator.copy(rt, second, rax); + allocator.copy(rt, third, rdx); + + if width.size() == 1 { + rt.asm.mov(ah, dl).unwrap(); + } + + compile_operation( + rt, + width, + src, + |asm, byte| asm.div(byte).unwrap(), + |asm, word| asm.div(word).unwrap(), + |asm, dword| asm.div(dword).unwrap(), + |asm, qword| asm.div(qword).unwrap(), + |asm, byte| asm.idiv(byte).unwrap(), + |asm, word| asm.idiv(word).unwrap(), + |asm, dword| asm.idiv(dword).unwrap(), + |asm, qword| asm.idiv(qword).unwrap(), + ); + + if width.size() == 1 { + rt.asm.movzx(dx, ah).unwrap(); + } + + allocator.replace(rax, Value::Output(0), true); + allocator.replace(rdx, Value::Output(1), true); + + allocator.consume(first); + allocator.consume(second); + allocator.consume(third); +} + +fn compile_exchange( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + width: VMWidth, +) { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, VMWidth::Lower64); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, first, dst); + + let src = allocator.acquire(rt, &pinned, &[dst]); + allocator.copy(rt, second, src); + + match width.size() { + 1 => { + let byte = get_gpr8(register_of_size(dst.into(), 1).unwrap()).unwrap(); + rt.asm.xchg(ptr(src), byte).unwrap(); + } + 2 => { + let word = get_gpr16(register_of_size(dst.into(), 2).unwrap()).unwrap(); + rt.asm.xchg(ptr(src), word).unwrap(); + } + 4 => { + let dword = get_gpr32(register_of_size(dst.into(), 4).unwrap()).unwrap(); + rt.asm.xchg(ptr(src), dword).unwrap(); + } + 8 => { + rt.asm.xchg(ptr(src), dst).unwrap(); + } + _ => unreachable!(), + } + + allocator.replace(dst, Value::Output(0), true); + + allocator.consume(first); + allocator.consume(second); +} + +fn compile_exchange_add( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + width: VMWidth, +) { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, VMWidth::Lower64); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, first, dst); + + let src = allocator.acquire(rt, &pinned, &[dst]); + allocator.copy(rt, second, src); + + match width.size() { + 1 => { + let byte = get_gpr8(register_of_size(dst.into(), 1).unwrap()).unwrap(); + rt.asm.lock().xadd(ptr(src), byte).unwrap(); + } + 2 => { + let word = get_gpr16(register_of_size(dst.into(), 2).unwrap()).unwrap(); + rt.asm.lock().xadd(ptr(src), word).unwrap(); + } + 4 => { + let dword = get_gpr32(register_of_size(dst.into(), 4).unwrap()).unwrap(); + rt.asm.lock().xadd(ptr(src), dword).unwrap(); + } + 8 => { + rt.asm.lock().xadd(ptr(src), dst).unwrap(); + } + _ => unreachable!(), + } + + allocator.copy(rt, first, src); + + rt.asm.add(src, dst).unwrap(); + + allocator.replace(dst, Value::Output(1), true); + allocator.replace(src, Value::Output(0), true); + + allocator.consume(first); + allocator.consume(second); +} + +fn compile_compare_exchange( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + third: &Expression, + width: VMWidth, +) { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + let third = compile_expression(rt, allocator, third, VMWidth::Lower64); + + let pinned = [first, second, third] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let src = allocator.acquire(rt, &pinned, &[rax]); + allocator.copy(rt, second, src); + + let dst = allocator.acquire(rt, &pinned, &[rax, src]); + allocator.copy(rt, third, dst); + + allocator.copy(rt, first, rax); + + match width.size() { + 1 => { + let byte = get_gpr8(register_of_size(src.into(), 1).unwrap()).unwrap(); + rt.asm.lock().cmpxchg(ptr(dst), byte).unwrap(); + } + 2 => { + let word = get_gpr16(register_of_size(src.into(), 2).unwrap()).unwrap(); + rt.asm.lock().cmpxchg(ptr(dst), word).unwrap(); + } + 4 => { + let dword = get_gpr32(register_of_size(src.into(), 4).unwrap()).unwrap(); + rt.asm.lock().cmpxchg(ptr(dst), dword).unwrap(); + } + 8 => { + rt.asm.lock().cmpxchg(ptr(dst), src).unwrap(); + } + _ => unreachable!(), + } + + allocator.replace(rax, Value::Output(0), true); + + allocator.consume(first); + allocator.consume(second); + allocator.consume(third); +} + +fn compile_operation( + rt: &mut Runtime, + width: VMWidth, + src: AsmRegister64, + unsigned8: U8, + unsigned16: U16, + unsigned32: U32, + unsigned64: U64, + signed8: S8, + signed16: S16, + signed32: S32, + signed64: S64, +) where + U8: FnOnce(&mut CodeAssembler, AsmRegister8), + U16: FnOnce(&mut CodeAssembler, AsmRegister16), + U32: FnOnce(&mut CodeAssembler, AsmRegister32), + U64: FnOnce(&mut CodeAssembler, AsmRegister64), + S8: FnOnce(&mut CodeAssembler, AsmRegister8), + S16: FnOnce(&mut CodeAssembler, AsmRegister16), + S32: FnOnce(&mut CodeAssembler, AsmRegister32), + S64: FnOnce(&mut CodeAssembler, AsmRegister64), +{ + let signed = width == width.signed(); + + match (width.size(), signed) { + (1, false) => { + let byte = get_gpr8(register_of_size(src.into(), 1).unwrap()).unwrap(); + unsigned8(&mut rt.asm, byte); + } + (2, false) => { + let word = get_gpr16(register_of_size(src.into(), 2).unwrap()).unwrap(); + unsigned16(&mut rt.asm, word); + } + (4, false) => { + let dword = get_gpr32(register_of_size(src.into(), 4).unwrap()).unwrap(); + unsigned32(&mut rt.asm, dword); + } + (8, false) => { + unsigned64(&mut rt.asm, src); + } + (1, true) => { + let byte = get_gpr8(register_of_size(src.into(), 1).unwrap()).unwrap(); + signed8(&mut rt.asm, byte); + } + (2, true) => { + let word = get_gpr16(register_of_size(src.into(), 2).unwrap()).unwrap(); + signed16(&mut rt.asm, word); + } + (4, true) => { + let dword = get_gpr32(register_of_size(src.into(), 4).unwrap()).unwrap(); + signed32(&mut rt.asm, dword); + } + (8, true) => { + signed64(&mut rt.asm, src); + } + _ => unreachable!(), + } +} + +fn compile_shift( + rt: &mut Runtime, + allocator: &mut Allocator, + first: &Expression, + second: &Expression, + width: VMWidth, + unsigned8: U8, + unsigned16: U16, + unsigned32: U32, + unsigned64: U64, + signed8: S8, + signed16: S16, + signed32: S32, + signed64: S64, +) where + U8: FnOnce(&mut CodeAssembler, AsmRegister8), + U16: FnOnce(&mut CodeAssembler, AsmRegister16), + U32: FnOnce(&mut CodeAssembler, AsmRegister32), + U64: FnOnce(&mut CodeAssembler, AsmRegister64), + S8: FnOnce(&mut CodeAssembler, AsmRegister8), + S16: FnOnce(&mut CodeAssembler, AsmRegister16), + S32: FnOnce(&mut CodeAssembler, AsmRegister32), + S64: FnOnce(&mut CodeAssembler, AsmRegister64), +{ + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + allocator.copy(rt, second, rcx); + + let dst = allocator.acquire(rt, &pinned, &[rcx]); + allocator.copy(rt, first, dst); + + compile_operation( + rt, width, dst, unsigned8, unsigned16, unsigned32, unsigned64, signed8, signed16, signed32, + signed64, + ); + + allocator.replace(dst, Value::Output(0), true); + allocator.consume(first); + allocator.consume(second); +} + +fn compile_effect(rt: &mut Runtime, allocator: &mut Allocator, effect: &Effect, width: VMWidth) { + match effect { + Effect::Add(first, second) => { + compile_binary(rt, allocator, first, second, width, |rt, first, second| { + rt.asm.add(first, second).unwrap(); + }) + } + Effect::Sub(first, second) => { + compile_binary(rt, allocator, first, second, width, |rt, first, second| { + rt.asm.sub(first, second).unwrap(); + }) + } + Effect::And(first, second) => { + compile_binary(rt, allocator, first, second, width, |rt, first, second| { + rt.asm.and(first, second).unwrap(); + }) + } + Effect::Or(first, second) => { + compile_binary(rt, allocator, first, second, width, |rt, first, second| { + rt.asm.or(first, second).unwrap(); + }) + } + Effect::Xor(first, second) => { + compile_binary(rt, allocator, first, second, width, |rt, first, second| { + rt.asm.xor(first, second).unwrap(); + }) + } + Effect::Mul(first, second) => compile_mul(rt, allocator, first, second, width), + Effect::Div(first, second, third) => { + compile_div(rt, allocator, first, second, third, width) + } + Effect::Shr(first, second) => compile_shift( + rt, + allocator, + first, + second, + width, + |asm, byte| { + asm.shr(byte, cl).unwrap(); + }, + |asm, word| { + asm.shr(word, cl).unwrap(); + }, + |asm, dword| { + asm.shr(dword, cl).unwrap(); + }, + |asm, qword| { + asm.shr(qword, cl).unwrap(); + }, + |asm, byte| { + asm.shr(byte, cl).unwrap(); + }, + |asm, word| { + asm.shr(word, cl).unwrap(); + }, + |asm, dword| { + asm.shr(dword, cl).unwrap(); + }, + |asm, qword| { + asm.shr(qword, cl).unwrap(); + }, + ), + Effect::Shl(first, second) => compile_shift( + rt, + allocator, + first, + second, + width, + |asm, byte| { + asm.shl(byte, cl).unwrap(); + }, + |asm, word| { + asm.shl(word, cl).unwrap(); + }, + |asm, dword| { + asm.shl(dword, cl).unwrap(); + }, + |asm, qword| { + asm.shl(qword, cl).unwrap(); + }, + |asm, byte| { + asm.shl(byte, cl).unwrap(); + }, + |asm, word| { + asm.shl(word, cl).unwrap(); + }, + |asm, dword| { + asm.shl(dword, cl).unwrap(); + }, + |asm, qword| { + asm.shl(qword, cl).unwrap(); + }, + ), + Effect::Ror(first, second) => compile_shift( + rt, + allocator, + first, + second, + width, + |asm, byte| { + asm.ror(byte, cl).unwrap(); + }, + |asm, word| { + asm.ror(word, cl).unwrap(); + }, + |asm, dword| { + asm.ror(dword, cl).unwrap(); + }, + |asm, qword| { + asm.ror(qword, cl).unwrap(); + }, + |asm, byte| { + asm.ror(byte, cl).unwrap(); + }, + |asm, word| { + asm.ror(word, cl).unwrap(); + }, + |asm, dword| { + asm.ror(dword, cl).unwrap(); + }, + |asm, qword| { + asm.ror(qword, cl).unwrap(); + }, + ), + Effect::Rol(first, second) => compile_shift( + rt, + allocator, + first, + second, + width, + |asm, byte| { + asm.rol(byte, cl).unwrap(); + }, + |asm, word| { + asm.rol(word, cl).unwrap(); + }, + |asm, dword| { + asm.rol(dword, cl).unwrap(); + }, + |asm, qword| { + asm.rol(qword, cl).unwrap(); + }, + |asm, byte| { + asm.rol(byte, cl).unwrap(); + }, + |asm, word| { + asm.rol(word, cl).unwrap(); + }, + |asm, dword| { + asm.rol(dword, cl).unwrap(); + }, + |asm, qword| { + asm.rol(qword, cl).unwrap(); + }, + ), + Effect::Sar(first, second) => compile_shift( + rt, + allocator, + first, + second, + width, + |asm, byte| { + asm.sar(byte, cl).unwrap(); + }, + |asm, word| { + asm.sar(word, cl).unwrap(); + }, + |asm, dword| { + asm.sar(dword, cl).unwrap(); + }, + |asm, qword| { + asm.sar(qword, cl).unwrap(); + }, + |asm, byte| { + asm.sar(byte, cl).unwrap(); + }, + |asm, word| { + asm.sar(word, cl).unwrap(); + }, + |asm, dword| { + asm.sar(dword, cl).unwrap(); + }, + |asm, qword| { + asm.sar(qword, cl).unwrap(); + }, + ), + Effect::Assign(expression) => { + let src = compile_expression(rt, allocator, expression, width); + let dst = allocator.acquire(rt, &[], &[]); + allocator.copy(rt, src, dst); + + allocator.replace(dst, Value::Output(0), true); + allocator.consume(src); + } + Effect::Bsr(expression) => compile_unary( + rt, + allocator, + expression, + width, + |rt, dst, src| rt.asm.bsr(dst, src).unwrap(), + |rt, dst, src| rt.asm.bsr(dst, src).unwrap(), + |rt, dst, src| rt.asm.bsr(dst, src).unwrap(), + ), + Effect::Tzcnt(expression) => compile_unary( + rt, + allocator, + expression, + width, + |rt, dst, src| rt.asm.tzcnt(dst, src).unwrap(), + |rt, dst, src| rt.asm.tzcnt(dst, src).unwrap(), + |rt, dst, src| rt.asm.tzcnt(dst, src).unwrap(), + ), + Effect::Exchange(first, second) => compile_exchange(rt, allocator, first, second, width), + Effect::ExchangeAdd(first, second) => { + compile_exchange_add(rt, allocator, first, second, width) + } + Effect::CompareExchange(first, second, third) => { + compile_compare_exchange(rt, allocator, first, second, third, width) + } + } +} + +fn compile_unary( + rt: &mut Runtime, + allocator: &mut Allocator, + expression: &Expression, + width: VMWidth, + unsigned16: U16, + unsigned32: U32, + unsigned64: U64, +) where + U16: FnOnce(&mut Runtime, AsmRegister16, AsmRegister16), + U32: FnOnce(&mut Runtime, AsmRegister32, AsmRegister32), + U64: FnOnce(&mut Runtime, AsmRegister64, AsmRegister64), +{ + let value = compile_expression(rt, allocator, expression, width); + let src = allocator.acquire(rt, &[], &[]); + + allocator.copy(rt, value, src); + + let mask = allocator.acquire(rt, &[], &[src]); + + rt.asm.mov(mask, width.mask() as i64).unwrap(); + rt.asm.and(src, mask).unwrap(); + + allocator.spill(rt, mask); + + let dst = allocator.mutable(rt, Value::Output(0), &[src]); + + match width.size() { + 2 => { + let dst = get_gpr16(register_of_size(dst.into(), 2).unwrap()).unwrap(); + let src = get_gpr16(register_of_size(src.into(), 2).unwrap()).unwrap(); + unsigned16(rt, dst, src); + } + 4 => { + let dst = get_gpr32(register_of_size(dst.into(), 4).unwrap()).unwrap(); + let src = get_gpr32(register_of_size(src.into(), 4).unwrap()).unwrap(); + unsigned32(rt, dst, src); + } + 8 => { + unsigned64(rt, dst, src); + } + _ => unreachable!(), + } + + allocator.dirty(Value::Output(0)); + allocator.consume(value); +} + +fn compile_expression( + rt: &mut Runtime, + allocator: &mut Allocator, + expression: &Expression, + width: VMWidth, +) -> Reference { + match expression { + Expression::Operand(Operand::Input(index)) => Reference::Value(Value::Input(*index)), + Expression::Operand(Operand::Output(index)) => Reference::Value(Value::Output(*index)), + Expression::Constant(value) => Reference::Immediate(*value as i64), + Expression::Flag(flag) => { + let dst = allocator.acquire(rt, &[], &[]); + + vreg::load_reg(rt, r12, VMReg::Flags, dst); + + let bit = flag.bit32().trailing_zeros() as i32; + + if bit > 0 { + rt.asm.shr(dst, bit).unwrap(); + } + + rt.asm.and(dst, 0x1).unwrap(); + + let tmp = allocator.temporary(); + + allocator.replace(dst, tmp, true); + + Reference::Value(tmp) + } + Expression::Compare(compare) => compile_compare(rt, allocator, compare, width), + Expression::Parity(node) => compile_parity(rt, allocator, node, width), + Expression::SignBit => Reference::Immediate(width.mask().ilog2() as i64), + Expression::BitSize => Reference::Immediate((width.size() * 8) as i64), + Expression::ByteMask(n) => { + Reference::Immediate(((0xFF as u64) << ((width.size() - 1 - n) * 8)) as i64) + } + Expression::Add(first, second) + | Expression::Sub(first, second) + | Expression::Mul(first, second) + | Expression::BitAnd(first, second) + | Expression::BitOr(first, second) + | Expression::BitXor(first, second) => { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, first, dst); + mask(rt, dst, width); + + let src = allocator.acquire(rt, &pinned, &[dst]); + allocator.copy(rt, second, src); + mask(rt, src, width); + + match expression { + Expression::Add(_, _) => rt.asm.add(dst, src).unwrap(), + Expression::Sub(_, _) => rt.asm.sub(dst, src).unwrap(), + Expression::Mul(_, _) => rt.asm.imul_2(dst, src).unwrap(), + Expression::BitAnd(_, _) => rt.asm.and(dst, src).unwrap(), + Expression::BitOr(_, _) => rt.asm.or(dst, src).unwrap(), + Expression::BitXor(_, _) => rt.asm.xor(dst, src).unwrap(), + _ => unreachable!(), + } + + mask(rt, dst, width); + + allocator.consume(first); + allocator.consume(second); + + if src != dst { + allocator.spill(rt, src); + } + + let tmp = allocator.temporary(); + allocator.replace(dst, tmp, true); + + Reference::Value(tmp) + } + Expression::BitShr(first, second) | Expression::BitShl(first, second) => { + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + allocator.copy(rt, second, rcx); + + let dst = allocator.acquire(rt, &pinned, &[rcx]); + allocator.copy(rt, first, dst); + mask(rt, dst, width); + + match expression { + Expression::BitShr(_, _) => rt.asm.shr(dst, cl).unwrap(), + Expression::BitShl(_, _) => rt.asm.shl(dst, cl).unwrap(), + _ => unreachable!(), + } + + mask(rt, dst, width); + + allocator.consume(first); + allocator.consume(second); + + let tmp = allocator.temporary(); + allocator.replace(dst, tmp, true); + + Reference::Value(tmp) + } + Expression::BitNot(inner) | Expression::LowByte(inner) => { + let src = compile_expression(rt, allocator, inner, width); + + let dst = allocator.acquire(rt, &[], &[]); + + allocator.copy(rt, src, dst); + + match expression { + Expression::BitNot(_) => rt.asm.not(dst).unwrap(), + Expression::LowByte(_) => { + let byte = get_gpr8(register_of_size(dst.into(), 1).unwrap()).unwrap(); + rt.asm.movzx(dst, byte).unwrap(); + } + _ => unreachable!(), + } + + allocator.consume(src); + + let tmp = allocator.temporary(); + allocator.replace(dst, tmp, true); + + Reference::Value(tmp) + } + } +} + +fn compile_flags(rt: &mut Runtime, allocator: &mut Allocator, flags: &Flags, width: VMWidth) { + let register = allocator.acquire(rt, &[], &[]); + + vreg::load_reg(rt, r12, VMReg::Flags, register); + + rt.asm + .mov(qword_ptr(rbp - allocator.offset(Value::Flags)), register) + .unwrap(); + + allocator.untrack(register); + + for (flag, expression) in flags.values() { + let value = compile_expression(rt, allocator, expression, width); + + let pinned = match value { + Reference::Value(value) => vec![value], + Reference::Immediate(_) => vec![], + }; + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, value, dst); + allocator.consume(value); + + let bit = flag.bit32().trailing_zeros() as i32; + let mask = flag.bit32(); + + rt.asm + .and(dword_ptr(rbp - allocator.offset(Value::Flags)), !mask) + .unwrap(); + rt.asm.shl(dst, bit).unwrap(); + rt.asm + .or(dword_ptr(rbp - allocator.offset(Value::Flags)), dst) + .unwrap(); + + allocator.spill(rt, dst); + } + + match flags.condition() { + None => { + let register = allocator.acquire(rt, &[], &[]); + + rt.asm + .mov(register, qword_ptr(rbp - allocator.offset(Value::Flags))) + .unwrap(); + + vreg::store_reg(rt, r12, register, VMReg::Flags); + + allocator.untrack(register); + } + Some(condition) => { + let condition = compile_expression(rt, allocator, condition, width); + + let pinned = match condition { + Reference::Value(value) => vec![value], + Reference::Immediate(_) => vec![], + }; + + let selector = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, condition, selector); + allocator.consume(condition); + + let register = allocator.acquire(rt, &[], &[selector]); + + rt.asm + .mov(register, qword_ptr(rbp - allocator.offset(Value::Flags))) + .unwrap(); + + let target = allocator.acquire(rt, &[], &[selector, register]); + rt.asm + .lea(target, ptr(r12 + rt.mapper.index(VMReg::Flags) as i32 * 8)) + .unwrap(); + + let skip = allocator.acquire(rt, &[], &[selector, register, target]); + rt.asm + .lea(skip, ptr(rbp - allocator.offset(Value::Flags))) + .unwrap(); + + rt.asm.test(selector, selector).unwrap(); + rt.asm.cmovz(target, skip).unwrap(); + rt.asm.mov(ptr(target), register).unwrap(); + } + } +} + +fn compile_compare( + rt: &mut Runtime, + allocator: &mut Allocator, + compare: &Compare, + width: VMWidth, +) -> Reference { + let (first, second) = match compare { + Compare::Equal(first, second) + | Compare::LessThan(first, second) + | Compare::GreaterThan(first, second) + | Compare::BitSet(first, second) => (first, second), + }; + + let first = compile_expression(rt, allocator, first, width); + let second = compile_expression(rt, allocator, second, width); + + let pinned = [first, second] + .into_iter() + .filter_map(|value| match value { + Reference::Value(value) => Some(value), + Reference::Immediate(_) => None, + }) + .collect::>(); + + let dst = allocator.acquire(rt, &pinned, &[]); + allocator.copy(rt, first, dst); + mask(rt, dst, width); + + let src = allocator.acquire(rt, &pinned, &[dst]); + allocator.copy(rt, second, src); + mask(rt, src, width); + + let tmp = allocator.acquire(rt, &pinned, &[dst, src]); + + let byte = get_gpr8(register_of_size(tmp.into(), 1).unwrap()).unwrap(); + + match compare { + Compare::Equal(_, _) => { + rt.asm.cmp(dst, src).unwrap(); + rt.asm.sete(byte).unwrap(); + } + Compare::LessThan(_, _) => { + rt.asm.cmp(dst, src).unwrap(); + rt.asm.setb(byte).unwrap(); + } + Compare::GreaterThan(_, _) => { + rt.asm.cmp(dst, src).unwrap(); + rt.asm.seta(byte).unwrap(); + } + Compare::BitSet(_, _) => { + rt.asm.bt(dst, src).unwrap(); + rt.asm.setc(byte).unwrap(); + } + } + + rt.asm.movzx(tmp, byte).unwrap(); + + allocator.consume(first); + allocator.consume(second); + allocator.spill(rt, dst); + + if src != dst { + allocator.spill(rt, src); + } + + let value = allocator.temporary(); + allocator.replace(tmp, value, true); + + Reference::Value(value) +} + +fn compile_parity( + rt: &mut Runtime, + allocator: &mut Allocator, + node: &Expression, + width: VMWidth, +) -> Reference { + let src = compile_expression(rt, allocator, node, width); + let dst = allocator.acquire(rt, &[], &[]); + + allocator.copy(rt, src, dst); + + let byte = get_gpr8(register_of_size(dst.into(), 1).unwrap()).unwrap(); + rt.asm.movzx(dst, byte).unwrap(); + rt.asm.popcnt(dst, dst).unwrap(); + rt.asm.not(dst).unwrap(); + rt.asm.and(dst, 0x1).unwrap(); + + allocator.consume(src); + + let value = allocator.temporary(); + allocator.replace(dst, value, true); + + Reference::Value(value) +} diff --git a/runtime/src/vm/handlers/semantic/mod.rs b/runtime/src/vm/handlers/semantic/mod.rs new file mode 100644 index 00000000..78a2ac87 --- /dev/null +++ b/runtime/src/vm/handlers/semantic/mod.rs @@ -0,0 +1,504 @@ +use rand::Rng; + +use crate::vm::bytecode::{Flag, VMWidth}; + +mod allocator; +pub mod compiler; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Operand { + Input(usize), + Output(usize), +} + +#[derive(Debug, Clone)] +pub enum Expression { + Operand(Operand), + Constant(u64), + BitAnd(Box, Box), + BitOr(Box, Box), + BitXor(Box, Box), + BitShr(Box, Box), + BitShl(Box, Box), + BitNot(Box), + Add(Box, Box), + Sub(Box, Box), + Mul(Box, Box), + LowByte(Box), + Compare(Box), + Parity(Box), + Flag(Flag), + SignBit, + BitSize, + ByteMask(usize), +} + +#[derive(Debug, Clone)] +pub enum Effect { + Add(Expression, Expression), + Sub(Expression, Expression), + And(Expression, Expression), + Or(Expression, Expression), + Xor(Expression, Expression), + Shr(Expression, Expression), + Shl(Expression, Expression), + Ror(Expression, Expression), + Rol(Expression, Expression), + Mul(Expression, Expression), + Div(Expression, Expression, Expression), + Assign(Expression), + Sar(Expression, Expression), + Bsr(Expression), + Tzcnt(Expression), + Exchange(Expression, Expression), + ExchangeAdd(Expression, Expression), + CompareExchange(Expression, Expression, Expression), +} + +#[derive(Debug, Clone)] +pub enum Compare { + Equal(Expression, Expression), + LessThan(Expression, Expression), + GreaterThan(Expression, Expression), + BitSet(Expression, Expression), +} + +#[derive(Debug, Clone)] +pub enum Flags { + Always(Vec<(Flag, Expression)>), + When(Expression, Vec<(Flag, Expression)>), +} + +#[derive(Debug)] +pub struct Operation { + pub effects: Vec, + pub flags: Flags, + pub stores: Option>, + pub widths: &'static [VMWidth], +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +enum Value { + Input(usize), + Output(usize), + Flags, + Temporary(usize), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Reference { + Value(Value), + Immediate(i64), +} + +impl Expression { + fn obfuscate(self, rng: &mut impl Rng, budget: &mut u32) -> Expression { + let expression = match self { + Expression::BitAnd(a, b) => Expression::BitAnd( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::BitOr(a, b) => Expression::BitOr( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::BitXor(a, b) => Expression::BitXor( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::BitShr(a, b) => Expression::BitShr( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::BitShl(a, b) => Expression::BitShl( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::Add(a, b) => Expression::Add( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::Sub(a, b) => Expression::Sub( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::Mul(a, b) => Expression::Mul( + Box::new(a.obfuscate(rng, budget)), + Box::new(b.obfuscate(rng, budget)), + ), + Expression::BitNot(a) => Expression::BitNot(Box::new(a.obfuscate(rng, budget))), + Expression::LowByte(a) => Expression::LowByte(Box::new(a.obfuscate(rng, budget))), + Expression::Parity(a) => Expression::Parity(Box::new(a.obfuscate(rng, budget))), + Expression::Compare(compare) => { + Expression::Compare(Box::new(compare.obfuscate(rng, budget))) + } + leaf => leaf, + }; + + if *budget == 0 || rng.gen() { + return expression; + } + + match expression { + // a ^ b = (a | b) - (a & b) + Expression::BitXor(a, b) => { + *budget -= 1; + Expression::Sub( + Box::new(Expression::BitOr(a.clone(), b.clone())), + Box::new(Expression::BitAnd(a, b)), + ) + } + // a | b = ~(~a & ~b) + Expression::BitOr(a, b) => { + *budget -= 1; + Expression::BitNot(Box::new(Expression::BitAnd( + Box::new(Expression::BitNot(a)), + Box::new(Expression::BitNot(b)), + ))) + } + // a & b = ~(~a | ~b) + Expression::BitAnd(a, b) => { + *budget -= 1; + Expression::BitNot(Box::new(Expression::BitOr( + Box::new(Expression::BitNot(a)), + Box::new(Expression::BitNot(b)), + ))) + } + // ~a = -a - 1 + Expression::BitNot(a) => { + *budget -= 1; + Expression::Sub( + Box::new(Expression::Sub(Box::new(Expression::Constant(0)), a)), + Box::new(Expression::Constant(1)), + ) + } + // a + b = (a ^ b) + ((a & b) << 1) + Expression::Add(a, b) => { + *budget -= 1; + Expression::Add( + Box::new(Expression::BitXor(a.clone(), b.clone())), + Box::new(Expression::BitShl( + Box::new(Expression::BitAnd(a, b)), + Box::new(Expression::Constant(1)), + )), + ) + } + // a - b = a + ~b + 1 + Expression::Sub(a, b) => { + *budget -= 1; + Expression::Add( + Box::new(Expression::Add(a, Box::new(Expression::BitNot(b)))), + Box::new(Expression::Constant(1)), + ) + } + other => other, + } + } +} + +impl Compare { + fn obfuscate(self, rng: &mut impl Rng, budget: &mut u32) -> Compare { + match self { + Compare::Equal(a, b) => { + Compare::Equal(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + Compare::LessThan(a, b) => { + Compare::LessThan(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + Compare::GreaterThan(a, b) => { + Compare::GreaterThan(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + Compare::BitSet(a, b) => { + Compare::BitSet(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + } + } +} + +impl Effect { + fn obfuscate(self, rng: &mut impl Rng, budget: &mut u32) -> Effect { + match self { + Effect::Add(a, b) => Effect::Add(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Sub(a, b) => Effect::Sub(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::And(a, b) => Effect::And(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Or(a, b) => Effect::Or(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Xor(a, b) => Effect::Xor(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Shr(a, b) => Effect::Shr(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Shl(a, b) => Effect::Shl(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Ror(a, b) => Effect::Ror(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Rol(a, b) => Effect::Rol(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Mul(a, b) => Effect::Mul(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Div(a, b, c) => Effect::Div( + a.obfuscate(rng, budget), + b.obfuscate(rng, budget), + c.obfuscate(rng, budget), + ), + Effect::Assign(a) => Effect::Assign(a.obfuscate(rng, budget)), + Effect::Sar(a, b) => Effect::Sar(a.obfuscate(rng, budget), b.obfuscate(rng, budget)), + Effect::Bsr(a) => Effect::Bsr(a.obfuscate(rng, budget)), + Effect::Tzcnt(a) => Effect::Tzcnt(a.obfuscate(rng, budget)), + Effect::Exchange(a, b) => { + Effect::Exchange(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + Effect::ExchangeAdd(a, b) => { + Effect::ExchangeAdd(a.obfuscate(rng, budget), b.obfuscate(rng, budget)) + } + Effect::CompareExchange(a, b, c) => Effect::CompareExchange( + a.obfuscate(rng, budget), + b.obfuscate(rng, budget), + c.obfuscate(rng, budget), + ), + } + } +} + +impl Operation { + fn operands(&self) -> usize { + let mut value = 0; + + for effect in &self.effects { + effect.operands(&mut value); + } + + self.flags.operands(&mut value); + + if let Some(stores) = &self.stores { + for store in stores { + store.operands(&mut value); + } + } + + value + } + + fn outputs(&self) -> usize { + let mut value = 0; + + for effect in &self.effects { + effect.outputs(&mut value); + } + + if let Some(stores) = &self.stores { + value = value.max(stores.len()); + } + + value + } + + fn temporaries(&self) -> usize { + let effects = self.effects.iter().map(Effect::temporary).sum::(); + let flags = self.flags.temporary(); + let stores = self + .stores + .as_ref() + .map(|stores| stores.iter().map(Expression::temporary).sum::()) + .unwrap_or(0); + + effects + flags + stores + } +} + +impl Flags { + fn values(&self) -> &[(Flag, Expression)] { + match self { + Flags::Always(values) | Flags::When(_, values) => values, + } + } + + fn condition(&self) -> Option<&Expression> { + match self { + Flags::Always(_) => None, + Flags::When(condition, _) => Some(condition), + } + } + + fn is_empty(&self) -> bool { + self.values().is_empty() + } + + fn operands(&self, value: &mut usize) { + if let Some(condition) = self.condition() { + condition.operands(value); + } + + for (_, expression) in self.values() { + expression.operands(value); + } + } + + fn temporary(&self) -> usize { + let condition = self.condition().map(Expression::temporary).unwrap_or(0); + let values = self + .values() + .iter() + .map(|(_, expression)| expression.temporary()) + .sum::(); + + condition + values + } +} + +impl Expression { + fn operands(&self, value: &mut usize) { + match self { + Expression::Operand(Operand::Input(index)) => { + *value = (*value).max(*index + 1); + } + Expression::Operand(Operand::Output(_)) + | Expression::Constant(_) + | Expression::Flag(_) + | Expression::SignBit + | Expression::BitSize + | Expression::ByteMask(_) => {} + Expression::BitAnd(first, second) + | Expression::BitOr(first, second) + | Expression::BitXor(first, second) + | Expression::BitShr(first, second) + | Expression::BitShl(first, second) + | Expression::Add(first, second) + | Expression::Sub(first, second) + | Expression::Mul(first, second) => { + first.operands(value); + second.operands(value); + } + Expression::BitNot(inner) | Expression::LowByte(inner) | Expression::Parity(inner) => { + inner.operands(value); + } + Expression::Compare(compare) => compare.operands(value), + } + } + + fn temporary(&self) -> usize { + match self { + Expression::Operand(_) + | Expression::Constant(_) + | Expression::SignBit + | Expression::BitSize + | Expression::ByteMask(_) => 0, + Expression::Flag(_) => 1, + Expression::BitAnd(first, second) + | Expression::BitOr(first, second) + | Expression::BitXor(first, second) + | Expression::BitShr(first, second) + | Expression::BitShl(first, second) + | Expression::Add(first, second) + | Expression::Sub(first, second) + | Expression::Mul(first, second) => 1 + first.temporary() + second.temporary(), + Expression::BitNot(inner) | Expression::LowByte(inner) | Expression::Parity(inner) => { + 1 + inner.temporary() + } + Expression::Compare(compare) => 1 + compare.temporary(), + } + } +} + +impl Effect { + fn operands(&self, value: &mut usize) { + match self { + Effect::Add(first, second) + | Effect::Sub(first, second) + | Effect::And(first, second) + | Effect::Or(first, second) + | Effect::Xor(first, second) + | Effect::Shr(first, second) + | Effect::Shl(first, second) + | Effect::Ror(first, second) + | Effect::Rol(first, second) + | Effect::Mul(first, second) + | Effect::Sar(first, second) + | Effect::Exchange(first, second) + | Effect::ExchangeAdd(first, second) => { + first.operands(value); + second.operands(value); + } + Effect::Div(first, second, third) => { + first.operands(value); + second.operands(value); + third.operands(value); + } + Effect::CompareExchange(first, second, third) => { + first.operands(value); + second.operands(value); + third.operands(value); + } + Effect::Assign(expression) | Effect::Bsr(expression) | Effect::Tzcnt(expression) => { + expression.operands(value); + } + } + } + + fn outputs(&self, value: &mut usize) { + match self { + Effect::Add(..) + | Effect::Sub(..) + | Effect::And(..) + | Effect::Or(..) + | Effect::Xor(..) + | Effect::Shr(..) + | Effect::Shl(..) + | Effect::Ror(..) + | Effect::Rol(..) + | Effect::Sar(..) + | Effect::Assign(..) + | Effect::Bsr(..) + | Effect::Tzcnt(..) + | Effect::Exchange(..) + | Effect::CompareExchange(..) => { + *value = (*value).max(1); + } + Effect::Mul(..) | Effect::Div(..) | Effect::ExchangeAdd(..) => { + *value = (*value).max(2); + } + } + } + + fn temporary(&self) -> usize { + match self { + Effect::Add(first, second) + | Effect::Sub(first, second) + | Effect::And(first, second) + | Effect::Or(first, second) + | Effect::Xor(first, second) + | Effect::Shr(first, second) + | Effect::Shl(first, second) + | Effect::Ror(first, second) + | Effect::Rol(first, second) + | Effect::Mul(first, second) + | Effect::Sar(first, second) + | Effect::Exchange(first, second) + | Effect::ExchangeAdd(first, second) => first.temporary() + second.temporary(), + Effect::Div(first, second, third) => { + first.temporary() + second.temporary() + third.temporary() + } + Effect::CompareExchange(first, second, third) => { + first.temporary() + second.temporary() + third.temporary() + } + Effect::Assign(expression) | Effect::Bsr(expression) | Effect::Tzcnt(expression) => { + expression.temporary() + } + } + } +} + +impl Compare { + fn operands(&self, value: &mut usize) { + match self { + Compare::Equal(first, second) + | Compare::LessThan(first, second) + | Compare::GreaterThan(first, second) + | Compare::BitSet(first, second) => { + first.operands(value); + second.operands(value); + } + } + } + + fn temporary(&self) -> usize { + match self { + Compare::Equal(first, second) + | Compare::LessThan(first, second) + | Compare::GreaterThan(first, second) + | Compare::BitSet(first, second) => first.temporary() + second.temporary(), + } + } +} diff --git a/runtime/src/vm/handlers/shl.rs b/runtime/src/vm/handlers/shl.rs index 1669752e..c5589c01 100644 --- a/runtime/src/vm/handlers/shl.rs +++ b/runtime/src/vm/handlers/shl.rs @@ -1,10 +1,87 @@ -crate::vm::handlers::arithmetic!( - shl, - shift, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Shl( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::When( + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + ), + vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::Sub( + Box::new(Expression::BitSize), + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + )), + ), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::GreaterThan( + Expression::BitXor( + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::SignBit), + )), + Box::new(Expression::BitShr( + Box::new(Expression::Operand(Operand::Output(0))), + Box::new(Expression::SignBit), + )), + ), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ], + ), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/shr.rs b/runtime/src/vm/handlers/shr.rs index 89573329..b4a8ebbc 100644 --- a/runtime/src/vm/handlers/shr.rs +++ b/runtime/src/vm/handlers/shr.rs @@ -1,10 +1,78 @@ -crate::vm::handlers::arithmetic!( - shr, - shift, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Shr( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::When( + Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + ), + vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::Sub( + Box::new(Expression::BitAnd( + Box::new(Expression::Operand(Operand::Input(1))), + Box::new(Expression::BitOr( + Box::new(Expression::SignBit), + Box::new(Expression::Constant(0x1f)), + )), + )), + Box::new(Expression::Constant(1)), + ), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Input(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ], + ), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/store_extend.rs b/runtime/src/vm/handlers/store_extend.rs index 777ef638..24ad8865 100644 --- a/runtime/src/vm/handlers/store_extend.rs +++ b/runtime/src/vm/handlers/store_extend.rs @@ -28,22 +28,22 @@ pub fn build(rt: &mut Runtime) { r8, &mut epilogue, Some(Box::new(|rt| { + // load ymm0 + rt.asm.vmovups(ymm0, ptr(rax + r9)).unwrap(); // load r8 scratch::load(rt, r12, r8); - // vpxor ymm0, ymm0, ymm0 - rt.asm.vpxor(xmm0, xmm0, xmm0).unwrap(); - // vmovq xmm0, r8 - rt.asm.vmovq(xmm0, r8).unwrap(); + // movq xmm0, r8 + rt.asm.movq(xmm0, r8).unwrap(); // vmovups [rax + r9], ymm0 rt.asm.vmovups(ptr(rax + r9), ymm0).unwrap(); })), Some(Box::new(|rt| { + // load ymm0 + rt.asm.vmovups(ymm0, ptr(rax + r9)).unwrap(); // load r8 scratch::load(rt, r12, r8); - // vpxor ymm0, ymm0, ymm0 - rt.asm.vpxor(xmm0, xmm0, xmm0).unwrap(); - // vmovd xmm0, r8d - rt.asm.vmovd(xmm0, r8d).unwrap(); + // movd xmm0, r8d + rt.asm.movd(xmm0, r8d).unwrap(); // vmovups [rax + r9], ymm0 rt.asm.vmovups(ptr(rax + r9), ymm0).unwrap(); })), diff --git a/runtime/src/vm/handlers/store_register.rs b/runtime/src/vm/handlers/store_register.rs index 5d20bf1a..57df52f6 100644 --- a/runtime/src/vm/handlers/store_register.rs +++ b/runtime/src/vm/handlers/store_register.rs @@ -1,4 +1,4 @@ -use iced_x86::code_asm::{eax, ptr, r12, r8, r8d, r9, r9b, r9w, rax, rcx}; +use iced_x86::code_asm::{eax, ptr, r12, r8, r8d, r9, r9b, r9d, r9w, rax, rcx}; use crate::{ runtime::Runtime, @@ -15,31 +15,38 @@ pub fn build(rt: &mut Runtime) { // r8d -> destination utils::bytecode::read_byte_zx(rt, rcx, r8d); - // load r9 - scratch::load(rt, r12, r9); - utils::width::dispatch( rt, rax, &mut epilogue, Some(Box::new(|rt| { + // load r9 + scratch::load(rt, r12, r9); // mov [r12 + r8*8], r9 rt.asm.mov(ptr(r12 + r8 * 8), r9).unwrap(); })), Some(Box::new(|rt| { + // load r9d + scratch::load_32(rt, r12, r9d); // mov [r12 + r8*8], r9 rt.asm.mov(ptr(r12 + r8 * 8), r9).unwrap(); })), None, Some(Box::new(|rt| { + // load r9 + scratch::load(rt, r12, r9); // mov [r12 + r8*8], r9w rt.asm.mov(ptr(r12 + r8 * 8), r9w).unwrap(); })), Some(Box::new(|rt| { + // load r9 + scratch::load(rt, r12, r9); // mov [r12 + r8*8 + 0x1], r9b rt.asm.mov(ptr(r12 + r8 * 8 + 0x1), r9b).unwrap(); })), Some(Box::new(|rt| { + // load r9 + scratch::load(rt, r12, r9); // mov [r12 + r8*8], r9b rt.asm.mov(ptr(r12 + r8 * 8), r9b).unwrap(); })), diff --git a/runtime/src/vm/handlers/sub.rs b/runtime/src/vm/handlers/sub.rs index 87e3ee54..43d080ff 100644 --- a/runtime/src/vm/handlers/sub.rs +++ b/runtime/src/vm/handlers/sub.rs @@ -1,10 +1,82 @@ -crate::vm::handlers::arithmetic!( - sub, - r8, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Sub( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::LessThan( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitAnd( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Output(0))), + )), + ), + Expression::SignBit, + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ( + Flag::Auxiliary, + Expression::Compare(Box::new(Compare::BitSet( + Expression::BitXor( + Box::new(Expression::BitXor( + Box::new(Expression::Operand(Operand::Input(0))), + Box::new(Expression::Operand(Operand::Input(1))), + )), + Box::new(Expression::Operand(Operand::Output(0))), + ), + Expression::Constant(4), + ))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/handlers/trailing_zeros.rs b/runtime/src/vm/handlers/trailing_zeros.rs index f630c76a..495a6b85 100644 --- a/runtime/src/vm/handlers/trailing_zeros.rs +++ b/runtime/src/vm/handlers/trailing_zeros.rs @@ -1,10 +1,34 @@ -crate::vm::handlers::arithmetic!( - tzcnt, - bitscan, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Tzcnt(Expression::Operand(Operand::Input(0)))], + flags: Flags::Always(vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Input(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ]), + stores: None, + widths: &[VMWidth::Lower64, VMWidth::Lower32, VMWidth::Lower16], + }, + ); +} diff --git a/runtime/src/vm/handlers/vector_add.rs b/runtime/src/vm/handlers/vector_add.rs index 6e50d8f9..40e45184 100644 --- a/runtime/src/vm/handlers/vector_add.rs +++ b/runtime/src/vm/handlers/vector_add.rs @@ -34,9 +34,10 @@ pub fn build(rt: &mut Runtime) { rt.asm.addps(xmm0, xmm1).unwrap(); }, |rt| { - // addsd xmm0, xmm1 - rt.asm.addsd(xmm0, xmm1).unwrap(); + // addpd xmm0, xmm1 + rt.asm.addpd(xmm0, xmm1).unwrap(); }, + // avx_int |rt| { // vpaddb ymm0, ymm0, ymm1 rt.asm.vpaddb(ymm0, ymm0, ymm1).unwrap(); @@ -66,8 +67,8 @@ pub fn build(rt: &mut Runtime) { rt.asm.vaddps(ymm0, ymm0, ymm1).unwrap(); }, |rt| { - // vaddsd ymm0, ymm0, ymm1 - rt.asm.vaddsd(xmm0, xmm0, xmm1).unwrap(); + // vaddpd ymm0, ymm0, ymm1 + rt.asm.vaddpd(ymm0, ymm0, ymm1).unwrap(); }, ); } diff --git a/runtime/src/vm/handlers/vector_sub.rs b/runtime/src/vm/handlers/vector_sub.rs index 6799d522..a82af39c 100644 --- a/runtime/src/vm/handlers/vector_sub.rs +++ b/runtime/src/vm/handlers/vector_sub.rs @@ -33,8 +33,8 @@ pub fn build(rt: &mut Runtime) { rt.asm.subps(xmm0, xmm1).unwrap(); }, |rt| { - // subsd xmm0, xmm1 - rt.asm.subsd(xmm0, xmm1).unwrap(); + // subpd xmm0, xmm1 + rt.asm.subpd(xmm0, xmm1).unwrap(); }, // avx_int |rt| { @@ -66,8 +66,8 @@ pub fn build(rt: &mut Runtime) { rt.asm.vsubps(ymm0, ymm0, ymm1).unwrap(); }, |rt| { - // vsubsd ymm0, ymm0, ymm1 - rt.asm.vsubsd(xmm0, xmm0, xmm1).unwrap(); + // vsubpd ymm0, ymm0, ymm1 + rt.asm.vsubpd(ymm0, ymm0, ymm1).unwrap(); }, ); } diff --git a/runtime/src/vm/handlers/xor.rs b/runtime/src/vm/handlers/xor.rs index 27ab3eaa..50ba21cc 100644 --- a/runtime/src/vm/handlers/xor.rs +++ b/runtime/src/vm/handlers/xor.rs @@ -1,10 +1,60 @@ -crate::vm::handlers::arithmetic!( - xor, - r8, - crate::vm::bytecode::Flag::Carry.bit64() - | crate::vm::bytecode::Flag::Overflow.bit64() - | crate::vm::bytecode::Flag::Sign.bit64() - | crate::vm::bytecode::Flag::Auxiliary.bit64() - | crate::vm::bytecode::Flag::Zero.bit64() - | crate::vm::bytecode::Flag::Parity.bit64() -); +use crate::{ + runtime::Runtime, + vm::{ + bytecode::{Flag, VMWidth}, + handlers::semantic::{self, Compare, Effect, Expression, Flags, Operand, Operation}, + }, +}; + +pub fn build(rt: &mut Runtime) { + semantic::compiler::compile( + rt, + &Operation { + effects: vec![Effect::Xor( + Expression::Operand(Operand::Input(0)), + Expression::Operand(Operand::Input(1)), + )], + flags: Flags::Always(vec![ + ( + Flag::Carry, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Overflow, + Expression::Compare(Box::new(Compare::Equal( + Expression::Constant(1), + Expression::Constant(0), + ))), + ), + ( + Flag::Sign, + Expression::Compare(Box::new(Compare::BitSet( + Expression::Operand(Operand::Output(0)), + Expression::SignBit, + ))), + ), + ( + Flag::Zero, + Expression::Compare(Box::new(Compare::Equal( + Expression::Operand(Operand::Output(0)), + Expression::Constant(0), + ))), + ), + ( + Flag::Parity, + Expression::Parity(Box::new(Expression::Operand(Operand::Output(0)))), + ), + ]), + stores: None, + widths: &[ + VMWidth::Lower64, + VMWidth::Lower32, + VMWidth::Lower16, + VMWidth::Lower8, + ], + }, + ); +} diff --git a/runtime/src/vm/lifters/arithmetic.rs b/runtime/src/vm/lifters/arithmetic.rs index f3156584..6fe25eab 100644 --- a/runtime/src/vm/lifters/arithmetic.rs +++ b/runtime/src/vm/lifters/arithmetic.rs @@ -1,15 +1,15 @@ use iced_x86::{Instruction, Mnemonic, OpKind}; -use crate::vm::bytecode::{Flag, VMMem, VMPrecision, VMReg, VMVec, VMWidth}; +use crate::vm::bytecode::{VMMem, VMPrecision, VMReg, VMVec, VMWidth}; use crate::vm::encoders::vector_div::VectorDiv; use crate::vm::encoders::vector_mul::VectorMul; use crate::vm::encoders::{ - add::Add, and::And, discard::Discard, load_address::LoadAddress, load_immediate::LoadImmediate, - load_memory::LoadMemory, load_register::LoadRegister, load_vector::LoadVector, or::Or, - rol::Rol, ror::Ror, sar::Sar, shl::Shl, shr::Shr, store_memory::StoreMemory, - store_merge::StoreMerge, store_register::StoreRegister, sub::Sub, vector_add::VectorAdd, - vector_and::VectorAnd, vector_and_not::VectorAndNot, vector_or::VectorOr, - vector_sub::VectorSub, vector_xor::VectorXor, xor::Xor, Encode, + adc::Adc, add::Add, and::And, discard::Discard, load_address::LoadAddress, + load_immediate::LoadImmediate, load_memory::LoadMemory, load_register::LoadRegister, + load_vector::LoadVector, or::Or, rol::Rol, ror::Ror, sar::Sar, sbb::Sbb, shl::Shl, shr::Shr, + store_memory::StoreMemory, store_merge::StoreMerge, store_register::StoreRegister, sub::Sub, + vector_add::VectorAdd, vector_and::VectorAnd, vector_and_not::VectorAndNot, + vector_or::VectorOr, vector_sub::VectorSub, vector_xor::VectorXor, xor::Xor, Encode, }; use crate::vm::lifters::{operation_width, source}; @@ -22,8 +22,8 @@ pub fn encode(instruction: &Instruction) -> Option>> { match instruction.mnemonic() { Mnemonic::Add => binary(instruction, |width| Add { width }, Tail::Writeback), Mnemonic::Sub => binary(instruction, |width| Sub { width }, Tail::Writeback), - Mnemonic::Adc => carry(instruction, |width| Add { width }), - Mnemonic::Sbb => carry(instruction, |width| Sub { width }), + Mnemonic::Adc => binary(instruction, |width| Adc { width }, Tail::Writeback), + Mnemonic::Sbb => binary(instruction, |width| Sbb { width }, Tail::Writeback), Mnemonic::Shl => binary(instruction, |width| Shl { width }, Tail::Writeback), Mnemonic::Shr => binary(instruction, |width| Shr { width }, Tail::Writeback), Mnemonic::Sar => binary(instruction, |width| Sar { width }, Tail::Writeback), @@ -191,52 +191,6 @@ pub fn binary( Some(operations) } -pub fn carry( - instruction: &Instruction, - make: impl Fn(VMWidth) -> O, -) -> Option>> { - let mut operations = Vec::>::new(); - - let width = operation_width(instruction, 0); - - source(&mut operations, instruction, 0, width); - source(&mut operations, instruction, 1, width); - - operations.push(Box::new(LoadRegister { - width: VMWidth::Lower64, - source: VMReg::Flags, - })); - operations.push(Box::new(LoadImmediate { - width: VMWidth::Lower64, - source: Flag::Carry.bit64().to_le_bytes().to_vec(), - })); - operations.push(Box::new(And { - width: VMWidth::Lower64, - })); - - operations.push(Box::new(Add { width })); - - operations.push(Box::new(make(width))); - - match instruction.op0_kind() { - OpKind::Register => { - operations.push(Box::new(StoreRegister { - width, - destination: VMReg::from(instruction.op0_register()), - })); - } - OpKind::Memory => { - operations.push(Box::new(LoadAddress { - source: VMMem::from(instruction), - })); - operations.push(Box::new(StoreMemory { width })); - } - _ => unreachable!(), - }; - - Some(operations) -} - fn unary( instruction: &Instruction, immediate: u64, diff --git a/runtime/src/vm/lifters/branch.rs b/runtime/src/vm/lifters/branch.rs index 8529f596..9efa7036 100644 --- a/runtime/src/vm/lifters/branch.rs +++ b/runtime/src/vm/lifters/branch.rs @@ -1,6 +1,6 @@ use iced_x86::{Code, Instruction, OpKind}; -use crate::vm::bytecode::{VMCondition, Flag, VMLogic, VMMem, VMReg, VMSeg, VMWidth}; +use crate::vm::bytecode::{Flag, VMCondition, VMLogic, VMMem, VMReg, VMSeg, VMWidth}; use crate::vm::encoders::load_address::LoadAddress; use crate::vm::encoders::load_immediate::LoadImmediate; use crate::vm::encoders::load_memory::LoadMemory; diff --git a/runtime/src/vm/lifters/bt.rs b/runtime/src/vm/lifters/bt.rs index 03163b6b..234a691f 100644 --- a/runtime/src/vm/lifters/bt.rs +++ b/runtime/src/vm/lifters/bt.rs @@ -1,6 +1,5 @@ use iced_x86::{Instruction, Mnemonic}; - use crate::vm::encoders::{ bit_test::BitTest, bit_test_complement::BitTestComplement, bit_test_reset::BitTestReset, bit_test_set::BitTestSet, Encode, diff --git a/runtime/src/vm/lifters/cmov.rs b/runtime/src/vm/lifters/cmov.rs index 9630ef91..167f00d3 100644 --- a/runtime/src/vm/lifters/cmov.rs +++ b/runtime/src/vm/lifters/cmov.rs @@ -1,6 +1,6 @@ use iced_x86::{Code, Instruction}; -use crate::vm::bytecode::{VMCondition, Flag, VMLogic}; +use crate::vm::bytecode::{Flag, VMCondition, VMLogic}; use crate::vm::encoders::block::Block; use crate::vm::encoders::Encode; use crate::vm::lifters::transfer; @@ -80,5 +80,17 @@ pub fn encode(instruction: &Instruction) -> Option>> { let body = transfer::encode(instruction)?; - Some(vec![Box::new(Block::skip(logic, conditions, body))]) + let mut encoded = Vec::new(); + + if instruction.op0_register().is_gpr32() { + let mut zero_extend = Instruction::default(); + zero_extend.set_code(Code::Mov_rm32_r32); + zero_extend.set_op_register(0, instruction.op0_register()); + zero_extend.set_op_register(1, instruction.op0_register()); + encoded.extend(transfer::encode(&zero_extend)?); + } + + encoded.push(Box::new(Block::skip(logic, conditions, body))); + + Some(encoded) } diff --git a/runtime/src/vm/lifters/cmpxchg.rs b/runtime/src/vm/lifters/cmpxchg.rs index a07d0a6f..89be758f 100644 --- a/runtime/src/vm/lifters/cmpxchg.rs +++ b/runtime/src/vm/lifters/cmpxchg.rs @@ -1,6 +1,6 @@ use iced_x86::{Instruction, OpKind}; -use crate::vm::bytecode::{VMCondition, Flag, VMLogic, VMMem, VMReg}; +use crate::vm::bytecode::{Flag, VMCondition, VMLogic, VMMem, VMReg}; use crate::vm::encoders::block::Block; use crate::vm::encoders::{ compare_exchange::CompareExchange, discard::Discard, load_address::LoadAddress, diff --git a/runtime/src/vm/lifters/extend.rs b/runtime/src/vm/lifters/extend.rs index fb8f349c..e9d50064 100644 --- a/runtime/src/vm/lifters/extend.rs +++ b/runtime/src/vm/lifters/extend.rs @@ -1,7 +1,6 @@ use iced_x86::{Instruction, Mnemonic, OpKind}; - -use crate::vm::bytecode::{VMMem, VMReg, VMWidth}; +use crate::vm::bytecode::{VMMem, VMReg}; use crate::vm::encoders::{ load_address::LoadAddress, load_memory::LoadMemory, load_register::LoadRegister, store_register::StoreRegister, Encode, @@ -13,10 +12,7 @@ pub fn encode(instruction: &Instruction) -> Option>> { let signed = matches!(instruction.mnemonic(), Mnemonic::Movsx | Mnemonic::Movsxd); - let destination_width = match VMWidth::from(instruction.op0_register()) { - VMWidth::Lower16 => VMWidth::Lower16, - _ => VMWidth::Lower64, - }; + let destination_width = operation_width(instruction, 0); let destination_register = VMReg::from(instruction.op0_register()); let source_width = operation_width(instruction, 1); @@ -29,11 +25,13 @@ pub fn encode(instruction: &Instruction) -> Option>> { match instruction.op1_kind() { OpKind::Register => { let source_register = VMReg::from(instruction.op1_register()); + operations.push(Box::new(LoadRegister { width: source_width, source: source_register, })); } + OpKind::Memory => { operations.push(Box::new(LoadAddress { source: VMMem::from(instruction), @@ -42,6 +40,7 @@ pub fn encode(instruction: &Instruction) -> Option>> { width: source_width, })); } + _ => unreachable!(), } diff --git a/runtime/src/vm/lifters/lea.rs b/runtime/src/vm/lifters/lea.rs index 92841346..33acb595 100644 --- a/runtime/src/vm/lifters/lea.rs +++ b/runtime/src/vm/lifters/lea.rs @@ -1,6 +1,5 @@ use iced_x86::{Code, Instruction}; - use crate::vm::bytecode::{VMMem, VMReg, VMWidth}; use crate::vm::encoders::{load_address::LoadAddress, store_register::StoreRegister, Encode}; diff --git a/runtime/src/vm/lifters/pcmpeqb.rs b/runtime/src/vm/lifters/pcmpeqb.rs index f3c5e617..98fd9204 100644 --- a/runtime/src/vm/lifters/pcmpeqb.rs +++ b/runtime/src/vm/lifters/pcmpeqb.rs @@ -1,6 +1,5 @@ use iced_x86::{Instruction, OpKind}; - use crate::vm::bytecode::{VMMem, VMVec, VMWidth}; use crate::vm::encoders::{ load_address::LoadAddress, load_memory::LoadMemory, load_vector::LoadVector, diff --git a/runtime/src/vm/lifters/pmovskb.rs b/runtime/src/vm/lifters/pmovskb.rs index 247d4320..51f05cf7 100644 --- a/runtime/src/vm/lifters/pmovskb.rs +++ b/runtime/src/vm/lifters/pmovskb.rs @@ -1,6 +1,5 @@ use iced_x86::Instruction; - use crate::vm::bytecode::{VMReg, VMVec, VMWidth}; use crate::vm::encoders::{ load_vector::LoadVector, packed_byte_mask::PackedByteMask, store_register::StoreRegister, diff --git a/runtime/src/vm/lifters/transfer.rs b/runtime/src/vm/lifters/transfer.rs index 03ceca86..35ba1010 100644 --- a/runtime/src/vm/lifters/transfer.rs +++ b/runtime/src/vm/lifters/transfer.rs @@ -1,6 +1,5 @@ use iced_x86::{Instruction, OpKind}; - use crate::vm::bytecode::{VMMem, VMReg, VMVec, VMWidth}; use crate::vm::encoders::{ load_address::LoadAddress, load_immediate::LoadImmediate, load_memory::LoadMemory, diff --git a/runtime/src/vm/lifters/tzcnt.rs b/runtime/src/vm/lifters/tzcnt.rs index 4b1f94e6..fbfec781 100644 --- a/runtime/src/vm/lifters/tzcnt.rs +++ b/runtime/src/vm/lifters/tzcnt.rs @@ -1,6 +1,5 @@ use iced_x86::Instruction; - use crate::vm::encoders::{trailing_zeros::TrailingZeros, Encode}; use crate::vm::lifters::arithmetic::{self, Tail}; diff --git a/runtime/src/vm/lifters/xadd.rs b/runtime/src/vm/lifters/xadd.rs index 9739ea6a..1c0b1097 100644 --- a/runtime/src/vm/lifters/xadd.rs +++ b/runtime/src/vm/lifters/xadd.rs @@ -1,6 +1,5 @@ use iced_x86::{Instruction, OpKind}; - use crate::vm::bytecode::{VMMem, VMReg}; use crate::vm::encoders::{ add::Add, exchange_add::ExchangeAdd, load_address::LoadAddress, load_register::LoadRegister, diff --git a/runtime/src/vm/lifters/xchg.rs b/runtime/src/vm/lifters/xchg.rs index 1341b47c..8589fdf1 100644 --- a/runtime/src/vm/lifters/xchg.rs +++ b/runtime/src/vm/lifters/xchg.rs @@ -1,6 +1,5 @@ use iced_x86::{Instruction, OpKind, Register}; - use crate::vm::bytecode::{VMMem, VMReg, VMWidth}; use crate::vm::encoders::{ exchange::Exchange, load_address::LoadAddress, load_register::LoadRegister, diff --git a/runtime/src/vm/mod.rs b/runtime/src/vm/mod.rs index e25789ae..9795ea28 100644 --- a/runtime/src/vm/mod.rs +++ b/runtime/src/vm/mod.rs @@ -3,7 +3,6 @@ pub mod encoders; pub mod functions; pub mod handlers; pub mod lifters; -#[cfg(debug_assertions)] mod snapshot; pub mod transform; pub mod utils; diff --git a/runtime/src/vm/transform/encrypt.rs b/runtime/src/vm/transform/encrypt.rs index 498690a0..db8d80b5 100644 --- a/runtime/src/vm/transform/encrypt.rs +++ b/runtime/src/vm/transform/encrypt.rs @@ -3,6 +3,7 @@ use std::{matches, slice}; use rand::Rng; use crate::mapper::Mapper; +use crate::utils::invert_multiplier; use crate::vm::bytecode::{VMReg, VMWidth}; use crate::vm::encoders::discard::Discard; use crate::vm::encoders::jcc::Jcc; @@ -25,7 +26,9 @@ struct Encryptor<'a> { } /// Encrypts every immediate against rolling immediates held in [`VMReg::VImmAdd`] and [`VMReg::VImmMul`]. -pub struct Encrypt; +pub struct Encrypt { + pub offset: u64, +} impl Transform for Encrypt { fn phase(&self) -> Phase { @@ -41,7 +44,16 @@ impl Transform for Encrypt { let mut encryptor = Encryptor::new(mapper, &mut operations, addend, multiplier); encryptor.process(); - encryptor.prologue(addend, multiplier); + + operations.splice( + 0..0, + restore( + addend, + multiplier, + self.offset, + invert_multiplier(self.offset | 1), + ), + ); operations } @@ -72,38 +84,6 @@ impl<'a> Encryptor<'a> { &mut self.multiplier, ); } - - /// Emits the seed sequence for [`VMReg::VImmAdd`] and [`VMReg::VImmMul`]. - fn prologue(&mut self, addend: u64, multiplier: u64) { - self.operations.insert( - 0, - Box::new(LoadImmediate { - width: VMWidth::Lower64, - source: addend.to_le_bytes().to_vec(), - }), - ); - self.operations.insert( - 1, - Box::new(LoadImmediate { - width: VMWidth::Lower64, - source: invert(multiplier).to_le_bytes().to_vec(), - }), - ); - self.operations.insert( - 2, - Box::new(StoreRegister { - width: VMWidth::Lower64, - destination: VMReg::VImmMul, - }), - ); - self.operations.insert( - 3, - Box::new(StoreRegister { - width: VMWidth::Lower64, - destination: VMReg::VImmAdd, - }), - ); - } } /// Emits a sequence to restore [`VMReg::VImmAdd`]/[`VMReg::VImmMul`] using the current values. @@ -122,7 +102,9 @@ fn restore( source, })); - let mut source = invert(destination_multiplier).to_le_bytes().to_vec(); + let mut source = invert_multiplier(destination_multiplier) + .to_le_bytes() + .to_vec(); encrypt(&mut source, source_addend, source_multiplier); sequence.push(Box::new(LoadImmediate { width: VMWidth::Lower64, @@ -266,19 +248,8 @@ fn leaf(operation: &mut Box, addend: u64, multiplier: u64) -> bool { false } -/// Computes the multiplicative inverse of odd `x` modulo 2^64. -fn invert(x: u64) -> u64 { - let mut inverse = x; - - for _ in 0..5 { - inverse = inverse.wrapping_mul(2u64.wrapping_sub(x.wrapping_mul(inverse))); - } - - inverse -} - /// Encrypts `source` against `addend` and `multiplier`. -fn encrypt(source: &mut [u8], addend: u64, multiplier: u64) { +pub fn encrypt(source: &mut [u8], addend: u64, multiplier: u64) { let mut buffer = [0u8; 8]; buffer[..source.len()].copy_from_slice(source); @@ -389,7 +360,7 @@ fn transform(addend: &mut u64, multiplier: &mut u64, preserve: bool) -> Vec Phase { + Phase::Indirect + } + + fn run( + &self, + mapper: &mut Mapper, + mut operations: Vec>, + ) -> Vec> { + indirect(mapper, &mut operations); + operations + } +} + +/// Redirects operations to use an indirect dispatch via [`Dispatch`]. +fn indirect(mapper: &mut Mapper, operations: &mut Vec>) { + let mut index = 0; + + while index < operations.len() { + if let Some(children) = operations[index].children_mut() { + indirect(mapper, children); + } + + let Some(op) = operations[index].op() else { + index += 1; + continue; + }; + + let operation = mem::replace( + &mut operations[index], + Box::new(LoadImmediate { + width: VMWidth::Lower8, + source: mapper.index(op).to_le_bytes().to_vec(), + }), + ); + + index += 1; + + operations.insert(index, Box::new(Dispatch { operation })); + + index += 1; + } +} diff --git a/runtime/src/vm/transform/mod.rs b/runtime/src/vm/transform/mod.rs index c1487f56..4f28a5f1 100644 --- a/runtime/src/vm/transform/mod.rs +++ b/runtime/src/vm/transform/mod.rs @@ -4,12 +4,14 @@ use std::mem; use crate::mapper::Mapper; use crate::vm::bytecode::{VMReg, VMWidth}; use crate::vm::encoders::store_register::StoreRegister; +use crate::vm::encoders::Depth; use crate::vm::{ bytecode::Phase, encoders::{Effect, Encode}, }; pub mod encrypt; +pub mod indirect; pub mod mutation; pub mod peephole; pub mod permute; @@ -49,10 +51,13 @@ pub fn atomize(operations: Vec>) -> Vec>> { /// Atomizes `operations` into depth-balanced atoms, collapsing into one if the sequence is unbalanced. pub fn collapse(operations: Vec>) -> Vec>> { let mut atoms = atomize(operations); + if atoms.len() <= 1 { return atoms; } + let single = atoms.drain(..).flatten().collect(); + vec![single] } @@ -82,13 +87,15 @@ pub fn deadzones( let mut events = Vec::new(); scan(operations, &mut events, &effect); + let first = events.iter().position(|(_, writes)| *writes); + let mut deadzones = vec![false; events.len()]; let mut live = true; for (i, (reads, writes)) in events.iter().enumerate().rev() { if *writes { - live = false; + live = Some(i) == first; } if !live { deadzones[i] = true; diff --git a/runtime/src/vm/transform/mutation/jcc.rs b/runtime/src/vm/transform/mutation/jcc.rs index fe58401b..c7b06d0c 100644 --- a/runtime/src/vm/transform/mutation/jcc.rs +++ b/runtime/src/vm/transform/mutation/jcc.rs @@ -58,7 +58,6 @@ pub fn mutate(operations: &mut Vec>, rng: &mut R) { jcc.conditions.push(y); } } - 1 => { // X OR !X == 1. jcc.logic = or; @@ -70,7 +69,6 @@ pub fn mutate(operations: &mut Vec>, rng: &mut R) { jcc.conditions.push(random(&flags, rng)); } } - _ => { // X AND 1 == X. jcc.logic = and; diff --git a/runtime/src/vm/transform/peephole.rs b/runtime/src/vm/transform/peephole.rs index db08338d..56482595 100644 --- a/runtime/src/vm/transform/peephole.rs +++ b/runtime/src/vm/transform/peephole.rs @@ -36,7 +36,10 @@ fn optimize(operations: &mut Vec>) { .as_any() .downcast_ref::(), ) { - if load.source == store.destination && load.width == store.width { + if load.source == store.destination + && load.width == store.width + && store.width.size() != 4 + { operations.drain(index..index + 2); continue; } @@ -48,7 +51,10 @@ fn optimize(operations: &mut Vec>) { .as_any() .downcast_ref::(), ) { - if store.destination == load.source && store.width == load.width { + if store.destination == load.source + && store.width == load.width + && store.width.size() != 4 + { if overwritten(&operations[index + 2..], store.destination) { operations.drain(index..index + 2); continue; diff --git a/runtime/src/vm/transform/permute.rs b/runtime/src/vm/transform/permute.rs index 763e3444..d40e2986 100644 --- a/runtime/src/vm/transform/permute.rs +++ b/runtime/src/vm/transform/permute.rs @@ -1,16 +1,17 @@ use std::any::Any; +use std::cell::RefCell; use std::collections::HashSet; use std::mem; -use crate::mapper::Mappable; +use crate::mapper::{Mappable, Mapper}; use crate::vm::bytecode::{VMMem, VMReg, VMVec, VMWidth}; use crate::vm::encoders::load_address::LoadAddress; use crate::vm::encoders::load_memory::LoadMemory; use crate::vm::encoders::load_register::LoadRegister; use crate::vm::encoders::store_memory::StoreMemory; use crate::vm::encoders::store_register::StoreRegister; -use crate::vm::encoders::{identity, Effect, Encode}; -use crate::vm::transform::{branches, collapse, descend, effects, vacant}; +use crate::vm::encoders::{identity, Depth, Effect, Encode}; +use crate::vm::transform::{branches, collapse, descend, effects, vacant, Phase, Transform}; struct Access { memory: VMMem, @@ -29,41 +30,50 @@ struct Profile { } /// Shuffles `operations` into a semantically equivalent sequence by using `picker` to select among available atomic blocks. -pub fn permute(mut operations: Vec>, picker: &mut F) -> Vec> -where - F: FnMut(&[usize]) -> usize, -{ - descend(&mut operations, |operations| { - let atoms = collapse(mem::take(operations)); - - let live = atoms - .iter() - .flat_map(|atom| effects(atom).0) - .collect::>(); - let (atoms, parked) = decouple(atoms, &live); - let (successors, mut indegree) = dependencies(&atoms); - - let n = successors.len(); - let mut ready = (0..n).filter(|&i| indegree[i] == 0).collect::>(); - let mut order = Vec::with_capacity(n); - - while !ready.is_empty() { - let chosen = ready.swap_remove(picker(&ready)); - order.push(chosen); - for &next in &successors[chosen] { - indegree[next] -= 1; - if indegree[next] == 0 { - ready.push(next); +pub struct Permute<'a> { + pub picker: &'a RefCell<&'a mut dyn FnMut(&[usize]) -> usize>, +} + +impl<'a> Transform for Permute<'a> { + fn phase(&self) -> Phase { + Phase::Permute + } + + fn run(&self, _rt: &mut Mapper, mut operations: Vec>) -> Vec> { + let mut picker = self.picker.borrow_mut(); + + descend(&mut operations, |operations| { + let atoms = collapse(mem::take(operations)); + + let live = atoms + .iter() + .flat_map(|atom| effects(atom).0) + .collect::>(); + let (atoms, parked) = decouple(atoms, &live); + let (successors, mut indegree) = dependencies(&atoms); + + let n = successors.len(); + let mut ready = (0..n).filter(|&i| indegree[i] == 0).collect::>(); + let mut order = Vec::with_capacity(n); + + while !ready.is_empty() { + let chosen = ready.swap_remove(picker(&ready)); + order.push(chosen); + for &next in &successors[chosen] { + indegree[next] -= 1; + if indegree[next] == 0 { + ready.push(next); + } } } - } - let mut permutated = schedule(atoms, &order); - cleanup(&mut permutated, &parked); - *operations = permutated; - }); + let mut permutated = schedule(atoms, &order); + cleanup(&mut permutated, &parked); + *operations = permutated; + }); - operations + operations + } } /// Removes adjacent [`StoreRegister`]/[`LoadRegister`] pairs created by incomplete scheduling. diff --git a/runtime/src/vm/transform/scramble.rs b/runtime/src/vm/transform/scramble.rs index 5e911006..ff4bb71f 100644 --- a/runtime/src/vm/transform/scramble.rs +++ b/runtime/src/vm/transform/scramble.rs @@ -3,19 +3,32 @@ use std::mem; use rand::seq::SliceRandom; use rand::Rng; +use crate::mapper::Mapper; use crate::vm::bytecode::VMWidth; use crate::vm::encoders::block::{Block, Jump, Target}; use crate::vm::encoders::jcc::Jcc; use crate::vm::encoders::label::Label; use crate::vm::encoders::load_immediate::LoadImmediate; use crate::vm::encoders::Encode; -use crate::vm::transform::atomize; +use crate::vm::transform::{atomize, Phase, Transform}; /// Shuffles the physical order of atoms by chaining them in execution order through signed-offset [`Jcc`]s. -pub fn scramble(mut operations: Vec>) -> Vec> { - walk(&mut operations, true); +pub struct Scramble; - operations +impl Transform for Scramble { + fn phase(&self) -> Phase { + Phase::Scramble + } + + fn run( + &self, + _mapper: &mut Mapper, + mut operations: Vec>, + ) -> Vec> { + walk(&mut operations, true); + + operations + } } /// Recurses into existing blocks and scrambles the current level. diff --git a/runtime/src/vm/utils/bytecode.rs b/runtime/src/vm/utils/bytecode.rs index 65a61dbd..fe7abd32 100644 --- a/runtime/src/vm/utils/bytecode.rs +++ b/runtime/src/vm/utils/bytecode.rs @@ -4,7 +4,7 @@ use iced_x86::code_asm::{ }; /// `mov {to}, [{base}]`; `inc {base}` -pub fn read_byte(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister8) { +pub fn read_byte(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister8) { // mov ..., [...] rt.asm.mov(to, ptr(base)).unwrap(); // inc ... @@ -12,7 +12,7 @@ pub fn read_byte(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister8) { } /// `movzx {to}, [{base}]`; `inc {base}` -pub fn read_byte_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { +pub fn read_byte_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { // movzx ..., [...] rt.asm.movzx(to, byte_ptr(base)).unwrap(); // inc ... @@ -20,7 +20,7 @@ pub fn read_byte_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { } /// `mov {to}, [{base}]`; `add {base}, 0x2` -pub fn read_word(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister16) { +pub fn read_word(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister16) { // mov ..., [...] rt.asm.mov(to, ptr(base)).unwrap(); // add ..., 0x2 @@ -28,7 +28,7 @@ pub fn read_word(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister16) { } /// `movzx {to}, [{base}]`; `add {base}, 0x2` -pub fn read_word_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { +pub fn read_word_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { // movzx ..., [...] rt.asm.movzx(to, word_ptr(base)).unwrap(); // add ..., 0x2 @@ -36,7 +36,7 @@ pub fn read_word_zx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { } /// `mov {to}, [{base}]`; `add {base}, 0x4` -pub fn read_dword(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { +pub fn read_dword(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { // mov ..., [...] rt.asm.mov(to, ptr(base)).unwrap(); // add ..., 0x4 @@ -44,7 +44,7 @@ pub fn read_dword(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister32) { } /// `movsxd {to}, [{base}]`; `add {base}, 0x4` -pub fn read_dword_sx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister64) { +pub fn read_dword_sx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister64) { // movsxd ..., [...] rt.asm.movsxd(to, ptr(base)).unwrap(); // add ..., 0x4 @@ -52,7 +52,7 @@ pub fn read_dword_sx(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister64) } /// `mov {to}, [{base}]`; `add {base}, 0x8` -pub fn read_qword(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister64) { +pub fn read_qword(rt: &mut Runtime, base: AsmRegister64, to: AsmRegister64) { // mov ..., [...] rt.asm.mov(to, ptr(base)).unwrap(); // add ..., 0x8 diff --git a/runtime/src/vm/utils/precision.rs b/runtime/src/vm/utils/precision.rs index c1f103c7..d411b08a 100644 --- a/runtime/src/vm/utils/precision.rs +++ b/runtime/src/vm/utils/precision.rs @@ -1,8 +1,9 @@ use iced_x86::code_asm::{AsmRegister64, CodeLabel}; -use crate::{runtime::Runtime, vm::bytecode::VMPrecision}; - -type Handler = Box; +use crate::{ + runtime::{Handler, Runtime}, + vm::bytecode::VMPrecision, +}; pub fn dispatch( rt: &mut Runtime, @@ -11,15 +12,12 @@ pub fn dispatch( mut int: Option, mut float: Option, ) { - let mut cases = Vec::new(); - let mut emits = Vec::new(); + let mut handlers = Vec::new(); macro_rules! case { ($opt:expr, $tag:expr) => { if let Some(f) = $opt.take() { - let label = rt.asm.create_label(); - cases.push((rt.mapper.index($tag) as u8, label)); - emits.push((label, f)); + handlers.push((vec![rt.mapper.index($tag) as u8], f)); } }; } @@ -27,12 +25,5 @@ pub fn dispatch( case!(int, VMPrecision::Integer); case!(float, VMPrecision::Float); - rt.jumps(precision, cases); - - for (mut label, f) in emits { - rt.asm.set_label(&mut label).unwrap(); - f(rt); - // jmp ... - rt.asm.jmp(*epilogue).unwrap(); - } + rt.switch(precision, *epilogue, handlers); } diff --git a/runtime/src/vm/utils/scratch.rs b/runtime/src/vm/utils/scratch.rs index c1ab3808..4a63d7c2 100644 --- a/runtime/src/vm/utils/scratch.rs +++ b/runtime/src/vm/utils/scratch.rs @@ -2,7 +2,7 @@ use crate::{ runtime::Runtime, vm::{bytecode::VMReg, utils::vreg}, }; -use iced_x86::code_asm::{ptr, r10, AsmRegister64, AsmRegisterXmm, AsmRegisterYmm}; +use iced_x86::code_asm::{ptr, r10, AsmRegister32, AsmRegister64, AsmRegisterXmm, AsmRegisterYmm}; pub fn store(rt: &mut Runtime, base: AsmRegister64, src: AsmRegister64) { // sub [...], 0x8 @@ -22,6 +22,15 @@ pub fn load(rt: &mut Runtime, base: AsmRegister64, dst: AsmRegister64) { rt.asm.mov(dst, ptr(r10 - 0x8)).unwrap(); } +pub fn load_32(rt: &mut Runtime, base: AsmRegister64, dst: AsmRegister32) { + // add [...], 0x8 + vreg::add_imm(rt, base, 0x8, VMReg::VScratch); + // mov r10, [...] + vreg::load_reg(rt, base, VMReg::VScratch, r10); + // mov ..., [r10 - 0x8] + rt.asm.mov(dst, ptr(r10 - 0x8)).unwrap(); +} + pub fn store_128(rt: &mut Runtime, base: AsmRegister64, src: AsmRegisterXmm) { // sub [...], 0x10 vreg::sub_imm(rt, base, 0x10, VMReg::VScratch); diff --git a/runtime/src/vm/utils/vvec.rs b/runtime/src/vm/utils/vvec.rs index 8671d615..1940a3be 100644 --- a/runtime/src/vm/utils/vvec.rs +++ b/runtime/src/vm/utils/vvec.rs @@ -1,25 +1,25 @@ use crate::{runtime::Runtime, vm::bytecode::VMVec}; use iced_x86::code_asm::{ptr, AsmRegister64, AsmRegisterXmm, AsmRegisterYmm}; -pub fn load_128(rt: &mut Runtime, base: AsmRegister64, src: VMVec, dst: AsmRegisterXmm) { +pub fn load_128(rt: &mut Runtime, base: AsmRegister64, src: VMVec, dst: AsmRegisterXmm) { rt.asm .movups(dst, ptr(base + rt.mapper.index(src) as i32 * 32)) .unwrap(); } -pub fn store_128(rt: &mut Runtime, base: AsmRegister64, src: AsmRegisterXmm, dst: VMVec) { +pub fn store_128(rt: &mut Runtime, base: AsmRegister64, src: AsmRegisterXmm, dst: VMVec) { rt.asm .movups(ptr(base + rt.mapper.index(dst) as i32 * 32), src) .unwrap(); } -pub fn load_256(rt: &mut Runtime, base: AsmRegister64, src: VMVec, dst: AsmRegisterYmm) { +pub fn load_256(rt: &mut Runtime, base: AsmRegister64, src: VMVec, dst: AsmRegisterYmm) { rt.asm .vmovups(dst, ptr(base + rt.mapper.index(src) as i32 * 32)) .unwrap(); } -pub fn store_256(rt: &mut Runtime, base: AsmRegister64, src: AsmRegisterYmm, dst: VMVec) { +pub fn store_256(rt: &mut Runtime, base: AsmRegister64, src: AsmRegisterYmm, dst: VMVec) { rt.asm .vmovups(ptr(base + rt.mapper.index(dst) as i32 * 32), src) .unwrap(); diff --git a/runtime/src/vm/utils/width.rs b/runtime/src/vm/utils/width.rs index 4116ca03..887b9a59 100644 --- a/runtime/src/vm/utils/width.rs +++ b/runtime/src/vm/utils/width.rs @@ -1,8 +1,9 @@ use iced_x86::code_asm::{AsmRegister64, CodeLabel}; -use crate::{runtime::Runtime, vm::bytecode::VMWidth}; - -type Handler = Box; +use crate::{ + runtime::{Handler, Runtime}, + vm::bytecode::VMWidth, +}; pub fn dispatch( rt: &mut Runtime, @@ -24,15 +25,12 @@ pub fn dispatch( let both8 = lower8.is_some() && higher8.is_some(); let merged8 = lower8.is_some() != higher8.is_some(); - let mut cases = Vec::new(); - let mut emits = Vec::new(); + let mut handlers = Vec::new(); macro_rules! case { ($opt:expr, $tag:expr) => { if let Some(f) = $opt.take() { - let label = rt.asm.create_label(); - cases.push((rt.mapper.index($tag) as u8, label)); - emits.push((label, f)); + handlers.push((vec![rt.mapper.index($tag) as u8], f)); } }; } @@ -41,10 +39,13 @@ pub fn dispatch( case!(lower8, VMWidth::Lower8); case!(higher8, VMWidth::Higher8); } else if merged8 { - let label = rt.asm.create_label(); - cases.push((rt.mapper.index(VMWidth::Lower8) as u8, label)); - cases.push((rt.mapper.index(VMWidth::Higher8) as u8, label)); - emits.push((label, lower8.take().or_else(|| higher8.take()).unwrap())); + handlers.push(( + vec![ + rt.mapper.index(VMWidth::Lower8) as u8, + rt.mapper.index(VMWidth::Higher8) as u8, + ], + lower8.take().or_else(|| higher8.take()).unwrap(), + )); } case!(lower16, VMWidth::Lower16); @@ -58,12 +59,5 @@ pub fn dispatch( case!(lower128, VMWidth::Lower128); case!(lower256, VMWidth::Lower256); - rt.jumps(width, cases); - - for (mut label, f) in emits { - rt.asm.set_label(&mut label).unwrap(); - f(rt); - // jmp ... - rt.asm.jmp(*epilogue).unwrap(); - } + rt.switch(width, *epilogue, handlers); } diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 774e2585..e62ae718 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -16,3 +16,4 @@ windows = { version = "0.62.2", features = [ "Win32_System_Memory", "Win32_System_Threading", ] } +paste = "1.0.15" diff --git a/tests/src/constants.rs b/tests/src/constants.rs index 94b8bbd0..836b01fd 100644 --- a/tests/src/constants.rs +++ b/tests/src/constants.rs @@ -1,11 +1,13 @@ +use iced_x86::{Register, UsedRegister}; use runtime::{ mapper::Mappable, + utils, vm::bytecode::{VMReg, VMVec}, }; use crate::State; -pub const REGISTERS: [VMReg; 17] = [ +pub const VIRTUAL_REGISTERS: [VMReg; 17] = [ VMReg::Rax, VMReg::Rcx, VMReg::Rdx, @@ -24,7 +26,8 @@ pub const REGISTERS: [VMReg; 17] = [ VMReg::R15, VMReg::Flags, ]; -pub const VECTORS: [VMVec; 16] = [ + +pub const VIRTUAL_VECTORS: [VMVec; 16] = [ VMVec::Ymm0, VMVec::Ymm1, VMVec::Ymm2, @@ -43,6 +46,26 @@ pub const VECTORS: [VMVec; 16] = [ VMVec::Ymm15, ]; +pub const NATIVE_REGISTERS: &[Register] = &[ + Register::RAX, + Register::RCX, + Register::RDX, + Register::RBX, + Register::RBP, + Register::RSI, + Register::RDI, + Register::R8, + Register::R9, + Register::R10, + Register::R11, + Register::R12, + Register::R13, + Register::R14, + Register::R15, +]; + +pub const IMMEDIATES: &[u64] = &[0, 0xFFFF_FFFF_FFFF_FFFF]; + pub fn baseline() -> State { let mut state = State::default(); @@ -79,40 +102,14 @@ pub fn vector(mut state: State, register: VMVec, bytes: [u128; 2]) -> State { state } -pub fn bytes16(value: u128) -> [u128; 2] { - let mut vector = [0u128; 2]; - vector[0] = value; - vector -} - -pub fn gpr() -> State { - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, IMM64_B) - .with(VMReg::Rdx, IMM64_C) +pub fn register(operand: usize, size: usize) -> Register { + utils::register_of_size(NATIVE_REGISTERS[operand % NATIVE_REGISTERS.len()], size).unwrap() } -pub fn simd() -> State { - let state = baseline(); - let state = vector(state, VMVec::Ymm0, bytes16(IMM128_A)); - let state = vector(state, VMVec::Ymm1, bytes16(IMM128_B)); - vector(state, VMVec::Ymm2, bytes16(IMM128_C)) +pub fn available(used: &[UsedRegister]) -> Register { + NATIVE_REGISTERS + .iter() + .copied() + .find(|candidate| !used.iter().any(|used| used.register() == *candidate)) + .unwrap() } - -pub const IMM8_A: u8 = 0x85; -pub const _IMM16_A: u16 = 0x8A5C; -pub const IMM32_A: u32 = 0x8A5C_E179; -pub const IMM64_A: u64 = 0x8A5C_E179_2D4B_7F67; -pub const IMM64_B: u64 = 0x5BD4_F28A_3E5C_6E78; -pub const IMM64_C: u64 = 0x4CE5_039B_4F6D_5D69; - -pub const SIMM8_A: i32 = 0x75; -pub const _SIMM16_A: i16 = 0x6A5C; -pub const SIMM32_A: i32 = 0x6A5C_E179; -pub const _SIMM64_A: i64 = 0x6A5C_E179_2D4B_7F67; - -pub const IMM128_A: u128 = 0x6A5C_E179_2D4B_7F67_1032_5476_789A_BCDE; -pub const IMM128_B: u128 = 0x5BD4_F28A_3E5C_6E78_2143_6587_89AB_CDEF; -pub const IMM128_C: u128 = 0x4CE5_039B_4F6D_5D69_3254_7698_9ABC_DEF0; - -pub const FAKE_BRANCH_ADDRESS: u64 = 0x1234_ABCD; diff --git a/tests/src/instructions.rs b/tests/src/instructions.rs index 41ba0acf..66b5f400 100644 --- a/tests/src/instructions.rs +++ b/tests/src/instructions.rs @@ -1,76 +1,821 @@ use std::slice; -use iced_x86::MemoryOperand; -use iced_x86::Register::{AL, CL, EAX, RAX, RBX, RCX, XMM0, XMM1, XMM2}; -use iced_x86::{Instruction, RflagsBits}; -use runtime::vm::bytecode::{self, Flag, VMReg}; - -use crate::constants::{ - baseline, gpr, simd, IMM128_B, IMM128_C, IMM32_A, IMM64_A, IMM64_B, IMM8_A, SIMM32_A, SIMM8_A, -}; -use crate::{ - decrypt_block, decrypt_payload, encrypt_block, instruction, Difference, Executor, State, +use iced_x86::{ + Code, Instruction, InstructionInfoFactory, OpAccess, OpCodeOperandKind, OpKind, Register, + RflagsBits, }; +use runtime::vm::bytecode::{self, Flag, VMReg, VMVec}; + +use crate::constants::{available, baseline, register, vector, IMMEDIATES}; +use crate::{decrypt_payload, encrypt_block, Difference, Executor, State}; + +macro_rules! define { + ($($code:ident)+ $(,)?) => { + $( + paste::paste! { + #[test] + fn []() { + test(Code::$code); + } + } + )+ + }; +} + +define!( +Adc_r16_rm16 +Adc_r32_rm32 +Adc_r64_rm64 +Adc_rm16_imm16 +Adc_rm16_imm8 +Adc_rm16_r16 +Adc_rm32_imm32 +Adc_rm32_imm8 +Adc_rm32_r32 +Adc_rm64_imm32 +Adc_rm64_imm8 +Adc_rm64_r64 +Adc_rm8_imm8 +Adc_rm8_r8 +Adc_r8_rm8 +Add_r16_rm16 +Add_r32_rm32 +Add_r64_rm64 +Add_rm16_imm16 +Add_rm16_imm8 +Add_rm16_r16 +Add_rm32_imm32 +Add_rm32_imm8 +Add_rm32_r32 +Add_rm64_imm32 +Add_rm64_imm8 +Add_rm64_r64 +Add_rm8_imm8 +Add_rm8_r8 +Add_r8_rm8 +Addpd_xmm_xmmm128 +Addps_xmm_xmmm128 +And_r16_rm16 +And_r32_rm32 +And_r64_rm64 +And_rm16_imm16 +And_rm16_imm8 +And_rm16_r16 +And_rm32_imm32 +And_rm32_imm8 +And_rm32_r32 +And_rm64_imm32 +And_rm64_imm8 +And_rm64_r64 +And_rm8_imm8 +And_rm8_r8 +And_r8_rm8 +Andnpd_xmm_xmmm128 +Andnps_xmm_xmmm128 +Andpd_xmm_xmmm128 +Andps_xmm_xmmm128 +Bsr_r16_rm16 +Bsr_r32_rm32 +Bsr_r64_rm64 +Bswap_r32 +Bswap_r64 +Bt_rm16_imm8 +Bt_rm16_r16 +Bt_rm32_imm8 +Bt_rm32_r32 +Bt_rm64_imm8 +Bt_rm64_r64 +Btc_rm16_imm8 +Btc_rm16_r16 +Btc_rm32_imm8 +Btc_rm32_r32 +Btc_rm64_imm8 +Btc_rm64_r64 +Btr_rm16_imm8 +Btr_rm16_r16 +Btr_rm32_imm8 +Btr_rm32_r32 +Btr_rm64_imm8 +Btr_rm64_r64 +Bts_rm16_imm8 +Bts_rm16_r16 +Bts_rm32_imm8 +Bts_rm32_r32 +Bts_rm64_imm8 +Bts_rm64_r64 +Cmp_r16_rm16 +Cmp_r32_rm32 +Cmp_r64_rm64 +Cmp_rm16_imm16 +Cmp_rm16_imm8 +Cmp_rm16_r16 +Cmp_rm32_imm32 +Cmp_rm32_imm8 +Cmp_rm32_r32 +Cmp_rm64_imm32 +Cmp_rm64_imm8 +Cmp_rm64_r64 +Cmp_rm8_imm8 +Cmp_rm8_r8 +Cmp_r8_rm8 +Cmpxchg_rm16_r16 +Cmpxchg_rm32_r32 +Cmpxchg_rm64_r64 +Cmpxchg_rm8_r8 +Cmova_r16_rm16 +Cmova_r32_rm32 +Cmova_r64_rm64 +Cmovae_r16_rm16 +Cmovae_r32_rm32 +Cmovae_r64_rm64 +Cmovb_r16_rm16 +Cmovb_r32_rm32 +Cmovb_r64_rm64 +Cmovbe_r16_rm16 +Cmovbe_r32_rm32 +Cmovbe_r64_rm64 +Cmove_r16_rm16 +Cmove_r32_rm32 +Cmove_r64_rm64 +Cmovg_r16_rm16 +Cmovg_r32_rm32 +Cmovg_r64_rm64 +Cmovge_r16_rm16 +Cmovge_r32_rm32 +Cmovge_r64_rm64 +Cmovl_r16_rm16 +Cmovl_r32_rm32 +Cmovl_r64_rm64 +Cmovle_r16_rm16 +Cmovle_r32_rm32 +Cmovle_r64_rm64 +Cmovne_r16_rm16 +Cmovne_r32_rm32 +Cmovne_r64_rm64 +Cmovno_r16_rm16 +Cmovno_r32_rm32 +Cmovno_r64_rm64 +Cmovnp_r16_rm16 +Cmovnp_r32_rm32 +Cmovnp_r64_rm64 +Cmovns_r16_rm16 +Cmovns_r32_rm32 +Cmovns_r64_rm64 +Cmovo_r16_rm16 +Cmovo_r32_rm32 +Cmovo_r64_rm64 +Cmovp_r16_rm16 +Cmovp_r32_rm32 +Cmovp_r64_rm64 +Cmovs_r16_rm16 +Cmovs_r32_rm32 +Cmovs_r64_rm64 +Dec_rm16 +Dec_rm32 +Dec_rm64 +Dec_rm8 +Div_rm16 +Div_rm32 +Div_rm64 +Div_rm8 +Divpd_xmm_xmmm128 +Divps_xmm_xmmm128 +Idiv_rm16 +Idiv_rm32 +Idiv_rm64 +Idiv_rm8 +Imul_r16_rm16 +Imul_r16_rm16_imm16 +Imul_r16_rm16_imm8 +Imul_r32_rm32 +Imul_r32_rm32_imm32 +Imul_r32_rm32_imm8 +Imul_r64_rm64 +Imul_r64_rm64_imm32 +Imul_r64_rm64_imm8 +Imul_rm16 +Imul_rm32 +Imul_rm64 +Imul_rm8 +Inc_rm16 +Inc_rm32 +Inc_rm64 +Inc_rm8 +Mov_r16_imm16 +Mov_r16_rm16 +Mov_r32_imm32 +Mov_r32_rm32 +Mov_r64_imm64 +Mov_r64_rm64 +Mov_r8_imm8 +Mov_r8_rm8 +Mov_rm16_imm16 +Mov_rm16_r16 +Mov_rm32_imm32 +Mov_rm32_r32 +Mov_rm64_imm32 +Mov_rm64_r64 +Mov_rm8_imm8 +Mov_rm8_r8 +Movapd_xmm_xmmm128 +Movapd_xmmm128_xmm +Movaps_xmm_xmmm128 +Movaps_xmmm128_xmm +Movd_rm32_xmm +Movd_xmm_rm32 +Movdqa_xmm_xmmm128 +Movdqa_xmmm128_xmm +Movdqu_xmm_xmmm128 +Movdqu_xmmm128_xmm +Movq_rm64_xmm +Movq_xmm_rm64 +Movsd_xmm_xmmm64 +Movsd_xmmm64_xmm +Movss_xmm_xmmm32 +Movss_xmmm32_xmm +Movsx_r16_rm16 +Movsx_r16_rm8 +Movsx_r32_rm16 +Movsx_r32_rm8 +Movsx_r64_rm8 +Movsxd_r64_rm32 +Movupd_xmm_xmmm128 +Movupd_xmmm128_xmm +Movups_xmm_xmmm128 +Movups_xmmm128_xmm +Movzx_r16_rm16 +Movzx_r16_rm8 +Movzx_r32_rm16 +Movzx_r32_rm8 +Movzx_r64_rm8 +Mulpd_xmm_xmmm128 +Mulps_xmm_xmmm128 +Mul_rm16 +Mul_rm32 +Mul_rm64 +Mul_rm8 +Neg_rm16 +Neg_rm32 +Neg_rm64 +Neg_rm8 +Not_rm16 +Not_rm32 +Not_rm64 +Not_rm8 +Or_r16_rm16 +Or_r32_rm32 +Or_r64_rm64 +Or_rm16_imm16 +Or_rm16_imm8 +Or_rm16_r16 +Or_rm32_imm32 +Or_rm32_imm8 +Or_rm32_r32 +Or_rm64_imm32 +Or_rm64_imm8 +Or_rm64_r64 +Or_rm8_imm8 +Or_rm8_r8 +Or_r8_rm8 +Orpd_xmm_xmmm128 +Orps_xmm_xmmm128 +Paddb_xmm_xmmm128 +Paddd_xmm_xmmm128 +Paddq_xmm_xmmm128 +Paddw_xmm_xmmm128 +Pand_xmm_xmmm128 +Pandn_xmm_xmmm128 +Pcmpeqb_xmm_xmmm128 +Pmovmskb_r64_xmm +Pmulld_xmm_xmmm128 +Pmullw_xmm_xmmm128 +Por_xmm_xmmm128 +Psubb_xmm_xmmm128 +Psubd_xmm_xmmm128 +Psubq_xmm_xmmm128 +Psubw_xmm_xmmm128 +Pxor_xmm_xmmm128 +Rol_rm16_CL +Rol_rm16_imm8 +Rol_rm32_CL +Rol_rm32_imm8 +Rol_rm64_CL +Rol_rm64_imm8 +Rol_rm8_CL +Rol_rm8_imm8 +Ror_rm16_CL +Ror_rm16_imm8 +Ror_rm32_CL +Ror_rm32_imm8 +Ror_rm64_CL +Ror_rm64_imm8 +Ror_rm8_CL +Ror_rm8_imm8 +Sar_rm16_CL +Sar_rm16_imm8 +Sar_rm32_CL +Sar_rm32_imm8 +Sar_rm64_CL +Sar_rm64_imm8 +Sar_rm8_CL +Sar_rm8_imm8 +Sbb_r16_rm16 +Sbb_r32_rm32 +Sbb_r64_rm64 +Sbb_rm16_imm16 +Sbb_rm16_imm8 +Sbb_rm16_r16 +Sbb_rm32_imm32 +Sbb_rm32_imm8 +Sbb_rm32_r32 +Sbb_rm64_imm32 +Sbb_rm64_imm8 +Sbb_rm64_r64 +Sbb_rm8_imm8 +Sbb_rm8_r8 +Sbb_r8_rm8 +Seta_rm8 +Setae_rm8 +Setb_rm8 +Setbe_rm8 +Sete_rm8 +Setg_rm8 +Setge_rm8 +Setl_rm8 +Setle_rm8 +Setne_rm8 +Setno_rm8 +Setnp_rm8 +Setns_rm8 +Seto_rm8 +Setp_rm8 +Sets_rm8 +Shl_rm16_CL +Shl_rm16_imm8 +Shl_rm32_CL +Shl_rm32_imm8 +Shl_rm64_CL +Shl_rm64_imm8 +Shl_rm8_CL +Shl_rm8_imm8 +Shr_rm16_CL +Shr_rm16_imm8 +Shr_rm32_CL +Shr_rm32_imm8 +Shr_rm64_CL +Shr_rm64_imm8 +Shr_rm8_CL +Shr_rm8_imm8 +Sub_r16_rm16 +Sub_r32_rm32 +Sub_r64_rm64 +Sub_rm16_imm16 +Sub_rm16_imm8 +Sub_rm16_r16 +Sub_rm32_imm32 +Sub_rm32_imm8 +Sub_rm32_r32 +Sub_rm64_imm32 +Sub_rm64_imm8 +Sub_rm64_r64 +Sub_rm8_imm8 +Sub_rm8_r8 +Sub_r8_rm8 +Subpd_xmm_xmmm128 +Subps_xmm_xmmm128 +Test_rm16_imm16 +Test_rm16_r16 +Test_rm32_imm32 +Test_rm32_r32 +Test_rm64_imm32 +Test_rm64_r64 +Test_rm8_imm8 +Test_rm8_r8 +Tzcnt_r16_rm16 +Tzcnt_r32_rm32 +Tzcnt_r64_rm64 +VEX_Vaddpd_xmm_xmm_xmmm128 +VEX_Vaddps_xmm_xmm_xmmm128 +VEX_Vandps_xmm_xmm_xmmm128 +VEX_Vdivpd_xmm_xmm_xmmm128 +VEX_Vdivps_xmm_xmm_xmmm128 +VEX_Vmulpd_xmm_xmm_xmmm128 +VEX_Vmulps_xmm_xmm_xmmm128 +VEX_Vpmulhw_xmm_xmm_xmmm128 +VEX_Vpmulld_xmm_xmm_xmmm128 +VEX_Vpmullw_xmm_xmm_xmmm128 +VEX_Vpxor_xmm_xmm_xmmm128 +VEX_Vsubpd_xmm_xmm_xmmm128 +VEX_Vsubps_xmm_xmm_xmmm128 +VEX_Vxorps_xmm_xmm_xmmm128 +Xadd_rm16_r16 +Xadd_rm32_r32 +Xadd_rm64_r64 +Xadd_rm8_r8 +Xchg_rm16_r16 +Xchg_rm32_r32 +Xchg_rm64_r64 +Xchg_rm8_r8 +Xor_r16_rm16 +Xor_r32_rm32 +Xor_r64_rm64 +Xor_rm16_imm16 +Xor_rm16_imm8 +Xor_rm16_r16 +Xor_rm32_imm32 +Xor_rm32_imm8 +Xor_rm32_r32 +Xor_rm64_imm32 +Xor_rm64_imm8 +Xor_rm64_r64 +Xor_rm8_imm8 +Xor_rm8_r8 +Xor_r8_rm8 +Xorpd_xmm_xmmm128 +Xorps_xmm_xmmm128 +); + +fn test(code: Code) { + for &immediate in IMMEDIATES { + let instruction = build(code, Test::Registers, Register::None, immediate); + + case(instruction, None, immediate); + + let mut factory = InstructionInfoFactory::new(); + let info = factory.info(&instruction); + + if !info.used_memory().is_empty() { + let base = available(info.used_registers()); + let instruction = build(code, Test::Memory, base, immediate); + let size = instruction.memory_size().size() as usize; + + let words = size.div_ceil(16); + + let mut backing = vec![(immediate as u128) | ((immediate as u128) << 64); words.max(1)]; + + let memory = unsafe { + slice::from_raw_parts_mut(backing.as_mut_ptr() as *mut u8, backing.len() * 16) + }; + + case(instruction, Some(&mut memory[..size]), immediate); + } + } +} + +#[derive(Clone, Copy)] +enum Test { + Registers, + Memory, +} + +fn build(code: Code, test: Test, memory_base: Register, immediate: u64) -> Instruction { + let info = code.op_code(); + let kinds = [ + info.op0_kind(), + info.op1_kind(), + info.op2_kind(), + info.op3_kind(), + info.op4_kind(), + ]; + + let memory_operand = if matches!(test, Test::Memory) { + kinds.iter().position(|kind| memory(*kind)) + } else { + None + }; + + let mut instruction = Instruction::default(); + instruction.set_code(code); + + for index in 0..5 { + let kind = kinds[index]; + + if kind == OpCodeOperandKind::None { + continue; + } + + if Some(index) == memory_operand { + instruction.set_op_kind(index as u32, OpKind::Memory); + instruction.set_memory_base(memory_base); + instruction.set_memory_index(Register::None); + instruction.set_memory_index_scale(1); + instruction.set_memory_displacement64(0); + instruction.set_memory_displ_size(0); + continue; + } + + operand(&mut instruction, index as u32, kind, immediate); + } + + instruction +} -#[test] -fn test_crypt() { - let mut buffer = vec![0xDE, 0xAD, 0xC0, 0xDE]; +fn operand(instruction: &mut Instruction, operand: u32, kind: OpCodeOperandKind, immediate: u64) { + let op = match kind { + OpCodeOperandKind::r8_or_mem + | OpCodeOperandKind::r16_or_mem + | OpCodeOperandKind::r32_or_mem + | OpCodeOperandKind::r32_or_mem_mpx + | OpCodeOperandKind::r64_or_mem + | OpCodeOperandKind::r64_or_mem_mpx + | OpCodeOperandKind::mm_or_mem + | OpCodeOperandKind::xmm_or_mem + | OpCodeOperandKind::ymm_or_mem + | OpCodeOperandKind::zmm_or_mem + | OpCodeOperandKind::bnd_or_mem_mpx + | OpCodeOperandKind::k_or_mem + | OpCodeOperandKind::r8_reg + | OpCodeOperandKind::r8_opcode + | OpCodeOperandKind::r16_reg + | OpCodeOperandKind::r16_reg_mem + | OpCodeOperandKind::r16_rm + | OpCodeOperandKind::r16_opcode + | OpCodeOperandKind::r32_reg + | OpCodeOperandKind::r32_reg_mem + | OpCodeOperandKind::r32_rm + | OpCodeOperandKind::r32_opcode + | OpCodeOperandKind::r32_vvvv + | OpCodeOperandKind::r64_reg + | OpCodeOperandKind::r64_reg_mem + | OpCodeOperandKind::r64_rm + | OpCodeOperandKind::r64_opcode + | OpCodeOperandKind::r64_vvvv + | OpCodeOperandKind::seg_reg + | OpCodeOperandKind::k_reg + | OpCodeOperandKind::kp1_reg + | OpCodeOperandKind::k_rm + | OpCodeOperandKind::k_vvvv + | OpCodeOperandKind::mm_reg + | OpCodeOperandKind::mm_rm + | OpCodeOperandKind::xmm_reg + | OpCodeOperandKind::xmm_rm + | OpCodeOperandKind::xmm_vvvv + | OpCodeOperandKind::xmmp3_vvvv + | OpCodeOperandKind::xmm_is4 + | OpCodeOperandKind::xmm_is5 + | OpCodeOperandKind::ymm_reg + | OpCodeOperandKind::ymm_rm + | OpCodeOperandKind::ymm_vvvv + | OpCodeOperandKind::ymm_is4 + | OpCodeOperandKind::ymm_is5 + | OpCodeOperandKind::zmm_reg + | OpCodeOperandKind::zmm_rm + | OpCodeOperandKind::zmm_vvvv + | OpCodeOperandKind::zmmp3_vvvv + | OpCodeOperandKind::cr_reg + | OpCodeOperandKind::dr_reg + | OpCodeOperandKind::tr_reg + | OpCodeOperandKind::bnd_reg + | OpCodeOperandKind::es + | OpCodeOperandKind::cs + | OpCodeOperandKind::ss + | OpCodeOperandKind::ds + | OpCodeOperandKind::fs + | OpCodeOperandKind::gs + | OpCodeOperandKind::al + | OpCodeOperandKind::cl + | OpCodeOperandKind::ax + | OpCodeOperandKind::dx + | OpCodeOperandKind::eax + | OpCodeOperandKind::rax + | OpCodeOperandKind::st0 + | OpCodeOperandKind::sti_opcode + | OpCodeOperandKind::tmm_reg + | OpCodeOperandKind::tmm_rm + | OpCodeOperandKind::tmm_vvvv => OpKind::Register, + OpCodeOperandKind::imm4_m2z | OpCodeOperandKind::imm8 | OpCodeOperandKind::imm8_const_1 => { + OpKind::Immediate8 + } + OpCodeOperandKind::imm8sex16 => OpKind::Immediate8to16, + OpCodeOperandKind::imm8sex32 => OpKind::Immediate8to32, + OpCodeOperandKind::imm8sex64 => OpKind::Immediate8to64, + OpCodeOperandKind::imm16 => OpKind::Immediate16, + OpCodeOperandKind::imm32 => OpKind::Immediate32, + OpCodeOperandKind::imm32sex64 => OpKind::Immediate32to64, + OpCodeOperandKind::imm64 => OpKind::Immediate64, + _ => panic!("unsupported operand kind: {kind:?}"), + }; + + instruction.set_op_kind(operand, op); + + if op == OpKind::Register { + instruction.set_op_register(operand, register_for(kind, operand as usize)); + } else { + let value = match kind { + OpCodeOperandKind::imm4_m2z => 0, + OpCodeOperandKind::imm8_const_1 => 1, + OpCodeOperandKind::imm8 + | OpCodeOperandKind::imm8sex16 + | OpCodeOperandKind::imm8sex32 + | OpCodeOperandKind::imm8sex64 => immediate as u8 as u64, + OpCodeOperandKind::imm16 => immediate as u16 as u64, + OpCodeOperandKind::imm32 | OpCodeOperandKind::imm32sex64 => immediate as u32 as u64, + OpCodeOperandKind::imm64 => immediate, + _ => unreachable!(), + }; + + instruction.set_immediate_u64(operand, value); + } +} + +fn register_for(kind: OpCodeOperandKind, operand: usize) -> Register { + match kind { + OpCodeOperandKind::al => Register::AL, + OpCodeOperandKind::cl => Register::CL, + OpCodeOperandKind::ax => Register::AX, + OpCodeOperandKind::dx => Register::DX, + OpCodeOperandKind::eax => Register::EAX, + OpCodeOperandKind::rax => Register::RAX, + OpCodeOperandKind::r8_reg | OpCodeOperandKind::r8_opcode | OpCodeOperandKind::r8_or_mem => { + register(operand, 1) + } + OpCodeOperandKind::r16_reg + | OpCodeOperandKind::r16_opcode + | OpCodeOperandKind::r16_reg_mem + | OpCodeOperandKind::r16_rm + | OpCodeOperandKind::r16_or_mem => register(operand, 2), + OpCodeOperandKind::r32_reg + | OpCodeOperandKind::r32_opcode + | OpCodeOperandKind::r32_vvvv + | OpCodeOperandKind::r32_reg_mem + | OpCodeOperandKind::r32_rm + | OpCodeOperandKind::r32_or_mem + | OpCodeOperandKind::r32_or_mem_mpx => register(operand, 4), + OpCodeOperandKind::r64_reg + | OpCodeOperandKind::r64_opcode + | OpCodeOperandKind::r64_vvvv + | OpCodeOperandKind::r64_reg_mem + | OpCodeOperandKind::r64_rm + | OpCodeOperandKind::r64_or_mem + | OpCodeOperandKind::r64_or_mem_mpx => register(operand, 8), + OpCodeOperandKind::xmm_reg + | OpCodeOperandKind::xmm_rm + | OpCodeOperandKind::xmm_vvvv + | OpCodeOperandKind::xmmp3_vvvv + | OpCodeOperandKind::xmm_is4 + | OpCodeOperandKind::xmm_is5 + | OpCodeOperandKind::xmm_or_mem => Register::XMM0 + ((operand % 16) as u32), + OpCodeOperandKind::ymm_reg + | OpCodeOperandKind::ymm_rm + | OpCodeOperandKind::ymm_vvvv + | OpCodeOperandKind::ymm_is4 + | OpCodeOperandKind::ymm_is5 + | OpCodeOperandKind::ymm_or_mem => Register::YMM0 + ((operand % 16) as u32), + OpCodeOperandKind::zmm_reg + | OpCodeOperandKind::zmm_rm + | OpCodeOperandKind::zmm_vvvv + | OpCodeOperandKind::zmmp3_vvvv + | OpCodeOperandKind::zmm_or_mem => Register::ZMM0 + ((operand % 16) as u32), + _ => panic!("unsupported operand kind: {kind:?}"), + } +} - let before = buffer.clone(); +fn memory(kind: OpCodeOperandKind) -> bool { + matches!( + kind, + OpCodeOperandKind::mem + | OpCodeOperandKind::mem_mpx + | OpCodeOperandKind::mem_mib + | OpCodeOperandKind::mem_vsib32x + | OpCodeOperandKind::mem_vsib64x + | OpCodeOperandKind::mem_vsib32y + | OpCodeOperandKind::mem_vsib64y + | OpCodeOperandKind::mem_vsib32z + | OpCodeOperandKind::mem_vsib64z + | OpCodeOperandKind::r8_or_mem + | OpCodeOperandKind::r16_or_mem + | OpCodeOperandKind::r32_or_mem + | OpCodeOperandKind::r32_or_mem_mpx + | OpCodeOperandKind::r64_or_mem + | OpCodeOperandKind::r64_or_mem_mpx + | OpCodeOperandKind::mm_or_mem + | OpCodeOperandKind::xmm_or_mem + | OpCodeOperandKind::ymm_or_mem + | OpCodeOperandKind::zmm_or_mem + | OpCodeOperandKind::bnd_or_mem_mpx + | OpCodeOperandKind::k_or_mem + | OpCodeOperandKind::r16_reg_mem + | OpCodeOperandKind::r32_reg_mem + | OpCodeOperandKind::r64_reg_mem + ) +} - encrypt_block(&mut buffer); +fn case(instruction: Instruction, memory: Option<&mut [u8]>, immediate: u64) { + let mut factory = InstructionInfoFactory::new(); + let info = factory.info(&instruction); - decrypt_block(&mut buffer); + let mut state = baseline(); - let after = buffer.clone(); + for used in info.used_registers() { + let register = used.register(); - assert_eq!(before, after); + if register.is_gpr() { + state = state.with(VMReg::from(register.full_register()), immediate); + } + } + + for index in 0..instruction.op_count() { + let register = instruction.op_register(index); + + if register.is_vector_register() { + let immediate = (immediate as u128) | ((immediate as u128) << 64); + state = vector(state, VMVec::from(register), [immediate, immediate]); + } + } + + let has_rax = info + .used_registers() + .iter() + .any(|used| used.register().full_register() == Register::RAX); + + let has_rdx = info + .used_registers() + .iter() + .any(|used| used.register().full_register() == Register::RDX); + + let reads_rdx = info.used_registers().iter().any(|used| { + used.register().full_register() == Register::RDX + && matches!( + used.access(), + OpAccess::Read | OpAccess::ReadWrite | OpAccess::ReadCondWrite | OpAccess::CondRead + ) + }); + + if has_rax && has_rdx && reads_rdx { + state = state.with(VMReg::from(Register::RDX), 0); + } + + if instruction.rflags_read() != RflagsBits::NONE + || instruction.rflags_written() != RflagsBits::NONE + { + state = state.with( + VMReg::Flags, + Flag::Carry.bit64() + | Flag::Parity.bit64() + | Flag::Auxiliary.bit64() + | Flag::Zero.bit64() + | Flag::Sign.bit64() + | Flag::Overflow.bit64(), + ); + } + + if let Some(memory) = memory { + state = state.with( + VMReg::from(instruction.memory_base()), + memory.as_mut_ptr() as u64, + ); + compare_memory(state, &[instruction], memory); + } else { + compare(state, &[instruction]); + } } -fn check(state: State, instruction: Instruction) { - check_with_memory(state, instruction, &mut []); +fn compare(state: State, instructions: &[Instruction]) { + compare_memory(state, instructions, &mut []); } -fn check_with_memory(state: State, instruction: Instruction, memory: &mut [u8]) { +fn compare_memory(state: State, instructions: &[Instruction], memory: &mut [u8]) { let baseline = memory.to_vec(); let mut executor = Executor::new(); - let mut native = executor.run_native(state.clone(), &[instruction]); + let mut native = executor.run_native(state.clone(), instructions); memory.copy_from_slice(&baseline); let mut executor = Executor::new(); - let lifted = bytecode::lift(&[instruction]) - .unwrap_or_else(|| panic!("{instruction} is not implemented")); - let transformed = bytecode::transform(&mut executor.rt.mapper, lifted, |_| 0); + let lifted = bytecode::lift(instructions).unwrap(); + let transformed = bytecode::transform(&mut executor.rt.mapper, lifted, 0, |_| 0); let mut bytes = bytecode::assemble(&mut executor.rt.mapper, &transformed); encrypt_block(&mut bytes); decrypt_payload(&mut bytes); - let mut emulated = executor.run_virtual(state.clone(), &bytes); + let mut emulated = executor.run_virtual(state, &bytes); - normalize_and_compare(&mut native, &mut emulated, instruction); + normalize(&mut native, &mut emulated, instructions); } -fn normalize_and_compare(native: &mut State, emulated: &mut State, instruction: Instruction) { - let normalize = |state: &mut State| { - state.registers.remove(&VMReg::Rsp); +fn normalize(native: &mut State, emulated: &mut State, instructions: &[Instruction]) { + native.registers.remove(&VMReg::Rsp); + emulated.registers.remove(&VMReg::Rsp); - if let Some(flags) = state.registers.get_mut(&VMReg::Flags) { - let mask = instruction.rflags_written() + let mask = instructions + .iter() + .fold(RflagsBits::NONE, |mask, instruction| { + mask | instruction.rflags_written() | instruction.rflags_cleared() - | instruction.rflags_set(); - *flags &= ((mask & RflagsBits::CF != 0) as u64 * Flag::Carry.bit64()) - | ((mask & RflagsBits::PF != 0) as u64 * Flag::Parity.bit64()) - | ((mask & RflagsBits::AF != 0) as u64 * Flag::Auxiliary.bit64()) - | ((mask & RflagsBits::ZF != 0) as u64 * Flag::Zero.bit64()) - | ((mask & RflagsBits::SF != 0) as u64 * Flag::Sign.bit64()) - | ((mask & RflagsBits::OF != 0) as u64 * Flag::Overflow.bit64()); - } - }; - - normalize(native); - normalize(emulated); + | instruction.rflags_set() + }); + + for state in [&mut *native, &mut *emulated] { + let flags = state.registers.get_mut(&VMReg::Flags).unwrap(); + + *flags &= ((mask & RflagsBits::CF != 0) as u64 * Flag::Carry.bit64()) + | ((mask & RflagsBits::PF != 0) as u64 * Flag::Parity.bit64()) + | ((mask & RflagsBits::AF != 0) as u64 * Flag::Auxiliary.bit64()) + | ((mask & RflagsBits::ZF != 0) as u64 * Flag::Zero.bit64()) + | ((mask & RflagsBits::SF != 0) as u64 * Flag::Sign.bit64()) + | ((mask & RflagsBits::OF != 0) as u64 * Flag::Overflow.bit64()); + } let differences = native.compare(emulated); @@ -92,1451 +837,11 @@ fn dump(differences: &[Difference]) -> String { "{register:?}: native={native:02X?} virtual={emulated:02X?}" )); } + Difference::Exception(native, emulated) => { + lines.push(format!("Exception: native={native:?} virtual={emulated:?}")); + } } } lines.join("\n") } - -macro_rules! testing { - ($name:ident, $state:expr, $instruction:expr) => { - #[test] - fn $name() { - check($state, $instruction); - } - }; -} - -macro_rules! testing_memory { - ($name:ident, mut $buf:ident = $init:expr, $state:expr, $instruction:expr) => { - #[test] - fn $name() { - let mut $buf = $init; - check_with_memory($state, $instruction, unsafe { - slice::from_raw_parts_mut($buf.as_mut_ptr() as *mut u8, 8) - }); - } - }; - ($name:ident, $buf:ident = $init:expr, $state:expr, $instruction:expr) => { - #[test] - fn $name() { - let $buf = $init; - check_with_memory($state, $instruction, unsafe { - slice::from_raw_parts_mut($buf.as_ptr() as *mut u8, 8) - }); - } - }; -} - -macro_rules! testing_set { - ($name:ident, $ins_variant:ident) => { - #[test] - fn $name() { - let mut buf = [0u8]; - check_with_memory( - baseline().with(VMReg::Rax, buf.as_mut_ptr() as u64), - instruction!($ins_variant, MemoryOperand::with_base(RAX)), - &mut buf, - ); - } - }; -} - -macro_rules! testing_simd_load { - ($name:ident, $init:expr, $instruction:expr) => { - #[test] - fn $name() { - let buf = $init; - check_with_memory( - simd().with(VMReg::Rax, buf.as_ptr() as u64), - $instruction, - unsafe { slice::from_raw_parts_mut(buf.as_ptr() as *mut u8, 32) }, - ); - } - }; -} - -macro_rules! testing_simd_store { - ($name:ident, $instruction:expr) => { - #[test] - fn $name() { - let mut buf = [0u128, 0u128]; - check_with_memory( - simd().with(VMReg::Rax, buf.as_mut_ptr() as u64), - $instruction, - unsafe { slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, 32) }, - ); - } - }; -} - -testing!(test_mov, gpr(), instruction!(Mov_r64_rm64, RAX, RCX)); -testing_memory!( - test_mov_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Mov_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_mov_store, - mut buf = [0u64], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Mov_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_mov_imm64, - gpr(), - instruction!(Mov_r64_imm64, RAX, IMM64_A as i64) -); -testing!( - test_mov_imm32, - gpr(), - instruction!(Mov_rm64_imm32, RAX, SIMM32_A) -); - -testing!(test_movzx, gpr(), instruction!(Movzx_r64_rm8, RAX, CL)); -testing_memory!( - test_movzx_load, - buf = [IMM8_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Movzx_r64_rm8, RCX, MemoryOperand::with_base(RAX)) -); - -testing!(test_movzsx, gpr(), instruction!(Movsx_r64_rm8, RAX, CL)); -testing_memory!( - test_movsx_load, - buf = [IMM8_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Movsx_r64_rm8, RCX, MemoryOperand::with_base(RAX)) -); - -testing!(test_movzsxd, gpr(), instruction!(Movsxd_r64_rm32, RAX, EAX)); -testing_memory!( - test_movsxd_load, - buf = [IMM32_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Movsxd_r64_rm32, RCX, MemoryOperand::with_base(RAX)) -); - -testing!(test_add, gpr(), instruction!(Add_rm64_r64, RAX, RCX)); -testing_memory!( - test_add_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Add_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_add_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Add_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_add_imm32, - gpr(), - instruction!(Add_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_add_imm8, - gpr(), - instruction!(Add_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_sub, gpr(), instruction!(Sub_rm64_r64, RAX, RCX)); -testing_memory!( - test_sub_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Sub_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_sub_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Sub_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_sub_imm32, - gpr(), - instruction!(Sub_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_sub_imm8, - gpr(), - instruction!(Sub_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_adc, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Adc_rm64_r64, RAX, RCX) -); -testing_memory!( - test_adc_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Flags, 0b0000000000000001u64) - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Adc_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_adc_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Flags, 0b0000000000000001u64) - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Adc_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_adc_imm32, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Adc_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_adc_imm8, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Adc_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_sbb, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Sbb_rm64_r64, RAX, RCX) -); -testing_memory!( - test_sbb_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Flags, 0b0000000000000001u64) - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Sbb_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_sbb_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Flags, 0b0000000000000001u64) - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Sbb_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_sbb_imm32, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Sbb_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_sbb_imm8, - gpr().with(VMReg::Flags, 0b0000000000000001u64), - instruction!(Sbb_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_cmp, gpr(), instruction!(Cmp_rm64_r64, RAX, RCX)); -testing_memory!( - test_cmp_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Cmp_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_cmp_store, - buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Cmp_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_cmp_imm32, - gpr(), - instruction!(Cmp_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_cmp_imm8, - gpr(), - instruction!(Cmp_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_test, gpr(), instruction!(Test_rm64_r64, RAX, RCX)); -testing_memory!( - test_test_store, - buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Test_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_test_imm32, - gpr(), - instruction!(Test_rm64_imm32, RAX, SIMM32_A) -); - -testing!(test_and, gpr(), instruction!(And_rm64_r64, RAX, RCX)); -testing_memory!( - test_and_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(And_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_and_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(And_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_and_imm32, - gpr(), - instruction!(And_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_and_imm8, - gpr(), - instruction!(And_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_or, gpr(), instruction!(Or_rm64_r64, RAX, RCX)); -testing_memory!( - test_or_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Or_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_or_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Or_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_or_imm32, - gpr(), - instruction!(Or_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_or_imm8, - gpr(), - instruction!(Or_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_xor, gpr(), instruction!(Xor_rm64_r64, RAX, RCX)); -testing_memory!( - test_xor_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_A), - instruction!(Xor_r64_rm64, RCX, MemoryOperand::with_base(RAX)) -); -testing_memory!( - test_xor_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Xor_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_xor_imm32, - gpr(), - instruction!(Xor_rm64_imm32, RAX, SIMM32_A) -); -testing!( - test_xor_imm8, - gpr(), - instruction!(Xor_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_rol, - gpr().with(VMReg::Rcx, IMM8_A), - instruction!(Rol_rm64_CL, RAX, CL) -); -testing_memory!( - test_rol_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM8_A), - instruction!(Rol_rm64_CL, MemoryOperand::with_base(RAX), CL) -); -testing!( - test_rol_imm8, - gpr(), - instruction!(Rol_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_ror, - gpr().with(VMReg::Rcx, IMM8_A), - instruction!(Ror_rm64_CL, RAX, CL) -); -testing_memory!( - test_ror_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM8_A), - instruction!(Ror_rm64_CL, MemoryOperand::with_base(RAX), CL) -); -testing!( - test_ror_imm8, - gpr(), - instruction!(Ror_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_shl, - gpr().with(VMReg::Rcx, IMM8_A), - instruction!(Shl_rm64_CL, RAX, CL) -); -testing_memory!( - test_shl_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM8_A), - instruction!(Shl_rm64_CL, MemoryOperand::with_base(RAX), CL) -); -testing!( - test_shl_imm8, - gpr(), - instruction!(Shl_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_shr, - gpr().with(VMReg::Rcx, IMM8_A), - instruction!(Shr_rm64_CL, RAX, CL) -); -testing_memory!( - test_shr_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM8_A), - instruction!(Shr_rm64_CL, MemoryOperand::with_base(RAX), CL) -); -testing!( - test_shr_imm8, - gpr(), - instruction!(Shr_rm64_imm8, RAX, SIMM8_A) -); - -testing!( - test_sar, - gpr().with(VMReg::Rcx, IMM8_A), - instruction!(Sar_rm64_CL, RAX, CL) -); -testing_memory!( - test_sar_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM8_A), - instruction!(Sar_rm64_CL, MemoryOperand::with_base(RAX), CL) -); -testing!( - test_sar_imm8, - gpr(), - instruction!(Sar_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_inc, gpr(), instruction!(Inc_rm64, RAX)); -testing_memory!( - test_inc_store, - mut buf = [IMM64_A], - baseline().with(VMReg::Rax, buf.as_mut_ptr() as u64), - instruction!(Inc_rm64, MemoryOperand::with_base(RAX)) -); - -testing!(test_dec, gpr(), instruction!(Dec_rm64, RAX)); -testing_memory!( - test_dec_store, - mut buf = [IMM64_A], - baseline().with(VMReg::Rax, buf.as_mut_ptr() as u64), - instruction!(Dec_rm64, MemoryOperand::with_base(RAX)) -); - -testing!(test_neg, gpr(), instruction!(Neg_rm64, RAX)); -testing_memory!( - test_neg_store, - mut buf = [IMM64_A], - baseline().with(VMReg::Rax, buf.as_mut_ptr() as u64), - instruction!(Neg_rm64, MemoryOperand::with_base(RAX)) -); - -testing!(test_not, gpr(), instruction!(Not_rm64, RAX)); -testing_memory!( - test_not_store, - mut buf = [IMM64_A], - baseline().with(VMReg::Rax, buf.as_mut_ptr() as u64), - instruction!(Not_rm64, MemoryOperand::with_base(RAX)) -); - -testing!(test_mul, gpr(), instruction!(Mul_rm64, RCX)); -testing_memory!( - test_mul_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Mul_rm64, MemoryOperand::with_base(RCX)) -); - -testing!(test_imul, gpr(), instruction!(Imul_rm64, RCX)); -testing_memory!( - test_imul_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Imul_rm64, MemoryOperand::with_base(RCX)) -); - -testing!(test_imul2, gpr(), instruction!(Imul_r64_rm64, RAX, RCX)); -testing_memory!( - test_imul2_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Imul_r64_rm64, RAX, MemoryOperand::with_base(RCX)) -); -testing!( - test_imul3, - gpr(), - instruction!(Imul_r64_rm64_imm32, RAX, RCX, SIMM32_A) -); -testing!( - test_imul3_imm8, - gpr(), - instruction!(Imul_r64_rm64_imm8, RAX, RCX, SIMM8_A) -); - -testing!( - test_div, - gpr().zeroed(VMReg::Rdx).with(VMReg::Rax, IMM64_A), - instruction!(Div_rm64, RCX) -); -testing_memory!( - test_div_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rdx, 0u64) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Div_rm64, MemoryOperand::with_base(RCX)) -); - -testing!( - test_idiv, - gpr().zeroed(VMReg::Rdx).with(VMReg::Rax, IMM64_A), - instruction!(Idiv_rm64, RCX) -); -testing_memory!( - test_idiv_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rdx, 0u64) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Idiv_rm64, MemoryOperand::with_base(RCX)) -); - -testing!(test_tzcnt, gpr(), instruction!(Tzcnt_r64_rm64, RAX, RCX)); -testing_memory!( - test_tzcnt_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Tzcnt_r64_rm64, RAX, MemoryOperand::with_base(RCX)) -); - -testing!(test_bsr, gpr(), instruction!(Bsr_r64_rm64, RAX, RCX)); -testing_memory!( - test_bsr_load, - buf = [IMM64_B], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rcx, buf.as_ptr() as u64), - instruction!(Bsr_r64_rm64, RAX, MemoryOperand::with_base(RCX)) -); - -testing!(test_bswap, gpr(), instruction!(Bswap_r64, RAX)); - -testing!(test_bt, gpr(), instruction!(Bt_rm64_r64, RAX, RCX)); -testing_memory!( - test_bt_store, - buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Bt_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_bt_imm8, - gpr(), - instruction!(Bt_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_bts, gpr(), instruction!(Bts_rm64_r64, RAX, RCX)); -testing_memory!( - test_bts_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Bts_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_bts_imm8, - gpr(), - instruction!(Bts_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_btr, gpr(), instruction!(Btr_rm64_r64, RAX, RCX)); -testing_memory!( - test_btr_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Btr_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_btr_imm8, - gpr(), - instruction!(Btr_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_btc, gpr(), instruction!(Btc_rm64_r64, RAX, RCX)); -testing_memory!( - test_btc_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Btc_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); -testing!( - test_btc_imm8, - gpr(), - instruction!(Btc_rm64_imm8, RAX, SIMM8_A) -); - -testing!(test_xchg, gpr(), instruction!(Xchg_rm64_r64, RAX, RCX)); -testing_memory!( - test_xchg_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Xchg_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); - -testing!(test_xadd, gpr(), instruction!(Xadd_rm64_r64, RAX, RCX)); -testing_memory!( - test_xadd_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Xadd_rm64_r64, MemoryOperand::with_base(RAX), RCX) -); - -testing!( - test_cmpxchg, - gpr(), - instruction!(Cmpxchg_rm64_r64, RBX, RCX) -); -testing_memory!( - test_cmpxchg_store, - mut buf = [IMM64_A], - baseline() - .with(VMReg::Rax, IMM64_A) - .with(VMReg::Rbx, buf.as_mut_ptr() as u64) - .with(VMReg::Rcx, IMM64_B), - instruction!(Cmpxchg_rm64_r64, MemoryOperand::with_base(RBX), RCX) -); - -testing!(test_cmove, gpr(), instruction!(Cmove_r64_rm64, RAX, RCX)); -testing!(test_cmovne, gpr(), instruction!(Cmovne_r64_rm64, RAX, RCX)); -testing!(test_cmova, gpr(), instruction!(Cmova_r64_rm64, RAX, RCX)); -testing!(test_cmovae, gpr(), instruction!(Cmovae_r64_rm64, RAX, RCX)); -testing!(test_cmovb, gpr(), instruction!(Cmovb_r64_rm64, RAX, RCX)); -testing!(test_cmovbe, gpr(), instruction!(Cmovbe_r64_rm64, RAX, RCX)); -testing!(test_cmovg, gpr(), instruction!(Cmovg_r64_rm64, RAX, RCX)); -testing!(test_cmovge, gpr(), instruction!(Cmovge_r64_rm64, RAX, RCX)); -testing!(test_cmovl, gpr(), instruction!(Cmovl_r64_rm64, RAX, RCX)); -testing!(test_cmovle, gpr(), instruction!(Cmovle_r64_rm64, RAX, RCX)); -testing!(test_cmovo, gpr(), instruction!(Cmovo_r64_rm64, RAX, RCX)); -testing!(test_cmovno, gpr(), instruction!(Cmovno_r64_rm64, RAX, RCX)); -testing!(test_cmovp, gpr(), instruction!(Cmovp_r64_rm64, RAX, RCX)); -testing!(test_cmovnp, gpr(), instruction!(Cmovnp_r64_rm64, RAX, RCX)); -testing!(test_cmovs, gpr(), instruction!(Cmovs_r64_rm64, RAX, RCX)); -testing!(test_cmovns, gpr(), instruction!(Cmovns_r64_rm64, RAX, RCX)); - -testing!(test_seta, gpr(), instruction!(Seta_rm8, AL)); -testing_set!(test_seta_store, Seta_rm8); - -testing!(test_setae, gpr(), instruction!(Setae_rm8, AL)); -testing_set!(test_setae_store, Setae_rm8); - -testing!(test_setb, gpr(), instruction!(Setb_rm8, AL)); -testing_set!(test_setb_store, Setb_rm8); - -testing!(test_setbe, gpr(), instruction!(Setbe_rm8, AL)); -testing_set!(test_setbe_store, Setbe_rm8); - -testing!(test_sete, gpr(), instruction!(Sete_rm8, AL)); -testing_set!(test_sete_store, Sete_rm8); - -testing!(test_setg, gpr(), instruction!(Setg_rm8, AL)); -testing_set!(test_setg_store, Setg_rm8); - -testing!(test_setge, gpr(), instruction!(Setge_rm8, AL)); -testing_set!(test_setge_store, Setge_rm8); - -testing!(test_setl, gpr(), instruction!(Setl_rm8, AL)); -testing_set!(test_setl_store, Setl_rm8); - -testing!(test_setle, gpr(), instruction!(Setle_rm8, AL)); -testing_set!(test_setle_store, Setle_rm8); - -testing!(test_setne, gpr(), instruction!(Setne_rm8, AL)); -testing_set!(test_setne_store, Setne_rm8); - -testing!(test_seto, gpr(), instruction!(Seto_rm8, AL)); -testing_set!(test_seto_store, Seto_rm8); - -testing!(test_setno, gpr(), instruction!(Setno_rm8, AL)); -testing_set!(test_setno_store, Setno_rm8); - -testing!(test_setp, gpr(), instruction!(Setp_rm8, AL)); -testing_set!(test_setp_store, Setp_rm8); - -testing!(test_setnp, gpr(), instruction!(Setnp_rm8, AL)); -testing_set!(test_setnp_store, Setnp_rm8); - -testing!(test_sets, gpr(), instruction!(Sets_rm8, AL)); -testing_set!(test_sets_store, Sets_rm8); - -testing!(test_setns, gpr(), instruction!(Setns_rm8, AL)); -testing_set!(test_setns_store, Setns_rm8); - -testing!( - test_pcmpeqb, - simd(), - instruction!(Pcmpeqb_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pcmpeqb_load, - [IMM128_B, 0u128], - instruction!(Pcmpeqb_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); - -testing!( - test_pmovmskb, - simd(), - instruction!(Pmovmskb_r64_xmm, RAX, XMM1) -); - -testing!(test_movd, simd(), instruction!(Movd_xmm_rm32, XMM0, EAX)); -testing_simd_load!( - test_movd_load, - [IMM128_B, 0u128], - instruction!(Movd_xmm_rm32, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movd_store, - instruction!(Movd_rm32_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!(test_movq, simd(), instruction!(Movq_xmm_rm64, XMM0, RAX)); -testing_simd_load!( - test_movq_load, - [IMM128_B, 0u128], - instruction!(Movq_xmm_rm64, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movq_store, - instruction!(Movq_rm64_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movaps, - simd(), - instruction!(Movaps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movaps_load, - [IMM128_B, 0u128], - instruction!(Movaps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movaps_store, - instruction!(Movaps_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movups, - simd(), - instruction!(Movups_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movups_load, - [IMM128_B, 0u128], - instruction!(Movups_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movups_store, - instruction!(Movups_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movapd, - simd(), - instruction!(Movapd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movapd_load, - [IMM128_B, 0u128], - instruction!(Movapd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movapd_store, - instruction!(Movapd_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movupd, - simd(), - instruction!(Movupd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movupd_load, - [IMM128_B, 0u128], - instruction!(Movupd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movupd_store, - instruction!(Movupd_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movdqa, - simd(), - instruction!(Movdqa_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movdqa_load, - [IMM128_B, 0u128], - instruction!(Movdqa_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movdqa_store, - instruction!(Movdqa_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movdqu, - simd(), - instruction!(Movdqu_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_movdqu_load, - [IMM128_B, 0u128], - instruction!(Movdqu_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movdqu_store, - instruction!(Movdqu_xmmm128_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movss, - simd(), - instruction!(Movss_xmm_xmmm32, XMM0, XMM1) -); -testing_simd_load!( - test_movss_load, - [IMM128_B, 0u128], - instruction!(Movss_xmm_xmmm32, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movss_store, - instruction!(Movss_xmmm32_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_movsd, - simd(), - instruction!(Movsd_xmm_xmmm64, XMM0, XMM1) -); -testing_simd_load!( - test_movsd_load, - [IMM128_B, 0u128], - instruction!(Movsd_xmm_xmmm64, XMM0, MemoryOperand::with_base(RAX)) -); -testing_simd_store!( - test_movsd_store, - instruction!(Movsd_xmmm64_xmm, MemoryOperand::with_base(RAX), XMM1) -); - -testing!( - test_pand, - simd(), - instruction!(Pand_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pand_load, - [IMM128_B, 0u128], - instruction!(Pand_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_andps, - simd(), - instruction!(Andps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_andps_load, - [IMM128_B, 0u128], - instruction!(Andps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_andpd, - simd(), - instruction!(Andpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_andpd_load, - [IMM128_B, 0u128], - instruction!(Andpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vandps, - simd(), - instruction!(VEX_Vandps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vandps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vandps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); - -testing!( - test_pandn, - simd(), - instruction!(Pandn_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pandn_load, - [IMM128_B, 0u128], - instruction!(Pandn_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_andnps, - simd(), - instruction!(Andnps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_andnps_load, - [IMM128_B, 0u128], - instruction!(Andnps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_andnpd, - simd(), - instruction!(Andnpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_andnpd_load, - [IMM128_B, 0u128], - instruction!(Andnpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); - -testing!(test_por, simd(), instruction!(Por_xmm_xmmm128, XMM0, XMM1)); -testing_simd_load!( - test_por_load, - [IMM128_B, 0u128], - instruction!(Por_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_orps, - simd(), - instruction!(Orps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_orps_load, - [IMM128_B, 0u128], - instruction!(Orps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_orpd, - simd(), - instruction!(Orpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_orpd_load, - [IMM128_B, 0u128], - instruction!(Orpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); - -testing!( - test_pxor, - simd(), - instruction!(Pxor_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pxor_load, - [IMM128_B, 0u128], - instruction!(Pxor_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_xorps, - simd(), - instruction!(Xorps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_xorps_load, - [IMM128_B, 0u128], - instruction!(Xorps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_xorpd, - simd(), - instruction!(Xorpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_xorpd_load, - [IMM128_B, 0u128], - instruction!(Xorpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vpxor, - simd(), - instruction!(VEX_Vpxor_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vpxor_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vpxor_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vxorps, - simd(), - instruction!(VEX_Vxorps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vxorps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vxorps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); - -testing!( - test_paddb, - simd(), - instruction!(Paddb_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_paddb_load, - [IMM128_B, 0u128], - instruction!(Paddb_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_paddw, - simd(), - instruction!(Paddw_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_paddw_load, - [IMM128_B, 0u128], - instruction!(Paddw_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_paddd, - simd(), - instruction!(Paddd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_paddd_load, - [IMM128_B, 0u128], - instruction!(Paddd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_paddq, - simd(), - instruction!(Paddq_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_paddq_load, - [IMM128_B, 0u128], - instruction!(Paddq_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_addps, - simd(), - instruction!(Addps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_addps_load, - [IMM128_B, 0u128], - instruction!(Addps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_addpd, - simd(), - instruction!(Addpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_addpd_load, - [IMM128_B, 0u128], - instruction!(Addpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vaddps, - simd(), - instruction!(VEX_Vaddps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vaddps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vaddps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vaddpd, - simd(), - instruction!(VEX_Vaddpd_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vaddpd_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vaddpd_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); - -testing!( - test_psubb, - simd(), - instruction!(Psubb_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_psubb_load, - [IMM128_B, 0u128], - instruction!(Psubb_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_psubw, - simd(), - instruction!(Psubw_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_psubw_load, - [IMM128_B, 0u128], - instruction!(Psubw_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_psubd, - simd(), - instruction!(Psubd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_psubd_load, - [IMM128_B, 0u128], - instruction!(Psubd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_psubq, - simd(), - instruction!(Psubq_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_psubq_load, - [IMM128_B, 0u128], - instruction!(Psubq_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_subps, - simd(), - instruction!(Subps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_subps_load, - [IMM128_B, 0u128], - instruction!(Subps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_subpd, - simd(), - instruction!(Subpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_subpd_load, - [IMM128_B, 0u128], - instruction!(Subpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vsubps, - simd(), - instruction!(VEX_Vsubps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vsubps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vsubps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vsubpd, - simd(), - instruction!(VEX_Vsubpd_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vsubpd_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vsubpd_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); - -testing!( - test_pmullw, - simd(), - instruction!(Pmullw_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pmullw_load, - [IMM128_B, 0u128], - instruction!(Pmullw_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -// testing!( -// test_pmulhw, -// simd(), -// instruction!(Pmulhw_xmm_xmmm128, XMM0, XMM1) -// ); -// testing_simd_load!( -// test_pmulhw_load, -// [IMM128_B, 0u128], -// instruction!(Pmulhw_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -// ); -testing!( - test_pmulld, - simd(), - instruction!(Pmulld_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_pmulld_load, - [IMM128_B, 0u128], - instruction!(Pmulld_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_mulps, - simd(), - instruction!(Mulps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_mulps_load, - [IMM128_B, 0u128], - instruction!(Mulps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_mulpd, - simd(), - instruction!(Mulpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_mulpd_load, - [IMM128_B, 0u128], - instruction!(Mulpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vpmullw, - simd(), - instruction!(VEX_Vpmullw_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vpmullw_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vpmullw_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vpmulhw, - simd(), - instruction!(VEX_Vpmulhw_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vpmulhw_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vpmulhw_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vpmulld, - simd(), - instruction!(VEX_Vpmulld_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vpmulld_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vpmulld_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vmulps, - simd(), - instruction!(VEX_Vmulps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vmulps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vmulps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vmulpd, - simd(), - instruction!(VEX_Vmulpd_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vmulpd_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vmulpd_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); - -testing!( - test_divps, - simd(), - instruction!(Divps_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_divps_load, - [IMM128_B, 0u128], - instruction!(Divps_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_divpd, - simd(), - instruction!(Divpd_xmm_xmmm128, XMM0, XMM1) -); -testing_simd_load!( - test_divpd_load, - [IMM128_B, 0u128], - instruction!(Divpd_xmm_xmmm128, XMM0, MemoryOperand::with_base(RAX)) -); -testing!( - test_vdivps, - simd(), - instruction!(VEX_Vdivps_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vdivps_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vdivps_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); -testing!( - test_vdivpd, - simd(), - instruction!(VEX_Vdivpd_xmm_xmm_xmmm128, XMM0, XMM1, XMM2) -); -testing_simd_load!( - test_vdivpd_load, - [IMM128_C, 0u128], - instruction!( - VEX_Vdivpd_xmm_xmm_xmmm128, - XMM0, - XMM1, - MemoryOperand::with_base(RAX) - ) -); diff --git a/tests/src/instrumentation.rs b/tests/src/instrumentation.rs index cc4a3b3f..e346bc21 100644 --- a/tests/src/instrumentation.rs +++ b/tests/src/instrumentation.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, ffi::c_void, ptr}; -use crate::{constants::VECTORS, NATIVE_REGISTRY, VIRTUAL_REGISTRY, XSTATE_AVX}; +use crate::{constants::VIRTUAL_VECTORS, NATIVE_REGISTRY, VIRTUAL_REGISTRY, XSTATE_AVX}; use runtime::vm::bytecode::{Flag, VMReg, VMVec}; use windows::Win32::{ Foundation::EXCEPTION_SINGLE_STEP, @@ -13,41 +13,59 @@ use windows::Win32::{ }, }; -pub unsafe extern "system" fn virtual_handler(_info: *mut EXCEPTION_POINTERS) -> i32 { - if VIRTUAL_REGISTRY - .lock() - .unwrap() - .contains(&GetCurrentThreadId()) - { +pub unsafe extern "system" fn virtual_handler(info: *mut EXCEPTION_POINTERS) -> i32 { + let terminate = { + let mut registry = VIRTUAL_REGISTRY.lock().unwrap(); + + if let Some(exception) = registry.get_mut(&GetCurrentThreadId()) { + *exception = Some((*(*info).ExceptionRecord).ExceptionCode.0 as u32); + true + } else { + false + } + }; + + if terminate { TerminateThread(GetCurrentThread(), 0).unwrap(); + return EXCEPTION_CONTINUE_EXECUTION; } + EXCEPTION_CONTINUE_SEARCH } pub unsafe extern "system" fn native_handler(info: *mut EXCEPTION_POINTERS) -> i32 { - let entry = NATIVE_REGISTRY - .lock() - .unwrap() - .get(&GetCurrentThreadId()) - .copied(); - - if let Some((context, limit)) = entry { - if (*(*info).ExceptionRecord).ExceptionCode == EXCEPTION_SINGLE_STEP { - let rip = (*(*info).ContextRecord).Rip as usize; - - if rip < limit { - (*(*info).ContextRecord).EFlags |= Flag::Trap.bit32(); - return EXCEPTION_CONTINUE_EXECUTION; - } + let terminate = { + let mut registry = NATIVE_REGISTRY.lock().unwrap(); + + if let Some((context, base, limit, exception)) = registry.get_mut(&GetCurrentThreadId()) { + if (*(*info).ExceptionRecord).ExceptionCode == EXCEPTION_SINGLE_STEP { + let rip = (*(*info).ContextRecord).Rip as usize; + + if rip >= *base && rip < *limit { + (*(*info).ContextRecord).EFlags |= Flag::Trap.bit32(); + return EXCEPTION_CONTINUE_EXECUTION; + } + + ptr::copy_nonoverlapping((*info).ContextRecord, *context as *mut CONTEXT, 1); - ptr::copy_nonoverlapping((*info).ContextRecord, context as *mut CONTEXT, 1); + true + } else { + *exception = Some((*(*info).ExceptionRecord).ExceptionCode.0 as u32); + + true + } + } else { + false } + }; + if terminate { TerminateThread(GetCurrentThread(), 0).unwrap(); return EXCEPTION_CONTINUE_EXECUTION; } + EXCEPTION_CONTINUE_SEARCH } @@ -101,11 +119,13 @@ pub unsafe fn write_vectors(context: &mut CONTEXT, vectors: &HashMap> 64) as i64, }; + *upper.add(index) = M128A { Low: value[1] as u64, High: (value[1] >> 64) as i64, @@ -116,20 +136,23 @@ pub unsafe fn write_vectors(context: &mut CONTEXT, vectors: &HashMap HashMap { let upper = LocateXStateFeature(context, XSTATE_AVX, None) as *const M128A; - VECTORS + VIRTUAL_VECTORS .iter() .enumerate() .map(|(index, &vector)| { let low = context.Anonymous.FltSave.XmmRegisters[index]; + let high = if upper.is_null() { M128A::default() } else { *upper.add(index) }; + let value = [ low.Low as u128 | ((low.High as u64 as u128) << 64), high.Low as u128 | ((high.High as u64 as u128) << 64), ]; + (vector, value) }) .collect() diff --git a/tests/src/lib.rs b/tests/src/lib.rs index f2322735..7aee8242 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,7 +1,7 @@ #![cfg(test)] use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, ffi::c_void, hint, mem, ptr, sync::{ @@ -11,7 +11,7 @@ use std::{ }; use crate::{ - constants::{FAKE_BRANCH_ADDRESS, REGISTERS, VECTORS}, + constants::{VIRTUAL_REGISTERS, VIRTUAL_VECTORS}, instrumentation::{ initialize_context, native_handler, read_register, read_vectors, virtual_handler, write_register, write_vectors, @@ -21,7 +21,7 @@ use iced_x86::{ code_asm::{ptr, r12, r12d, r8, r9, rax, ymm0}, BlockEncoder, BlockEncoderOptions, Instruction, InstructionBlock, }; -use obfuscator::protections::virtualization::crypt; +use obfuscator::protections::virtualization::crypt::{self, Cipher}; use runtime::{ mapper::Mappable, runtime::{BoolDef, DataDef, FnDef, Runtime}, @@ -38,8 +38,7 @@ use windows::Win32::{ CONTEXT, CONTEXT_ALL_AMD64, CONTEXT_XSTATE_AMD64, }, Memory::{ - VirtualAlloc, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, - PAGE_EXECUTE_READWRITE, PAGE_READWRITE, + VirtualAlloc, VirtualFree, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE_READWRITE, }, Threading::{ CreateThread, FlsAlloc, ResumeThread, SuspendThread, TlsAlloc, WaitForSingleObject, @@ -52,38 +51,41 @@ mod constants; mod instructions; mod instrumentation; -static FAKE_BRANCH_MAPPED: OnceLock<()> = OnceLock::new(); - static TLS_REGISTERS: OnceLock = OnceLock::new(); static TLS_KEY: OnceLock = OnceLock::new(); static TLS_DEBUG: OnceLock = OnceLock::new(); static FLS_CLEANUP: OnceLock = OnceLock::new(); -static NATIVE_REGISTRY: LazyLock>> = +static NATIVE_REGISTRY: LazyLock)>>> = LazyLock::new(|| Mutex::new(HashMap::new())); static NATIVE_HANDLER: OnceLock<()> = OnceLock::new(); -static VIRTUAL_REGISTRY: LazyLock>> = - LazyLock::new(|| Mutex::new(HashSet::new())); +static VIRTUAL_REGISTRY: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); static VIRTUAL_HANDLER: OnceLock<()> = OnceLock::new(); const XSTATE_AVX: u32 = 2; const XSTATE_MASK_AVX: u64 = 4; -#[derive(Clone, PartialEq, Eq, Default)] -pub(crate) struct State { +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct State { pub registers: HashMap, pub vectors: HashMap, + pub exception: Option, } -pub(crate) enum Difference { +pub enum Difference { Register(VMReg, u64, u64), Vector(VMVec, [u128; 2], [u128; 2]), + Exception(u32, u32), } impl State { - pub fn with>(mut self, register: VMReg, value: T) -> Self { - self.registers.insert(register, value.into()); + pub fn with>(mut self, register: VMReg, value: T) -> Self + where + T::Error: std::fmt::Debug, + { + self.registers.insert(register, value.try_into().unwrap()); self } @@ -93,6 +95,26 @@ impl State { pub fn compare(&self, other: &Self) -> Vec { let mut differences = Vec::new(); + + match (self.exception, other.exception) { + (Some(native), Some(virtual_)) => { + if native != virtual_ { + differences.push(Difference::Exception(native, virtual_)); + } + + return differences; + } + (Some(native), None) => { + differences.push(Difference::Exception(native, 0)); + return differences; + } + (None, Some(virtual_)) => { + differences.push(Difference::Exception(0, virtual_)); + return differences; + } + (None, None) => {} + } + for (®ister, &expected) in &self.registers { if let Some(&received) = other.registers.get(®ister) { if expected != received { @@ -100,6 +122,7 @@ impl State { } } } + for (&vector, &expected) in &self.vectors { if let Some(&received) = other.vectors.get(&vector) { if expected != received { @@ -107,37 +130,29 @@ impl State { } } } + differences } } -pub(crate) struct Executor { +pub struct Executor { pub rt: Runtime, - pub mem: *mut c_void, + pub memory: *mut c_void, } impl Executor { - pub const TEST_KEY_SEED: u64 = 0x1234567890ABCDEF; - pub const TEST_KEY_MUL: u64 = 0x1234567890ABCDEF; - pub const TEST_KEY_ADD: u64 = 0x1234567890ABCDEF; + pub const TEST_KEY_INITIALIZER: u64 = 0x1234567890ABCDEF; + pub const TEST_KEY_MULTIPLIER: u64 = 0x1234567890ABCDEF; + pub const TEST_KEY_ADDEND: u64 = 0x1234567890ABCDEF; pub const SIZE: usize = 0x100000; pub fn new() -> Self { - FAKE_BRANCH_MAPPED.get_or_init(|| unsafe { - let _ = VirtualAlloc( - Some(FAKE_BRANCH_ADDRESS as *const c_void), - Self::SIZE, - MEM_COMMIT | MEM_RESERVE, - PAGE_READWRITE, - ); - }); - let mut rt = Runtime::new(64); - rt.define_data_qword(DataDef::VmKeySeed, Self::TEST_KEY_SEED); - rt.define_data_qword(DataDef::VmKeyMul, Self::TEST_KEY_MUL); - rt.define_data_qword(DataDef::VmKeyAdd, Self::TEST_KEY_ADD); + rt.define_data_qword(DataDef::VmKeyInitializer, Self::TEST_KEY_INITIALIZER); + rt.define_data_qword(DataDef::VmKeyMultiplier, Self::TEST_KEY_MULTIPLIER); + rt.define_data_qword(DataDef::VmKeyAddend, Self::TEST_KEY_ADDEND); rt.define_bool(BoolDef::HasVeh, true); @@ -158,7 +173,7 @@ impl Executor { *FLS_CLEANUP.get_or_init(|| unsafe { FlsAlloc(None) }), ); - let mem = unsafe { + let memory = unsafe { VirtualAlloc( None, Self::SIZE, @@ -167,7 +182,7 @@ impl Executor { ) }; - Self { rt, mem } + Self { rt, memory } } pub fn run_virtual(&mut self, state: State, bytes: &[u8]) -> State { @@ -207,9 +222,9 @@ impl Executor { ) .unwrap(); - let mut vectors = vec![[0u128; 2]; VECTORS.len()]; + let mut vectors = vec![[0u128; 2]; VIRTUAL_VECTORS.len()]; - for vector in VECTORS { + for vector in VIRTUAL_VECTORS { if let Some(v) = state.vectors.get(&vector) { vectors[self.rt.mapper.index(vector) as usize] = *v; } @@ -218,7 +233,7 @@ impl Executor { // mov r9, ... self.rt.asm.mov(r9, vectors.as_ptr() as u64).unwrap(); - for vector in VECTORS { + for vector in VIRTUAL_VECTORS { // vmovdqu ymm0, [r9 + ...] self.rt .asm @@ -234,8 +249,10 @@ impl Executor { // lea rax, [...] self.rt .asm - .lea(rax, ptr(self.rt.data_labels[&DataDef::VmCode])) + .lea(rax, ptr(self.rt.data_labels[&DataDef::VmCodeStart])) .unwrap(); + // add rax, size_of::() + self.rt.asm.add(rax, size_of::() as i32).unwrap(); // mov [r12 + ...], rax utils::vreg::store_reg(&mut self.rt, r12, rax, VMReg::BPointer); @@ -251,7 +268,7 @@ impl Executor { // mov r8, ... self.rt.asm.mov(r8, registers.as_mut_ptr() as u64).unwrap(); - for register in REGISTERS { + for register in VIRTUAL_REGISTERS { // mov r9, [r12 + ...] self.rt .asm @@ -275,7 +292,7 @@ impl Executor { // mov r9, ... self.rt.asm.mov(r9, vectors.as_mut_ptr() as u64).unwrap(); - for vector in VECTORS { + for vector in VIRTUAL_VECTORS { // vmovdqu ymm0, [r8 + ...] self.rt .asm @@ -291,16 +308,18 @@ impl Executor { // ret self.rt.asm.ret().unwrap(); - self.rt.define_data_bytes(DataDef::VmCode, bytes); + let mut data = (size_of::() as i32).to_le_bytes().to_vec(); + data.extend_from_slice(bytes); + self.rt.define_data_bytes(DataDef::VmCodeStart, &data); - let ip = self.mem as u64; + let ip = self.memory as u64; let code = self.rt.assemble(ip); assert!(code.len() <= Self::SIZE); unsafe { - ptr::copy_nonoverlapping(code.as_ptr(), self.mem as *mut u8, code.len()); + ptr::copy_nonoverlapping(code.as_ptr(), self.memory as *mut u8, code.len()); } VIRTUAL_HANDLER.get_or_init(|| unsafe { @@ -316,15 +335,15 @@ impl Executor { Some(mem::transmute::< *const (), unsafe extern "system" fn(*mut c_void) -> u32, - >(self.mem as *const ())), - Some(self.mem as *mut c_void), + >(self.memory as *const ())), + Some(self.memory as *mut c_void), THREAD_CREATE_SUSPENDED, Some(&mut thread_id), ) .unwrap() }; - VIRTUAL_REGISTRY.lock().unwrap().insert(thread_id); + VIRTUAL_REGISTRY.lock().unwrap().insert(thread_id, None); unsafe { ResumeThread(thread); @@ -332,22 +351,27 @@ impl Executor { CloseHandle(thread).unwrap(); } - VIRTUAL_REGISTRY.lock().unwrap().remove(&thread_id); + let exception = VIRTUAL_REGISTRY + .lock() + .unwrap() + .remove(&thread_id) + .flatten(); State { - registers: REGISTERS + registers: VIRTUAL_REGISTERS .iter() .map(|&r| (r, registers[self.rt.mapper.index(r) as usize])) .collect(), - vectors: VECTORS + vectors: VIRTUAL_VECTORS .iter() .map(|&v| (v, vectors[self.rt.mapper.index(v) as usize])) .collect(), + exception, } } pub fn run_native(&mut self, state: State, instructions: &[Instruction]) -> State { - let ip = self.mem as u64; + let ip = self.memory as u64; let result = BlockEncoder::encode( 64, @@ -359,12 +383,13 @@ impl Executor { unsafe { ptr::copy_nonoverlapping( result.code_buffer.as_ptr(), - self.mem as *mut u8, + self.memory as *mut u8, result.code_buffer.len(), ); } - let limit = self.mem as usize + result.code_buffer.len(); + let base = self.memory as usize; + let limit = base + result.code_buffer.len(); let initialized = AtomicBool::new(false); @@ -405,7 +430,7 @@ impl Executor { &mut *context }; - context.Rip = self.mem as u64; + context.Rip = self.memory as u64; for (®ister, &value) in &state.registers { write_register(context, register, value); @@ -418,10 +443,10 @@ impl Executor { AddVectoredExceptionHandler(1, Some(native_handler)); }); - NATIVE_REGISTRY - .lock() - .unwrap() - .insert(thread_id, (context as *mut CONTEXT as usize, limit)); + NATIVE_REGISTRY.lock().unwrap().insert( + thread_id, + (context as *mut CONTEXT as usize, base, limit, None), + ); unsafe { SetThreadContext(thread, context).unwrap(); @@ -430,70 +455,48 @@ impl Executor { CloseHandle(thread).unwrap(); } - NATIVE_REGISTRY.lock().unwrap().remove(&thread_id); + let exception = NATIVE_REGISTRY + .lock() + .unwrap() + .remove(&thread_id) + .and_then(|(_, _, _, exception)| exception); context.EFlags &= !(Flag::Interrupt.bit32() | Flag::Reserved1.bit32()); - let registers = REGISTERS + let registers = VIRTUAL_REGISTERS .iter() .map(|®ister| (register, read_register(context, register))) .collect(); let vectors = unsafe { read_vectors(context) }; - State { registers, vectors } + State { + registers, + vectors, + exception, + } } } impl Drop for Executor { fn drop(&mut self) { unsafe { - let _ = VirtualFree(self.mem, 0, MEM_RELEASE); + let _ = VirtualFree(self.memory, 0, MEM_RELEASE); } } } -pub(crate) fn encrypt_block(block: &mut Vec) { - crypt::encrypt_block( - block, - Executor::TEST_KEY_SEED, - Executor::TEST_KEY_MUL, - Executor::TEST_KEY_ADD, - 0, - ); +fn cipher() -> crypt::Cipher { + Cipher::new(Executor::TEST_KEY_MULTIPLIER, Executor::TEST_KEY_ADDEND) } -pub(crate) fn decrypt_payload(block: &mut Vec) { - crypt::decrypt_payload( - block, - Executor::TEST_KEY_SEED, - Executor::TEST_KEY_MUL, - Executor::TEST_KEY_ADD, - 0, - ) -} -pub(crate) fn decrypt_block(block: &mut Vec) { - crypt::decrypt_block( - block, - Executor::TEST_KEY_SEED, - Executor::TEST_KEY_MUL, - Executor::TEST_KEY_ADD, - 0, - ) +pub fn encrypt_block(block: &mut Vec) { + cipher().encrypt_block(block, Executor::TEST_KEY_INITIALIZER, 0); } -macro_rules! instruction { - (branch $code:ident, $target:expr) => { - iced_x86::Instruction::with_branch(iced_x86::Code::$code, $target).unwrap() - }; - ($code:ident, $a:expr) => { - iced_x86::Instruction::with1(iced_x86::Code::$code, $a).unwrap() - }; - ($code:ident, $a:expr, $b:expr) => { - iced_x86::Instruction::with2(iced_x86::Code::$code, $a, $b).unwrap() - }; - ($code:ident, $a:expr, $b:expr, $c:expr) => { - iced_x86::Instruction::with3(iced_x86::Code::$code, $a, $b, $c).unwrap() - }; +pub fn decrypt_payload(block: &mut Vec) { + cipher().decrypt_payload(block, Executor::TEST_KEY_INITIALIZER, 0); } -pub(crate) use instruction; +pub fn decrypt_block(block: &mut Vec) { + cipher().decrypt_block(block, Executor::TEST_KEY_INITIALIZER, 0); +}