ebCTF 2013: "MD5 Colliding" 400 pts

Writeup author: Radu Caragea


Task text:

Please upload 5 Windows console executable files with the same MD5 but with different printed outputs (file type: MS Windows, PE32 executable, console)

The output for the files should be:
File1: All Eindbazen are wearing wooden shoes
File2: All Eindbazen live in a windmill
File3: All Eindbazen grow their own tulips
File4: All Eindbazen smoke weed all day
File5: All Eindbazen are cheap bastards

NOTE: The goal of this challenge is NOT to attack this webservice, this is NOT a WEB challenge.

First of all I'm not really a crypto person so I learned a lot during this challenge which, unfortunately, I didn't manage to finish during the contest.
The first attempt was to use some sort of derivative of the evilize proof-of-concept tool http://www.mscs.dal.ca/~selinger/md5collision/ which produces 'good' and 'evil' binaries that each do something else by replacing some blocks inside the binary. It didn't seem to scale for more than 2 outputs so I had to find something else. Hashclash http://code.google.com/p/hashclash/ seemed like a good alternative but it took about 4 hours for one collision so this clearly wasn't the intended solution.
The approach that yielded success was the one presented at http://cryptography.hyperlink.cz/MD5_collisions.html, which can produce blocks that collide into the same MD5.

The usefulness of collision blocks is that, because of the M-D construction, you have the following:

If MD5(BlockA) = MD5(BlockB)  (even if SHA1(BlockA) != SHA1(BlockB) )
Given any binary data D: MD5(BlockA + D) = MD5(BlockB + D) (where + is the concatenation operator)

(This means that we could create an executable and append it to the 2 collision blocks and have 2 new executables with the same MD5 but unfortunately these wouldn't respect the PE format so they wouldn't run)

However, the converse is not true:

If MD5(BlockA) = MD5(BlockB)  (even if SHA1(BlockA) != SHA1(BlockB) )
Given any binary data D, usually: MD5(D + BlockA) != MD5(D + BlockB) (where + is the concatenation operator)

Why is this the case? The reason is that the collision blocks were created using an IV that does not coincide with the md5 digest of the data D. Fortunately, we can give the program that creates collisions arguments to set up the IV. This means that we can have 2 executables that are identical except for a final appended block (but still have the same MD5). To setup the IV from a file I used the following:

partial_md5.c
#include <stdio.h>
#include <openssl/md5.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <fcntl.h>
 
int main(int argc, char **argv)
{
	int fin;
	struct stat st;
	void *p = NULL;
	MD5_CTX ctx;
	unsigned char final[16];
 
	if (argv[1] == NULL) {
		printf("./partial_md5 </path/to/file>\n");
		exit(-1);
	}
 
	fin = open(argv[1], O_RDONLY);
	if (fin < 0 ) {
		printf("Can't open %s\n", argv[1]);
		exit(-1);
	}
	stat(argv[1], &st);
	if (st.st_size % 64 != 0) {
		printf("Filesize is not a multiple of blocksize (64B)!\n");
		printf("Pad it with %ld more bytes!\n", 64 - (st.st_size % 64) );
		exit(-1);
	}
 
	p = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fin, 0);
	if (!p) {
		printf("Mmap failed\n");
		exit(-1);
	}
 
	MD5_Init(&ctx);
	MD5_Update(&ctx, p, st.st_size);
 
	printf("Partial MD5 is %08X %08X %08X %08X\n", ctx.A, ctx.B, ctx.C, ctx.D);
	MD5_Final(final, &ctx);
 
	printf("Final MD5 is %08X %08X %08X %08X\n", ctx.A, ctx.B, ctx.C, ctx.D);
 
	return 0;
}

This can be made to scale relatively easily:

Given any binary data D:

Generate 2 collision block suffixes: A1 and A2:
    MD5(D + A1) =
    MD5(D + A2)
