@@ -7,13 +7,16 @@ use libafl::{
77 common:: HasMetadata ,
88 corpus:: { Corpus , CorpusId , NopCorpus } ,
99 inputs:: BytesInput ,
10- mutators:: { HavocScheduledMutator , MutationResult , Mutator , havoc_mutations} ,
10+ mutators:: {
11+ HavocScheduledMutator , MutationResult , Mutator , Tokens , havoc_mutations, tokens_mutations,
12+ } ,
1113 random_corpus_id,
1214 state:: { HasCorpus , HasRand , StdState } ,
1315} ;
1416use libafl_bolts:: {
1517 HasLen , Named ,
1618 rands:: { Rand , StdRand } ,
19+ tuples:: Merge ,
1720} ;
1821use rand:: RngCore ;
1922
@@ -288,13 +291,82 @@ impl<M, R> Named for IrGenerator<M, R> {
288291 }
289292}
290293
294+ /// A dictionary of Bitcoin-significant byte sequences for the raw byte mutator.
295+ ///
296+ /// `OperationMutator` drives all `LoadBytes` mutations through [`LibAflByteMutator`], and
297+ /// `LoadBytes` values end up as raw output/input scripts (`BuildRawScripts`), witness stack
298+ /// elements (`AddWitness`) and raw p2p message payloads. Plain havoc rarely assembles a valid
299+ /// opcode sequence or a recognizable script template by chance, so the script interpreter and the
300+ /// structured parsers behind these buffers stay shallowly explored. Feeding the havoc stage a
301+ /// dictionary of real Script opcodes and standard script templates lets `TokenInsert`/
302+ /// `TokenReplace` splice meaningful fragments in, reaching interpreter branches that random bytes
303+ /// almost never hit.
304+ fn bitcoin_script_dictionary ( ) -> Tokens {
305+ // Single-byte Script opcodes. Values mirror Bitcoin Core's `script/script.h`.
306+ let opcodes: & [ u8 ] = & [
307+ // Push value
308+ 0x00 , // OP_0 / OP_FALSE
309+ 0x4f , // OP_1NEGATE
310+ 0x51 , // OP_1 / OP_TRUE
311+ 0x52 , 0x53 , 0x54 , 0x55 , 0x56 , 0x57 , 0x58 , // OP_2 ..= OP_8
312+ 0x59 , 0x5a , 0x5b , 0x5c , 0x5d , 0x5e , 0x5f , 0x60 , // OP_9 ..= OP_16
313+ // Push-data length prefixes
314+ 0x4c , 0x4d , 0x4e , // OP_PUSHDATA1/2/4
315+ // Control flow
316+ 0x61 , // OP_NOP
317+ 0x63 , 0x64 , 0x67 , 0x68 , // OP_IF, OP_NOTIF, OP_ELSE, OP_ENDIF
318+ 0x69 , 0x6a , // OP_VERIFY, OP_RETURN
319+ // Stack / alt-stack ops (OP_TOALTSTACK .. OP_TUCK)
320+ 0x6b , 0x6c , 0x6d , 0x6e , 0x6f , 0x70 , 0x71 , 0x72 , 0x73 , 0x74 , //
321+ 0x75 , 0x76 , 0x77 , 0x78 , 0x79 , 0x7a , 0x7b , 0x7c , 0x7d , // .. OP_DROP/OP_DUP/OP_SWAP/OP_TUCK
322+ // Splice / size (OP_CAT .. OP_SIZE, splice ops disabled)
323+ 0x7e , 0x7f , 0x80 , 0x81 , 0x82 , //
324+ // Bitwise / equality (OP_INVERT .. OP_EQUALVERIFY, bitwise ops disabled)
325+ 0x83 , 0x84 , 0x85 , 0x86 , 0x87 , 0x88 , // .. OP_EQUAL/OP_EQUALVERIFY
326+ // Numeric / arithmetic ops (OP_1ADD .. OP_WITHIN)
327+ 0x8b , 0x8c , 0x8d , 0x8e , 0x8f , 0x90 , 0x91 , 0x92 , 0x93 , 0x94 , //
328+ 0x95 , 0x96 , 0x97 , 0x98 , 0x99 , 0x9a , 0x9b , 0x9c , 0x9d , 0x9e , //
329+ 0x9f , 0xa0 , 0xa1 , 0xa2 , 0xa3 , 0xa4 , 0xa5 , // .. OP_WITHIN
330+ // Crypto
331+ 0xa6 , 0xa7 , 0xa8 , 0xa9 , 0xaa , // OP_RIPEMD160 ..= OP_HASH256
332+ 0xab , // OP_CODESEPARATOR
333+ 0xac , 0xad , // OP_CHECKSIG, OP_CHECKSIGVERIFY
334+ 0xae , 0xaf , // OP_CHECKMULTISIG, OP_CHECKMULTISIGVERIFY
335+ // Locktime / Taproot
336+ 0xb1 , 0xb2 , // OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY
337+ 0xba , // OP_CHECKSIGADD (tapscript)
338+ ] ;
339+
340+ let mut tokens = Tokens :: new ( ) ;
341+ tokens. add_tokens ( opcodes. iter ( ) . map ( |op| vec ! [ * op] ) ) ;
342+
343+ // Multi-byte structural templates: standard scriptPubKey shapes, witness program prefixes and
344+ // a few protocol-level constants. Inserting these whole lets the mutator land on standard
345+ // output types (and their near-misses) in one step.
346+ let templates: & [ & [ u8 ] ] = & [
347+ & [ 0x00 , 0x14 ] , // P2WPKH prefix: OP_0 <20-byte push>
348+ & [ 0x00 , 0x20 ] , // P2WSH prefix: OP_0 <32-byte push>
349+ & [ 0x51 , 0x20 ] , // P2TR prefix: OP_1 <32-byte push>
350+ & [ 0x76 , 0xa9 , 0x14 ] , // P2PKH head: OP_DUP OP_HASH160 <20-byte push>
351+ & [ 0x88 , 0xac ] , // P2PKH tail: OP_EQUALVERIFY OP_CHECKSIG
352+ & [ 0xa9 , 0x14 ] , // P2SH head: OP_HASH160 <20-byte push>
353+ & [ 0x87 ] , // P2SH tail: OP_EQUAL
354+ & [ 0xc0 ] , // Tapscript leaf version
355+ & [ 0x50 ] , // Taproot annex prefix
356+ & [ 0x00 , 0x01 ] , // segwit marker+flag
357+ ] ;
358+ tokens. add_tokens ( templates. iter ( ) . map ( |t| t. to_vec ( ) ) ) ;
359+
360+ tokens
361+ }
362+
291363pub struct LibAflByteMutator {
292364 state : StdState < NopCorpus < BytesInput > , BytesInput , StdRand , NopCorpus < BytesInput > > ,
293365}
294366
295367impl LibAflByteMutator {
296368 pub fn new ( ) -> Self {
297- let state = StdState :: new (
369+ let mut state = StdState :: new (
298370 StdRand :: new ( ) ,
299371 NopCorpus :: < BytesInput > :: new ( ) ,
300372 NopCorpus :: new ( ) ,
@@ -303,6 +375,9 @@ impl LibAflByteMutator {
303375 )
304376 . unwrap ( ) ;
305377
378+ // Make a Bitcoin Script dictionary available to the token mutators below.
379+ state. add_metadata ( bitcoin_script_dictionary ( ) ) ;
380+
306381 Self { state }
307382 }
308383}
@@ -311,7 +386,7 @@ impl fuzzamoto_ir::OperationByteMutator for LibAflByteMutator {
311386 fn mutate_bytes ( & mut self , bytes : & mut Vec < u8 > ) {
312387 let mut input = BytesInput :: from ( bytes. clone ( ) ) ;
313388
314- let mut mutator = HavocScheduledMutator :: new ( havoc_mutations ( ) ) ;
389+ let mut mutator = HavocScheduledMutator :: new ( havoc_mutations ( ) . merge ( tokens_mutations ( ) ) ) ;
315390 let _ = mutator. mutate ( & mut self . state , & mut input) ;
316391
317392 bytes. clear ( ) ;
0 commit comments