*Description: MD5_ChecksumAsUnpackingKey demonstrates that a checksum can be used as an unpacking key for code executed later (in this case, for the MD5 program). =========================================================== *Usage: MD5_ChecksumAsUnpackingKey.exe Output: The MD5 value of Abraham Lincoln's Gettysburg Address. =========================================================== *Note: This program uses the same source to compute an MD5 hash as the other samples. Much of the MD5 computation takes place in the execution of the unsigned char array code_buf. code_buf is an array of hex values which, when unpacked correctly, form the executable code of the original MD5 program. Since it may seem suspicious to use source code with binary code already in it, it should be noted that code_buf can be replaced with any array of binary code. To use another program instead of the MD5 program, use an “inlined” version of it. Meaning any function calls in the source code should be directly replaced by the function’s code; in addition, any global variables should be moved into the function body. These measures are all taken to ensure there are no absolute references, so that the code produced is effectively portable. After that, convert the code to a hex string. This can be done by compiling the function’s code, then manually going through each instruction in a debugger and copying its opcode into the hex array. Similarly, the output of “objdump -d” may be used to obtain the instruction encodings. Or a simple routine can be written in the source which parses the function code from beginning to end, then produces its hex string in the appropriate format. In the samples, the MD5 code was produced from http://people.csail.mit.edu/rivest/Md5.c. The function MDString() in our source was assembled from “inlining” the original source code into one function. Then code_buf was produced as mentioned above, from parsing MDString() from beginning to end using C labels. =========================================================== *MD5_ChecksumAsUnpackingKey algorithm: In the case of MD5_ChecksumAsUnpackingKey, each byte in code_buf has been incremented. The unpacking routine simply decrements each byte. However, MD5_ChecksumAsUnpackingKey performs a checksum on code_buf first before unpacking it. Thus, code_buf is passed from main() to checksumUnpacker(), where each byte is read and added to the checksum counter. The counter has been intentionally initialized to -274997 so that after correctly checksumming code_buf it adds up to 1. Then, this value of 1 is used to unpack code_buf, as mentioned above, by decrementing each byte. The intuition is that if code_buf is undisturbed, then the sum will come out to 1, and the code will unpack correctly. However if code_buf has been tampered, then the sum should not come out to 1, and the code will unpack incorrectly. After unpacking, execution is passed back to main(), where code_buf is casted to a function pointer then called. =========================================================== *Related work: J. Cappaert, B. Preneel, B. Anckaert, M. Madou, and K. De Bosschere. Towards tamper resistant code encryption: Practice and experience. In Information Security Practice and Experience, pages 86–100. Springer, 2008. P. Wang, S. Kim, and K. Kim. Tamper resistant software through dynamic integrity checking. In Proc. 2005 Symposium on Cryptography and Information Security (SCIS2005), Jan. 2005.