how does afl flips bits?

how does afl flips bits?

·

4 min read

I found this code while looking at AFL++ code and was curious to know how it worked

#define FLIP_BIT(_ar, _b)                   \
  do {                                      \
                                            \
    u8 *_arf = (u8 *)(_ar);                 \
    u32 _bf = (_b);                         \
    _arf[(_bf) >> 3] ^= (128 >> ((_bf)&7)); \
                                            \
  } while (0)

Starting from the bottom

((_bf)&7)

What is the magic 7 doing? try with some values

DEBUGPRINT[4]: lab.c:26: 0&7=0
DEBUGPRINT[4]: lab.c:26: 1&7=1
...
DEBUGPRINT[4]: lab.c:26: 7&7=7
DEBUGPRINT[4]: lab.c:26: 8&7=0
DEBUGPRINT[4]: lab.c:26: 9&7=1

not very insightful

16   : 10000
7    : 00111
16&7 : 00000  (0)
--------------------
17   : 10001
7    : 00111
17&7 : 00001  (1)
--------------------
...
--------------------
23   : 10111
7    : 00111
23&7 : 00111  (7)
--------------------
23   : 10111
8    : 01000
23&7 : 00000  (0)
--------------------

So it loops the values from 0 to 7. That seems extremely obvious after printing the binary numbers.

next:

(128 >> (_bf)&7)

128 is the first number that needs 8 bits to be represented, if we shift it we can get a mask to any bit we want.

Want 3rd highest:

10000000     (128)
00000010 >>  (2)
00100000     (32)

That gives us a mask with only the 3rd highest bit toggled.

shr is the same as number/(2**distance), its pretty intuitive if you think of the >> as <uint>/(2**<uint>) or like a function uint >>(uint, uint) divides arg1 by 2**arg2 and it works the same for << uint <<(uint, uint) multiplies arg1 by 2**arg2.

Interpreting shr using base 10 like above is probably not good, since if you try doing that with XOR, and probably other bitwise operations, you will not be able to in a simple way, like using only (+-/*). You can do it but it would take multiple lines of code. This might not be a problem for most people but I got stuck trying to figure out how to do that for a bit.

next, looking at everything together:

_arf[(_bf) >> 3] ^= (128 >> ((_bf)&7));

bf >> 3 has a clearer meaning now (bf / 8), but by itself that doesn't tell us anything.

So lets print out some stuff

a[0 >> 3] -> a[0]=41
a[1 >> 3] -> a[0]=41
...
a[7 >> 3] -> a[0]=41
a[8 >> 3] -> a[1]=41

not very helpful

(41) a[0] ^= 80 = c1
(41) a[0] ^= 40 = 1
(41) a[0] ^= 20 = 61
(41) a[0] ^= 10 = 51
(41) a[0] ^= 8 = 49
(41) a[0] ^= 4 = 45
(41) a[0] ^= 2 = 43
(41) a[0] ^= 1 = 40
(41) a[1] ^= 80 = c1
(41) a[1] ^= 40 = 1

worse

01000001   (65) "A"
10000000 ^ (128)
11000001   (193)
--------   (65)
01000001   (65) "A"
01000000 ^ (64)
00000001   (1)
--------   (65)
01000001   (65) "A"
00100000 ^ (32)
01100001   (97)
--------

Now i understand what this code is doing. By xor'ing with a power of 2 in the range 1 to 128, you can choose a bit to flip 1 being the lowest bit and 128 the highest in (big-endian ?)

I want to flip the highest bit:

01000001   (65)  "A"
10000000 ^ (128) 128 >> 0
11000001   (193)

What about the 4th bit:

01000001   (65)  "A"
00001000 ^ (8)   128 >> 4
01001001   (73)

pretty cool

why (_bf) >> 3 though?

_bf is used in both sides of the equation, on the right side it chooses which bit to flip and on the left it works as an index to the bitarray. I was interpreting _arf as an array of bytes that doesn't make sense in a BIT_FLIP macro, unless you want to pass both byte and bit to flip to the macro.

So _arf[(_bf) >> 3] is basically a getter for bits.

the rest is kinda boring

u8 *_arf = (u8 *)(_ar);
u32 _bf = (_b);

These are just aliases to uint8_t and uint32_t

do { ... } while (0)

explained here better already: macros

Did you find this article valuable?

Support shafouz by becoming a sponsor. Any amount is appreciated!