Generate 2 more collision block suffixes: B1 and B2:
    MD5(D + A1 + B1) = 
    MD5(D + A2 + B1) = 
    MD5(D + A1 + B2) = 
    MD5(D + A2 + B2)
    (once the md5 compression function is applied to either D+A1 or D+A2 it yields the same thing
Generate 2 more collision block suffixes C1 and C2:
    MD5(D + A1 + B1 + C1) = 
    MD5(D + A2 + B1 + C1) = 
    MD5(D + A1 + B2 + C1) = 
    MD5(D + A2 + B2 + C1) = 
    MD5(D + A1 + B1 + C2) = 
    MD5(D + A2 + B1 + C2) = 
    MD5(D + A1 + B2 + C2) = 
    MD5(D + A2 + B2 + C2)

So you can have N collisions with logN collision block generation calls.
A quick and dirty solution is to create an executable that prints the requested strings according to the executable name. Uploading this to the site will satisfy the conditions: different SHA1 sums but equal MD5 sums. However, there is no guarantee that the executables preserve their given names.

A more complex solution that actually leads to something resembling the 'evilize' tool is one that makes the collisions inside the binary rather than at the end.

base.c
#include <stdio.h>
        /* this initialization doesn't work with cl.exe, only gcc. */
        //unsigned char buff[] = { [0 ... 4095] = 0x69};
 
	unsigned char buff[4096] = "iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii";
	int magic_pos1 = 0xDEADBEEF;
	int magic_pos2 = 0xDEADCAFE;
	int magic_pos3 = 0xDEADBABE;
 
 
	int magic_byte1 = 0x31415925;
	int magic_byte2 = 0x12344321;
	int magic_byte3 = 0xABCDDABC;
 
int main()
{
	int *buffer = (int *)buff;
	if(buffer[magic_pos1] == magic_byte1 && buffer[magic_pos2] == magic_byte2 && buffer[magic_pos3] == magic_byte3 )
		puts("All Eindbazen are wearing wooden shoes");
	if(buffer[magic_pos1] == magic_byte1 && buffer[magic_pos2] == magic_byte2 && buffer[magic_pos3] != magic_byte3 )
		puts("All Eindbazen live in a windmill");
	if(buffer[magic_pos1] == magic_byte1 && buffer[magic_pos2] != magic_byte2 && buffer[magic_pos3] == magic_byte3 )
		puts("All Eindbazen grow their own tulips");
	if(buffer[magic_pos1] == magic_byte1 && buffer[magic_pos2] != magic_byte2 && buffer[magic_pos3] != magic_byte3 )
		puts("All Eindbazen smoke weed all day");
 
	if(buffer[magic_pos1] != magic_byte1 && buffer[magic_pos2] == magic_byte2 && buffer[magic_pos3] == magic_byte3 )
		puts("All Eindbazen are cheap bastards");
 
	if(buffer[magic_pos1] != magic_byte1 && buffer[magic_pos2] == magic_byte2 && buffer[magic_pos3] != magic_byte3 )
		puts("Mos Craciun si prietenii sai");
 
	if(buffer[magic_pos1] != magic_byte1 && buffer[magic_pos2] != magic_byte2 && buffer[magic_pos3] == magic_byte3 )
		puts("To be or not to be");
 
	if(buffer[magic_pos1] != magic_byte1 && buffer[magic_pos2] != magic_byte2 && buffer[magic_pos3] != magic_byte3 )
		puts("That is the question");
 
	return 0;
}

Note: magic_byte should actually be magic_dword, initially I didn't have everything scripted
Having a large enough buffer means that we can insert the three collision blocks inside it after compile time without any issues. The binary is thus split in three parts:

The key here is that we can use the previous approach to create 8 signatures that collide into the same MD5 when appended to the prefix. After that we can add absolutely anything to the suffix (or modify it) and the collisions are preserved. This is where the magic values come in handy, we can select what output to print by replacing them with suitable values.

root@dmns:x86_64 [example] # ./split_replace.py base.exe
First three arguments should be </path/to/main_exec> <signature byte (e.g 69)> <signature byte count>
root@dmns:x86_64 [example] # ./split_replace.py base.exe 69 4096
Wrote base.exe.prefix
Now run ./suffix_collision.sh base.exe.prefix
 
root@dmns:x86_64 [example] # ../suffix_collision.sh base.exe.prefix
[*] Creating two suffix blocks that collide into the same MD5 for the prefix [base.exe.prefix]
Partial MD5 is D9C1930D E11B6193 AE34C75F C4072C43
Final MD5 is 312D1F08 8A337C8E 67FABB21 66F2D2B0
[*] Removing previous collisions
[*] Starting collision search
 
Init vector : 0xD9C1930D,0xE11B6193,0xAE34C75F,0xC4072C43
 
Start from X=344122AB ...
Generating block 1 ...
 
 The first block collision took  : 1.070000 sec
Block 1 to disk: 0
Block 2 to disk: 0
 
 The second block collision took  : 0.080000 sec
 The first and the second blocks together took : 1.150000 sec
Generation completed.
mkdir: cannot create directory 'base.exe.prefix.collision': File exists
[*] Done.
[*] Testing to see if it actually worked
First difference is at offset X and has value Y
-0000010: 8b7dbe4c  .}.L
e667a52e91b05ae5306638f9d4261289  base.exe.prefix.collision/binary_A
e667a52e91b05ae5306638f9d4261289  base.exe.prefix.collision/binary_B
 
 
 
