Bug fixes, new transform passes, test coverage, and CI tooling

Bug fixes:
- pclntab/rtcorrupt_linux.go: remove duplicate mprotectRW/mprotectRO that
  broke GOOS=linux builds (they're defined in rtcorrupt_unsafe.go for
  darwin+linux).
- pclntab/pclntab.go: readUint now guards against negative offset
  (previously panicked via slice out-of-range).
- integrity/integrity.go: SetExpectedHashHex now validates length 64
  up-front and reports byte index on parse error; added ClearExpectedHash
  for runtime toggling and test isolation.
- transform/mba.go: mbaApplyBinOp used strings.Index on first match
  instead of the current match; replaced with FindAllStringSubmatchIndex
  + two-pass replacement.
- transform/transform.go: removed duplicate unconditional
  SplitLongStrings call.

New transform passes (CLI flags + Config fields + pipeline wiring):
- splice, constblind, mba, fakesigs, funcsplit, bbreorder — all
  default true.

Cleanup:
- strenc/keyderive.go: removed unused DeriveFromBinary + buildSeed.
- transform/transform.go: removed unused trimFirstParen.
- antidebug_hook_darwin.go: modernized readAddr to unsafe.Slice with
  noescape pattern (vet-safe).

Tests (10 packages, 116 tests, all green):
- internal/transform: 28 tests covering every pipeline stage.
- pkg/strenc: 7 tests for key derivation + XOR stream.
- pkg/stringcrypt: 7 tests for CFB round-trip + tamper detection.
- pkg/pageman: 14 tests for alignment + mprotect guards.
- pkg/integrity: 14 tests including ClearExpectedHash teardown.
- pkg/pclntab: 12 tests including readUint negative-offset guard.
- pkg/antisandbox: 6 tests for hostname/MAC lists + Check delegation.
- pkg/antiemul: 6 tests (1 skipped on non-Linux) for no-panic + timing.

Tooling:
- Makefile: added test, verify, lint, fmt, vet targets.
  vet uses -unsafeptr=false (only legitimate uintptr→pointer is the
  darwin antidebug hook). fmt skips build/ (generated).
- README.md: test coverage table + Makefile target reference.
This commit is contained in:
2026-06-01 14:08:50 +01:00
parent 68f8904282
commit b6c3bacd23
18 changed files with 1486 additions and 93 deletions
+55 -21
View File
@@ -152,42 +152,60 @@ var mbaOrIdentities = []mbaIdentity{
}
func mbaApplyBinOp(line string, re *regexp.Regexp, identities []mbaIdentity, rng *rand.Rand) string {
// Track positions already transformed to avoid double-transforming
var transformed []struct{ start, end int }
// Use FindAllStringSubmatchIndex so we know the exact position of each match
// in the line. FindStringIndex on the match alone only gives a position relative
// to the match, not the line, which previously caused us to mis-attribute
// already-transformed ranges (the bug: strings.Index(line, match) returns the
// first occurrence, not the current one).
type range_ struct{ start, end int }
var transformed []range_
return re.ReplaceAllStringFunc(line, func(match string) string {
loc := re.FindStringIndex(match)
if loc == nil {
return match
// Mutate the line by recording each replacement and applying in a second pass.
// We can't use ReplaceAllStringFunc alone because we need the absolute position.
matches := re.FindAllStringSubmatchIndex(line, -1)
if len(matches) == 0 {
return line
}
type repl struct {
start, end int
text string
}
var repls []repl
for _, loc := range matches {
fullStart, fullEnd := loc[0], loc[1]
if fullEnd > len(line) {
continue
}
match := line[fullStart:fullEnd]
// Check if this match overlaps with any already-transformed region
absStart := strings.Index(line, match)
// Overlap with already-transformed region?
overlap := false
for _, t := range transformed {
if absStart >= t.start && absStart < t.end {
return match
if fullStart < t.end && fullEnd > t.start {
overlap = true
break
}
}
if overlap {
continue
}
sub := re.FindStringSubmatch(match)
if len(sub) < 3 {
return match
continue
}
a, b := sub[1], sub[2]
// Don't transform if either operand looks like a comparison result or is empty
if a == "" || b == "" {
return match
continue
}
// Don't transform pure integer-integer operations (e.g., 2 + 3)
// Those are constant-folded by the compiler anyway
// Constant-folded by the compiler, no point in transforming.
if isPureNumeric(a) && isPureNumeric(b) {
return match
continue
}
ident := identities[rng.Intn(len(identities))]
var result string
switch len(strings.Split(ident.template, "%s")) - 1 {
case 2:
@@ -202,8 +220,24 @@ func mbaApplyBinOp(line string, re *regexp.Regexp, identities []mbaIdentity, rng
result = fmt.Sprintf(ident.template, a, b)
}
return result
})
repls = append(repls, repl{start: fullStart, end: fullEnd, text: result})
transformed = append(transformed, range_{fullStart, fullEnd})
}
if len(repls) == 0 {
return line
}
var buf strings.Builder
buf.Grow(len(line))
cursor := 0
for _, r := range repls {
buf.WriteString(line[cursor:r.start])
buf.WriteString(r.text)
cursor = r.end
}
buf.WriteString(line[cursor:])
return buf.String()
}
func mbaApplyUnaryNot(line string, rng *rand.Rand) string {