Get Thumbprints from Windows Catalog File on Linux

By | June 1, 2021

This article is related to the “Windows Catalog Files” post and describes how to parse catalog sis.cat file using posix c++ on Linux platform. On Windows as it is mentioned in “Windows Catalog Files” it is possible to retrieve thumbprints with mscat.h API. The Linux parsing method is based on searching positions of some OIDs in catalog file plus position of jsATTR1 attribute value (0x11010001) in js.cat file. The OIDs and attribute positions lead to location of thumbprint data (hash value and file name). These OID are 1.3.6.1.4.1.311.12.2.1 and 1.3.6.1.4.1.311.2.1.25. Of course it is not possible to find these OID strings in catalog file because they are ASN.1 encoded. Besides OIDs 0x11,1,0,1 byte sequence was used for parsing. There sequence represents jsATTR1 values in cdf.txt catalog definition file. (see “Windows Catalog Files” post).
The source code of catalog file parser is:


#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <sys/stat.h>
int main(int n, char ** s)
{
   // Binary presentation of 1.3.6.1.4.1.311.12.2.1
   const unsigned char oidFile[10] = {0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x0C,0x02,0x01};
   // Binary presentation of 1.3.6.1.4.1.311.2.1.25
   const unsigned char oidSHA1[10] = {0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x19};
   // jsATTR1=0x11010001
   const unsigned char attrValue[4] = {0x11,1,0,1};
   unsigned char * buffer = NULL;
   char sha1Str[64];
   char fileNameStr[128];
   if(n < 2)
   {
      printf("Argument missing:\nName of catalog file\n");
      return -1;
   }
   struct stat filestat;
   if(stat(s[1], &filestat) == 0)
   {
      if(filestat.st_size > 0)
      {
         buffer = new unsigned char[filestat.st_size];
         FILE * pfile = fopen(s[1], "r");
         if(pfile)
         {
            size_t size = fread (buffer, 1, filestat.st_size, pfile );
            pclose(pfile);
            void * pSha1Oid = buffer;
            void * pSha1OidStart = buffer;
            size_t searchsizeSha1 = filestat.st_size;

            void * pAttrOid = buffer;
            void * pAttrOidStart = buffer;
            size_t searchsizeAttr = filestat.st_size;
            while(pSha1Oid)
            {
               pSha1Oid = memmem(pSha1OidStart, searchsizeSha1, oidSHA1, sizeof(oidSHA1));
               if(pSha1Oid)
               {
                  pSha1OidStart = (void*)((long)pSha1Oid + sizeof(oidSHA1) + 18);
                  unsigned char sha1Len = *(unsigned char*)pSha1OidStart;
                  memset(sha1Str,0,64);
                  for(int i=0; i<(int)sha1Len; i++)
                  {
                     pSha1OidStart = (void*)((long)pSha1OidStart+1);
                     sprintf((sha1Str+2*i),"%02X",*(unsigned char*)pSha1OidStart);;
                  }
                  searchsizeSha1 = filestat.st_size - ((long)pSha1OidStart - (long)buffer);
                  printf("SHA1: %s\n", sha1Str);
                  // Find attribute (file name)
                  while(pAttrOid)
                  {
                     pAttrOid = memmem(pAttrOidStart, searchsizeAttr, oidFile, sizeof(oidFile));
                     if(pAttrOid)
                     {
                        pAttrOidStart = (void*)((long)pAttrOid + sizeof(oidFile));
                        pAttrOid = memmem(pAttrOidStart, searchsizeAttr, attrValue, sizeof(attrValue));
                        pAttrOidStart = (void*)((long)pAttrOid + sizeof(attrValue) + 1);
                        unsigned char fileNameLen = *(unsigned char*)pAttrOidStart;
                        memset(fileNameStr,0,128);
                        pAttrOidStart = (void*)((long)pAttrOidStart + 1);
                        for(int i=0; i<fileNameLen; i+=2)
                        {
                           fileNameStr[i/2] = *(char*)pAttrOidStart;
                           pAttrOidStart = (void*)((long)pAttrOidStart+2);
                        }
                        printf("File: %s\n", fileNameStr);
                        searchsizeAttr = filestat.st_size - ((long)pAttrOidStart - (long)buffer);
                     }
                     printf("\n");
                     break;
                  }
               }
            }
         }
      }
   } else {
      printf("No such file: %s\n", s[1]);
   }
   delete[] buffer;
   return 0;
}

Result:


# ./thumbprint /tmp/js.cat
SHA1: 041C68DB30A3EC437D0BB20AAA3562812C5BF130
File: wonderword.js

SHA1: 265A86A9D9DF5DFDBAD77E06C85605BEC4BEB32A
File: jquery.js

SHA1: 6A6E7B36874B0DB2926356296A0106862616733A
File: printable.js

SHA1: D4A71241C67C9C05BEB65BA34C9B6D90125A2473
File: initrows.js

SHA1: FAA9AF99112D1F87C0A35DD068A277D79DFEBA46
File: anchor.js

To make this parser some reverse engineering job has been done and this openssl command helped me significantly:


# openssl asn1parse -inform DER -in /tmp/js.cat

Leave a Reply

Your email address will not be published. Required fields are marked *