root@dmns:x86_64 [example] # ../suffix_collision.sh base.exe.prefix.collision/binary_A
[*] Creating two suffix blocks that collide into the same MD5 for the prefix [base.exe.prefix.collision/binary_A]
Partial MD5 is 8299ACA2 B8587451 89614383 6C1B057D
Final MD5 is 2EA567E6 E55AB091 F9386630 891226D4
[*] Removing previous collisions
[*] Starting collision search
 
Init vector : 0x8299ACA2,0xB8587451,0x89614383,0x6C1B057D
 
Start from X=34624688 ...
Generating block 1 ...
 
 The first block collision took  : 4.780000 sec
Block 1 to disk: 0
Block 2 to disk: 0
 
 The second block collision took  : 0.590000 sec
 The first and the second blocks together took : 5.370000 sec
Generation completed.
[*] Done.
[*] Testing to see if it actually worked
First difference is at offset X and has value Y
-0000010: c52e5463  ..Tc
66eddd0eb32a970940025189fe461bf1  base.exe.prefix.collision/binary_A.collision/binary_A
66eddd0eb32a970940025189fe461bf1  base.exe.prefix.collision/binary_A.collision/binary_B
 
 
root@dmns:x86_64 [example] # ../suffix_collision.sh base.exe.prefix.collision/binary_A.collision/binary_A
[*] Creating two suffix blocks that collide into the same MD5 for the prefix [base.exe.prefix.collision/binary_A.collision/binary_A]
Partial MD5 is 9C2E3256 5B66CB52 9EDC29AC 38CBB1DF
Final MD5 is 0EDDED66 09972AB3 89510240 F11B46FE
[*] Removing previous collisions
[*] Starting collision search
 
Init vector : 0x9C2E3256,0x5B66CB52,0x9EDC29AC,0x38CBB1DF
 
Start from X=34836A64 ...
Generating block 1 ...
 
 The first block collision took  : 1.890000 sec
Block 1 to disk: 0
Block 2 to disk: 0
 
 The second block collision took  : 2.700000 sec
 The first and the second blocks together took : 4.590000 sec
Generation completed.
[*] Done.
[*] Testing to see if it actually worked
First difference is at offset X and has value Y
-0000010: f75d56da  .]V.
b5146c88229f4f401b780cade203a639  base.exe.prefix.collision/binary_A.collision/binary_A.collision/binary_A
b5146c88229f4f401b780cade203a639  base.exe.prefix.collision/binary_A.collision/binary_A.collision/binary_B

This has created 3 pairs of suffixes. The first one differed from the second at position 0x10, having the value 8b7dbe4c, the next one at position 0x10, having the value c52e5463 and the third one at position 0x10, having the value f75d56da. The blocks have each 128 bytes == 0x80, so the actual offsets from the beginning of the buffer are:

What remains is just a simple pattern search and replace:

root@dmns:x86_64 [example] # ../multiplex.sh 8b7dbe4c c52e5463 f75d56da
mkdir: cannot create directory 'output': File exists
Signature left is now 3968 bytes long
Wrote prefix + collision block + rest of signature to output/A__
Signature left is now 3968 bytes long
Wrote prefix + collision block + rest of signature to output/B__
Signature left is now 3840 bytes long
Wrote prefix + collision block + rest of signature to output/AA_
Signature left is now 3840 bytes long
Wrote prefix + collision block + rest of signature to output/AB_
Signature left is now 3840 bytes long
Wrote prefix + collision block + rest of signature to output/BB_
Signature left is now 3840 bytes long
Wrote prefix + collision block + rest of signature to output/BA_
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/AAA
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/AAB
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/ABA
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/ABB
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/BAA
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/BAB
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/BBA
Signature left is now 3712 bytes long
Wrote prefix + collision block + rest of signature to output/BBB
Magic patching output/AAA
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/AAB
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/ABA
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/ABB
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/BAA
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/BAB
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/BBA
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da
Magic patching output/BBB
Patching efbeadde with 04000000
Patching fecaadde with 24000000
Patching bebaadde with 44000000
Patching 25594131 with 8b7dbe4c
Patching 21433412 with c52e5463
Patching bcdacdab with f75d56da

And the final verification:

root@dmns:x86_64 [example] # for i in `ls output/*magic_fixed`; do md5sum $i; done
73ed3d82cbdcb61ff41e1791e28c91e9  output/AAA.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/AAB.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/ABA.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/ABB.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/BAA.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/BAB.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/BBA.magic_fixed
73ed3d82cbdcb61ff41e1791e28c91e9  output/BBB.magic_fixed
root@dmns:x86_64 [example] # for i in `ls output/*magic_fixed`; do wine $i; done
All Eindbazen are wearing wooden shoes
All Eindbazen live in a windmill
All Eindbazen grow their own tulips
All Eindbazen smoke weed all day
All Eindbazen are cheap bastards
Mos Craciun si prietenii sai
To be or not to be
That is the question