6

I am trying to reverse engineer my HP printer's firmware, so I dumped the SPI chip from the board, and there are a lot of strings, but almost always look something like this: enter image description here

I noticed a pattern that sometimes after every 8 bytes FF byte is added. However, this is not the whole pattern. Does anyone have a clue what could this be? It is not the dump error since I extracted some JPEG images from uncompressed parts just fine, and multiple dumps produce the same file.

UPDATE: I figured it could be LZSS compression, and I managed to decompress this part and got this. However, when I tried the same script on the other part which has the same pattern it does not decompress properly. What could be the reason?

I used the QuickBMS script for LZSS algorithm (code below). Ideally, I would like to do it in python, but the scripts I found did not produce the good result even with the same LZSS parameters. I am not super familiar with QuickBMS scripting.

# lzss decompression function written in 100% bms scripting

set NAME string "unpacked.dat"
get ZSIZE asize
math SIZE = ZSIZE
math SIZE *= 10
log MEMORY_FILE 0 ZSIZE
callfunction LZSS_BMS_DUMP

# you must set: MEMORY_FILE (input), ZSIZE (input size), MEMORY_FILE2 (output), SIZE (max size)
startfunction LZSS_BMS_DUMP
    set EI long 12
    set EJ long 4
    set P long 2
    set rless long P
    set init_chr long 0x00

    set N long 1
    math N <<= EI
    set F long 1
    math F <<= EJ

    # pre-allocate memory for faster performances
    putvarchr MEMORY_FILE3 N 0
    for i = 0 < N
        putvarchr MEMORY_FILE3 i init_chr
    next i
    putvarchr MEMORY_FILE2 SIZE 0

    math r = N
    math r -= F
    math r -= rless
    math N -= 1
    math F -= 1

    math src = 0
    math dst = 0
    math srcend = ZSIZE
    math dstend = SIZE

    math flags = 0
    for src = 0 < srcend
        if flags & 0x100
        else
            getvarchr flags MEMORY_FILE src
            math src += 1
            math flags |= 0xff00
        endif
        if flags & 1
            getvarchr c MEMORY_FILE src
            math src += 1
            putvarchr MEMORY_FILE2 dst c
            math dst += 1
            putvarchr MEMORY_FILE3 r c
            math r += 1
            math r &= N
        else
            getvarchr i MEMORY_FILE src
            math src += 1
            getvarchr j MEMORY_FILE src
            math src += 1
            math TMP = j
            math TMP >>= EJ
            math TMP <<= 8
            math i |= TMP
            math j &= F
            math j += P
            for k = 0 <= j
                math TMP = i
                math TMP += k
                math TMP &= N
                getvarchr c  MEMORY_FILE3 TMP
                putvarchr MEMORY_FILE2 dst c
                math dst += 1
                putvarchr MEMORY_FILE3 r c
                math r += 1
                math r &= N
            next k
        endif
        math flags >>= 1
    next
    math SIZE = dst
    log NAME 0 SIZE MEMORY_FILE2
endfunction
  • 6
    That is typical for per-character compression such as LZMA (if I remember my abbreviations correct). It was a natural next step up from basic RLE (where a leading value or one or more bits indicate what to repeat). The next step was to compress bits, and in such a case you would not be able to find plain text anymore. – usr2564301 Feb 5 '18 at 23:33
  • 1
    Thanks for pointing me in the right direction! It seems to be LZSS actually. I managed do decompress one part of the file apparently correct. But some other parts with the same compression don't decompress correctly. I think I haven't figured out the exact parameters or it could be some LZSS variant... – hlex Feb 6 '18 at 17:20
  • 1
    Perhaps you could show your current code in your question. It may help if you put in a link to the data as well, so others don't have to re-type it from your screenshot. – usr2564301 Feb 6 '18 at 17:45
  • Yes, good point, here it is! – hlex Feb 6 '18 at 18:40
  • Can't actually download the 1st sample part in your post, the link isn't set to public. – TkTech Feb 7 '18 at 6:47
1

A couple of years of experience later, I revisited this project and figured it out! The compression is in fact LZSS, and the QuickBMS script from my question is able to decompress it correctly. The second section I mentioned was problematic just because there are uncompressed blocks in between compressed blocks in the chip dump, so, obviously, they need to be extracted and decompressed separately.

If anyone is interested in understanding LZSS properly, I really liked the way it was explained in the book "Retrogame Archeology: Exploring Old Computer Games", page 104 (can be viewed for free on Google books).

Thanks all for helping, I could not have figured it out without an amazing community here! :)

| improve this answer | |
  • 1
    you should accept your own answer. – 0xC0000022L May 29 at 19:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.