package winpwn import ( "bytes" "fmt" ) // FindXORKey finds a single byte k such that XORing data with the // repeating key k produces no byte present in badChars -- the search step // behind pwntools' encoders.xor, scoped to a single-byte key (the common // case: avoiding \x00/\x0a/\x0d in a payload that itself gets typed/parsed // as text before reaching the target). Returns an error if no byte in // 0..255 works, which can happen if badChars is large enough that every // possible key XORs at least one data byte into a forbidden value. func FindXORKey(data []byte, badChars []byte) (byte, error) { var bad [256]bool for _, b := range badChars { bad[b] = true } candidate: for k := 0; k < 256; k++ { key := byte(k) if bad[key] { continue // the key itself ends up nowhere in the output, but // disallowing it too keeps the result usable as a literal // byte elsewhere in the same payload (e.g. a decoder stub // that embeds the key as an immediate). } for _, b := range data { if bad[b^key] { continue candidate } } return key, nil } return 0, fmt.Errorf("no single-byte XOR key avoids all %d bad chars for this %d-byte input", len(badChars), len(data)) } // EncodeXOR finds a single-byte XOR key avoiding badChars (via FindXORKey) // and returns data encoded with it, plus the key itself. Decode by XORing // again with the same key (see Xor in fiddling.go) -- this is the // data-level half of pwntools' bad-character avoidance; it does not emit a // self-decoding stub, so the receiving side needs to already know how to // undo it (e.g. the target's own code does the XOR, or your script decodes // a leaked buffer before parsing it). func EncodeXOR(data []byte, badChars []byte) (encoded []byte, key byte, err error) { key, err = FindXORKey(data, badChars) if err != nil { return nil, 0, err } return Xor(data, []byte{key}), key, nil } // HasBadChars reports whether data contains any byte in badChars -- the // quick check before bothering with an encoder at all. func HasBadChars(data []byte, badChars []byte) bool { return bytes.ContainsAny(data, string(badChars)) }