diff --git a/asm_unpackers/.gitignore b/asm_unpackers/.gitignore new file mode 100644 index 0000000..84c048a --- /dev/null +++ b/asm_unpackers/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/asm_unpackers/Makefile b/asm_unpackers/Makefile new file mode 100644 index 0000000..27f9982 --- /dev/null +++ b/asm_unpackers/Makefile @@ -0,0 +1,22 @@ +build/unpack_riscv64: ../c_unpacker/main.c ../c_unpacker/unpack.c + mkdir -p build + riscv64-linux-gnu-gcc -g -static -o $@ $^ + +test_riscv64: build/unpack_riscv64 + qemu-riscv64 $< test_data.upk /tmp/out.bin + cmp test_data.bin /tmp/out.bin + +build/unpack_armv6m: ../c_unpacker/main.c ../c_unpacker/unpack.c + mkdir -p build + arm-linux-gnueabihf-gcc -g -static -o $@ $? + +test_armv6m: build/unpack_armv6m + qemu-arm $< test_data.upk /tmp/out.bin + cmp test_data.bin /tmp/out.bin + +build/unpack_armv6m.bin: unpack_armv6m.S + arm-none-eabi-gcc -march=armv6-m -c -o build/unpack_armv6m.o $? + arm-none-eabi-objcopy -O binary --only-section=.text build/unpack_armv6m.o $@ + +sizes: build/unpack_armv6m.bin + ls -l build/*.bin \ No newline at end of file diff --git a/asm_unpackers/test_data.bin b/asm_unpackers/test_data.bin new file mode 100644 index 0000000..0b79206 --- /dev/null +++ b/asm_unpackers/test_data.bin @@ -0,0 +1,99 @@ +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; + +u8* upkr_data_ptr; +u8 upkr_probs[1 + 255 + 1 + 2*32 + 2*32]; +#ifdef UPKR_BITSTREAM +u16 upkr_state; +u8 upkr_current_byte; +int upkr_bits_left; +#else +u32 upkr_state; +#endif + +int upkr_decode_bit(int context_index) { +#ifdef UPKR_BITSTREAM + while(upkr_state < 32768) { + if(upkr_bits_left == 0) { + upkr_current_byte = *upkr_data_ptr++; + upkr_bits_left = 8; + } + upkr_state = (upkr_state << 1) + (upkr_current_byte & 1); + upkr_current_byte >>= 1; + --upkr_bits_left; + } +#else + while(upkr_state < 4096) { + upkr_state = (upkr_state << 8) | *upkr_data_ptr++; + } +#endif + + int prob = upkr_probs[context_index]; + int bit = (upkr_state & 255) < prob ? 1 : 0; + + int tmp = prob; + if(!bit) { + tmp = 256 - tmp; + } + upkr_state = tmp * (upkr_state >> 8) + (upkr_state & 255); + tmp += (256 - tmp + 8) >> 4; + if(!bit) { + upkr_state -= prob; + tmp = 256 - tmp; + } + upkr_probs[context_index] = tmp; + + return bit; +} + +int upkr_decode_length(int context_index) { + int length = 0; + int bit_pos = 0; + while(upkr_decode_bit(context_index)) { + length |= upkr_decode_bit(context_index + 1) << bit_pos++; + context_index += 2; + } + return length | (1 << bit_pos); +} + +void* upkr_unpack(void* destination, void* compressed_data) { + upkr_data_ptr = (u8*)compressed_data; + upkr_state = 0; +#ifdef UPKR_BITSTREAM + upkr_bits_left = 0; +#endif + for(int i = 0; i < sizeof(upkr_probs); ++i) + upkr_probs[i] = 128; + + u8* write_ptr = (u8*)destination; + + int prev_was_match = 0; + int offset = 0; + for(;;) { + if(upkr_decode_bit(0)) { + if(prev_was_match || upkr_decode_bit(256)) { + offset = upkr_decode_length(257) - 1; + if(offset == 0) { + break; + } + } + int length = upkr_decode_length(257 + 64); + while(length--) { + *write_ptr = write_ptr[-offset]; + ++write_ptr; + } + prev_was_match = 1; + } else { + int byte = 1; + while(byte < 256) { + int bit = upkr_decode_bit(byte); + byte = (byte << 1) + bit; + } + *write_ptr++ = byte; + prev_was_match = 0; + } + } + + return write_ptr; +} diff --git a/asm_unpackers/test_data.upk b/asm_unpackers/test_data.upk new file mode 100644 index 0000000..2622da0 Binary files /dev/null and b/asm_unpackers/test_data.upk differ diff --git a/asm_unpackers/unpack_armv6m.S b/asm_unpackers/unpack_armv6m.S new file mode 100644 index 0000000..87652d6 --- /dev/null +++ b/asm_unpackers/unpack_armv6m.S @@ -0,0 +1,172 @@ +.syntax unified +.thumb + +.section .text + +#define ALIGNUP(n, align) (((n) + (align) - 1) & ~((align) - 1)) +#define PROB_LEN (1 + 255 + 1 + 2*32 + 2*32) +#define DATA_OFF ALIGNUP(PROB_LEN, 4) + +// auto upkr_unpack(uint8_t * out, uint8_t * in) -> tuple +.global upkr_unpack +.type upkr_unpack, %function +// r0 .. out_ptr (returned) +// r1 .. in_ptr (returned) +// r2 .. state +// r3 .. offset +// r4 .. prev_was_match +// r5 .. subroutine arg (preserved) +// r6 .. subroutine ret +// r7 .. probs ptr +upkr_unpack: + push { r4, r5, r6, r7, lr } + sub sp, sp, #DATA_OFF + + mov r7, sp + movs r2, #255 + adds r2, r2, #(PROB_LEN - 255) + movs r3, #128 +.Lclear: + subs r2, r2, #1 + strb r3, [r7, r2] + bne .Lclear + +.Lloop: + movs r5, #0 + bl upkr_decode_bit + beq .Ldata +.Lmatch: + // r6 = 1 + lsls r5, r6, #8 + cmp r4, #0 + mov r4, r6 // = 1 + bne 1f + + bl upkr_decode_bit + beq 2f + +1: + adds r5, r5, #1 + bl upkr_decode_length + subs r5, r5, #1 + // r4 = 1 + subs r3, r4, r6 + beq .Lend +2: + + adds r5, r5, #65 + bl upkr_decode_length +.Lcopy_loop: + ldrb r5, [r0, r3] + strb r5, [r0] + adds r0, r0, #1 + subs r6, r6, #1 + bne .Lcopy_loop + b .Lloop + +.Ldata: + movs r4, #0 + movs r5, #1 + +.Ldata_loop: + bl upkr_decode_bit + lsls r5, r5, #1 + adds r5, r5, r6 + lsrs r6, r5, #8 + beq .Ldata_loop + + strb r5, [r0] + adds r0, r0, #1 + b .Lloop + +.Lend: + add sp, sp, #DATA_OFF + pop { r4, r5, r6, r7, pc } + +.type upkr_decode_length, %function +// r0 .. length tmp (saved) +// r1 .. +// r2 .. +// r3 .. +// r4 .. bit pos (saved) +// r5 .. content index (saved) +// r6 .. length (returned) +// r7 .. +upkr_decode_length: + push { r0, r4, r5, lr } + + movs r4, #0 + movs r0, #0 +.Lbit_loop: + bl upkr_decode_bit + beq 1f + + adds r5, r5, #1 + bl upkr_decode_bit + adds r5, r5, #1 + lsls r6, r6, r4 + adds r4, r4, #1 + orrs r0, r0, r6 + b .Lbit_loop +1: + movs r6, #1 + lsls r6, r6, r4 + orrs r6, r6, r0 + + pop { r0, r4, r5, pc } + +.type upkr_decode_bit, %function +// r0 .. tmp / prob (saved) +// r1 .. out_ptr (modified) +// r2 .. state (modified) +// r3 .. scratch (saved) +// r4 .. +// r5 .. content index (preserved) +// r6 .. bit (returned) +// r7 .. probs ptr (preserved) +upkr_decode_bit: + push { r0, r3, lr } + +.Lstate_loop: + lsrs r3, r2, #12 + bne 1f + lsls r2, r2, #8 + ldrb r6, [r1] + adds r1, r1, #1 + orrs r2, r2, r6 + b .Lstate_loop +1: + + ldrb r0, [r7, r5] + + lsrs r3, r2, #8 + uxtb r2, r2 + + subs r6, r2, r0 + lsrs r6, r6, #31 + bne 1f + + subs r2, r2, r0 + rsbs r0, r0, #0 + uxtb r0, r0 +1: + + muls r3, r3, r0 + adds r2, r2, r3 + + rsbs r3, r0, #0 + uxtb r3, r3 + adds r3, r3, #8 + lsrs r3, r3, #4 + adds r0, r0, r3 + + cmp r6, #0 + bne 1f + + rsbs r0, r0, #0 +1: + + strb r0, [r7, r5] + + cmp r6, #0 + pop { r0, r3, pc } diff --git a/c_unpacker/main.c b/c_unpacker/main.c index d9d9c9a..828fc66 100644 --- a/c_unpacker/main.c +++ b/c_unpacker/main.c @@ -1,7 +1,7 @@ #include #include -int upkr_unpack(void* destination, void* compressed_data); +void* upkr_unpack(void* destination, void* compressed_data); int main(int argn, char** argv) { void* input_buffer = malloc(1024*1024); @@ -13,7 +13,8 @@ int main(int argn, char** argv) { printf("Compressed size: %d\n", in_size); - int out_size = upkr_unpack(output_buffer, input_buffer); + void* end_ptr = upkr_unpack(output_buffer, input_buffer); + int out_size = (char*)end_ptr - (char*)output_buffer; printf("Uncompressed size: %d\n", out_size); diff --git a/c_unpacker/unpack.c b/c_unpacker/unpack.c index c893d56..0b79206 100644 --- a/c_unpacker/unpack.c +++ b/c_unpacker/unpack.c @@ -57,7 +57,7 @@ int upkr_decode_length(int context_index) { return length | (1 << bit_pos); } -int upkr_unpack(void* destination, void* compressed_data) { +void* upkr_unpack(void* destination, void* compressed_data) { upkr_data_ptr = (u8*)compressed_data; upkr_state = 0; #ifdef UPKR_BITSTREAM @@ -95,5 +95,5 @@ int upkr_unpack(void* destination, void* compressed_data) { } } - return write_ptr - (u8*)destination; + return write_ptr; }