Files
2026-07-21 22:30:10 +02:00

75 lines
4.0 KiB
Markdown

## Context
BlackMagic is a pure C# process manipulation library. It replaced FASM (native x86 assembler DLL) with hand-assembled `byte[]` stubs. Two capabilities were lost:
- `InjectAndExecuteEx`: non-blocking remote thread that returns a handle without waiting.
- Text-based assembly: build code payloads from `pushad`, `mov eax, 0x1234`, etc. instead of raw bytes.
Existing code:
- `BMThread.cs` has blocking `Execute(addr, param)` → waits 10s, returns exit code.
- `BMThread.cs` has `CreateRemoteThread(addr, param)` → returns `SafeMemoryHandle?`.
- `SInject.cs` has `InjectCode(addr, bytes)` and `InjectCode(bytes)` → allocate + write.
- `BlackMagicTest/` uses xUnit, `net8.0-windows`, pure-logic tests (no process needed).
## Goals / Non-Goals
**Goals:**
- Add `InjectAndExecuteEx()`: inject code + create remote thread, return handle, do NOT wait.
- Add `AsmBuilder`: pure C# x86 text assembler. Convert `"pushad\nmov eax, 1\npopad"``byte[]`.
- Add `SetPassLimit(int)` on `AsmBuilder` for label resolution iteration control.
- Add convenience overloads: `InjectAndExecute(string asm)`, `InjectAndExecuteEx(string asm)`.
- TDD: write tests first for every public API surface.
**Non-Goals:**
- Full x86 instruction set (only common payload subset: mov, push, pop, call, jmp, ret, nop, pushad/popad, test, je/jne, inc, add, sub, xor, and, or, cmp, lea, nop, hlt).
- x64 text assembly (keep x86 only; x64 stubs remain hand-assembled `byte[]`).
- Reimplementing FASM's directive system (`org`, `use32`, macros).
- Native DLL dependency.
## Decisions
### D1: AsmBuilder lives in `BlackMagic/Asm/AsmBuilder.cs`
New directory `Asm/` keeps assembler code isolated. Static class, no instance state needed — each `Assemble()` call is self-contained.
**Alternatives considered:**
- Instance class with `AddLine()` builder pattern → rejected: adds statefulness for no benefit. Each payload is built fresh.
- Put in `Injection/` → rejected: assembler is generic, not injection-specific.
### D2: Two-pass assembler (labels + bytes)
Pass 1: scan instructions, record label positions, emit bytes (reserving 4 bytes for near jumps). Pass 2: resolve label offsets, patch jump targets.
This handles forward references (`jmp @skip` before `@skip:` label is defined) without multiple iterations. `SetPassLimit()` caps the loop for safety but 2 passes is sufficient for all payload patterns.
**Alternatives considered:**
- Single-pass → rejected: can't resolve forward jumps.
- FASM-style multi-pass → overkill: payloads don't need complex expression evaluation.
### D3: Instruction encoding via switch + helper methods
Each instruction maps to hand-coded byte emission. Helper methods: `EmitU8()`, `EmitU32()`, `EmitModRM()`, `EmitSIB()`. Same pattern as `SInject.cs`'s existing `BuildStub()` methods.
**Alternatives considered:**
- Lookup table / dictionary → rejected: instruction encoding is irregular (ModRM, SIB, displacement, immediate). Switch statements are clearer.
### D4: InjectAndExecuteEx returns `SafeMemoryHandle`
Matches existing `CreateRemoteThread()` return type. Caller is responsible for disposing the handle. No auto-wait, no auto-close.
### D5: TDD approach
Write xUnit tests in `BlackMagicTest/AsmBuilderTests.cs` first:
- Each instruction: known input text → expected `byte[]` output.
- Label resolution: forward jump, backward jump, multiple labels.
- Error cases: unknown instruction, missing operand, invalid register.
- `SetPassLimit`: verify iteration cap is respected.
Then implement `AsmBuilder` to make tests pass.
## Risks / Trade-offs
- **Instruction subset**: users may need an instruction not in the initial set. Mitigation: document supported instructions, add new ones incrementally.
- **Label complexity**: relative jumps are limited to ±127 bytes (near) or ±2GB (far). Payloads rarely exceed this, but document the limit.
- **No runtime validation of payloads**: the assembler produces bytes; it doesn't verify the result is safe to execute. This matches FASM's behavior — the assembler doesn't validate semantics.