Tascam US-4x4HR

USB audio interface #

US-2x2HR and US-4x4HR models should be very similar. But probably multiple changes over the last generation (US-2x2). Let’s see if we can look at the firmware updater and fw image.

initial poking #

The ‘settings’ program requests a json, https://teac-global.com/download/fw/us-hr/US-HR_UpdateInformation.json, but the links contained there are broken ? Or I am missing some request magic and get punished with a 404. Downloading a fw updater from the european website works better. The .exe clearly contains the firmware image.

Using Resource Hacker: finds only icons

Looking around in the disasm, it’s probably some Qt resource format. One tool found, https://github.com/axstin/qtextract , not tested.

Running fw updater, see hint about version ‘110’ and ‘build 16’. In About screen, see large License text file that I recall seeing in disasm. In ghidra, that chunk is easily found, as well as its length. Just nearby, clearly some kind of resource section:

        004119c4 78              ??         78h    x
        004119c5 c3              ??         C3h
        004119c6 00              ??         00h
        004119c7 72              ??         72h    r
        004119c8 00              ??         00h
        004119c9 65              ??         65h    e
        004119ca 00              ??         00h
        004119cb 73              ??         73h    s
        004119cc 00              ??         00h
        004119cd 0b              ??         0Bh                                              len(filename?)
        004119ce 03              ??         03h
        004119cf d9              ??         D9h
        004119d0 f4              ??         F4h
        004119d1 b4              ??         B4h
        004119d2 00              ??         00h
        004119d3 4c 00 49        unicode    u"LICENSE.txt"
                 00 43 00 
                 45 00 4e 
        004119e9 0c              db         Ch                                               len(filename) ?
        004119ea 07              ??         07h
        004119eb fa              ??         FAh
        004119ec 19              ??         19h
        004119ed be              ??         BEh
        004119ee 00              ??         00h
        004119ef 75 00 70        unicode    u"update44.bin"
                 00 64 00 
                 61 00 74 

followed by the contents of those 2 resources:

                             lic_txt_content                                 XREF[2]:     0040c953(*), 0040eeb3(*)  
        00411a40 00 00 76 a4     ddw        76A4h                                            sizeof content (bigendian)
        00411a44 3d 3d 3d        char[303   "=============================================
                 3d 3d 3d 
                 3d 3d 3d 
        004190e8 00 02 30 20     ddw        23020h                                           length of blob
                             update44_bin_blob
        004190ec 55 53 2d        db[143392]
                 34 78 34 
                 48 52 00 

fw blob parsing #

ok, found some error messages relating to checksum errors which lead to:

bVar4 = parse_fw_blob?((int)(param_1 + 0x53d),(int)param_1[0x4053d]);

not sure how the blob data ends up in those args. Was not successful in navigating Qt/C++ mess, but ‘param_1’ would be a large class, holding a static buffer where the file would be copied.

parse_fw_blob is just a wrapper around parse_fw_blob2 (my excellent nomenclature…). In there, some important information.

//extract blob size
  uVar3 = (uint)param_1[0x15] << 0x10 | (uint)param_1[0x14] << 0x18 | (uint)param_1[0x17] |
          (uint)param_1[0x16] << 0x8;
  if (uVar3 == 0x0) {
    printf("%s:%d:ERROR\n","../../ustest/prgdecode.c",0x9d);
    return 0x1;
  }

Ah, we’re reconstructing a big-endian u32; if I look at those offsets in the extracted blob, I find the value 0x23020 which matches its filesize and resource size. Good.

More hints around there indicate that the blob has a 0x20 byte header. My best guess currently:

struct {
    char[8] devicename;	// "US-4x4HR"
    u16_be version;	//0x6E = 110
    u16_be build;	//0x10 = 16
    u32_be unknown_field;	//0x7e50c15 ; not clear what that refers to.
    u32_be filesize;	// big-endian
    u8 padding[];
} //sizeof = 0x20

possible ‘decryption’ #

In parse_fw_blob2() :

copy_junk((int)_Memory + 0x20,(int)(param_1 + 0x20),file_size - 0x20);

// what's that ?

void __cdecl copy_junk(byte *dest,byte *src,int len) {
  int idx;
  idx = 0x0;
  if (0x0 < len) {
    do {
      dest[idx] = (&DAT_00411680)[src[idx]];
      idx = idx + 0x1;
    } while (idx != len);
  }

A look-up table ? how quaint. Let’s see what’s at 411680

                             BYTE_ARRAY_00411680                             XREF[1]:     copy_junk:0040b8a8(*)  
        00411680 00 cc c1        db[256]
                 c2 c3 cd 
                 ce cf c4 
           00411680 [0]             0h, CCh, C1h, C2h,
           00411684 [4]            C3h, CDh, CEh, CFh,
           00411688 [8]            C4h, C5h, C6h, C7h,
           0041168c [12]           C8h, C9h, CAh, CBh,
           00411690 [16]           20h, 2Ah, 2Bh, 2Ch,
	   .....

Hmm, I don’t recognize that pattern, but it doesn’t really matter. Cook one small python script, essentially:

decrypt_lut = b'\x00\xcc\xc1 .....
decrypted = bytes(map(lambda x:decrypt_lut[x], fw_blob))

the firmware is now “decrypted”, as confirmed by low entropy, as well as the presence of many promising strings :

..\src\device\cs4272.c:%d:ERROR
 -- Runtime Assertion
..\src\device\usbaudio.c:1705 dsp->desc_pos == dspin->put_pos
... etc

My brain doesn’t recognize the instruction set, but cpu_rec claims ‘Blackfin’. This is believable since there is some online mentions of other tascam models (US-16x08, many DR recorders etc) using blackfin DSP / mcu. This is in contrast to the previous-generation model US-2x2 which was based on a LPC1820, ARM cortex-m3.

Anyway I have no business with the firmware itself at the moment, and ghidra doesn’t have native support for blackfin. There is one independant implementation here : https://github.com/sualk/ghidra-blackfin (haven’t tested)

fw blob checksum(s) ? #

Also in parse_fw_blob2():

    iVar3 = 0x0;
    sum = 0x0;
    do {
      ofs = iVar3 + 0x20;
      iVar3 = iVar3 + 0x1;
      sum = sum + *(byte *)((int)_Memory + ofs);
    } while (fw_actual_size != iVar3);

simple sum of all bytes, skipping the 0x20-byte header.