Proprietary File Analysis: Keystream Recovery and Resource Extraction

Classified in Computers

Written on in English with a size of 5.58 KB

C Implementation for Proprietary File Decryption (The Glide Utility)

This C source code outlines a utility, likely named glide, designed to analyze and decrypt data stored in a proprietary file format (possibly a PWL file). The process involves reading the file, recovering parts of the encryption keystream based on known offsets and user input, and then decrypting embedded resources.

Core Data Structures and Global Declarations

The program utilizes several global arrays to manage file data, the derived keystream, and resource pointers.

unsigned char Data[100001];
unsigned char keystream[1001];
int Rpoint[300];
  • Data: Stores the entire content of the input file.
  • keystream: Holds the recovered encryption key stream used for XOR decryption.
  • Rpoint: An array storing the starting offsets of the resources within the Data buffer.

Main Execution Flow and Argument Handling

The main function handles command-line arguments, ensuring the filename is provided, and optionally accepting a username, which is crucial for keystream generation.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Note: getch() is non-standard and often requires conio.h

// Global Data Buffers
unsigned char Data[100001];
unsigned char keystream[1001];
int Rpoint[300];

int main(int argc, char *argv[]) {
    FILE *fd;
    int i, j;
    int size;
    char ch;
    char *name;
    int cracked;
    int sizemask;
    int maxr = 0; // Initialize maxr
    int rsz;
    int pos;
    int Rall[300] = {0}; /* Resource allocation table (initialized to zero) */

    if (argc < 2) {
        printf("Usage: glide filename (username)\n");
        exit(1);
    }

    /* Read PWL file */
    fd = fopen(argv[1], "rb");
    if (fd == NULL) {
        printf("Cannot open file %s\n", argv[1]);
        exit(1);
    }

    size = 0;
    while (!feof(fd)) {
        Data[size++] = fgetc(fd);
    }
    size--; // Correct for the extra read attempt that hit EOF
    fclose(fd);

    /* Find Username */
    name = argv[1];
    if (argc > 2) {
        name = argv[2];
    }
    printf("Username: %s\n", name);

    /* Copy encrypted text into keystream */
    cracked = size - 0x0208;
    if (cracked < 0) {
        cracked = 0;
    }
    if (cracked > 1000) {
        cracked = 1000;
    }
    memcpy(keystream, Data + 0x208, cracked);

    /* Generate 20 bytes of keystream by XORing with username */
    for (i = 0; i < 20; i++) {
        ch = toupper(name[i]);
        if (ch == 0) break;
        if (ch == '.') break;
        keystream[i] ^= ch;
    }
    cracked = 20;

    /* Find allocated resources */
    // Calculate sizemask (little-endian 16-bit value)
    sizemask = keystream[0] + (keystream[1] << 8);
    printf("Sizemask: %04X\n", sizemask);

    // Populate Resource Allocation Table (Rall)
    for (i = 0; i < 256; i++) {
        if (Data[i] != 0xff) {
            Rall[Data[i]]++;
            if (Data[i] > maxr) {
                maxr = Data[i];
            }
        }
    }

    // Resource pointer table size appears to be divisible by 16
    maxr = (((maxr / 16) + 1) * 16);

    /* Search for resources */
    Rpoint[0] = 0x0208 + 2 * maxr + 20 + 2; /* First resource pointer offset */

    // Iterate through potential resources up to maxr
    for (i = 0; i < maxr; i++) {
        /* Find the size of the current resource */
        pos = Rpoint[i];
        // Read 16-bit size (little-endian)
        rsz = Data[pos] + (Data[pos + 1] << 8);
        rsz ^= sizemask;

        printf("Analyzing block with size: %04x (%d:%d)\n", rsz, i, Rall[i]);

        if ((Rall[i] == 0) && (rsz != 0)) {
            printf("Unused resource has nonzero size!!!\n");
            printf("If the last line produced any: You may try to recover.\n");
            printf("Press 'y' to attempt the recovery: ");
            ch = getch(); // Non-standard function call
            printf("\n");

            if (ch != 'y' && ch != 'Y') {
                exit(0);
            }
            rsz = 2;
            i = i - 1; // Re-process the current index
        }

        pos = pos + rsz;

        /* Resources have a tendency to have the wrong size for some reason. */
        /* Check for correct size */
        if (i) { // Skip check for the first resource (i=0)
            while (Data[pos + 3] != keystream[1]) {
                printf(":%02X", Data[pos + 3]); // Print byte value
                pos = pos + 2; /* Very rude, may fail */
            }
        }
        pos += 2; /* Include pointer in size */
        Rpoint[i + 1] = pos;
    }

    Rpoint[maxr] = size;

    /* Insert Table data into keystream */
    for (i = 0; i <= maxr; i++) {
        // XOR low byte
        keystream[20 + 2 * i] ^= Rpoint[i] & 0x00ff;
        // XOR high byte
        keystream[21 + 2 * i] ^= (Rpoint[i] >> 8) & 0x00ff;
    }
    cracked += maxr * 2 + 2;
    printf("%d bytes of keystream recovered.\n", cracked);

    /* Decrypt resources */
    // Iterate up to maxr (the number of resource pointers calculated)
    for (i = 0; i < maxr; i++) {
        rsz = Rpoint[i + 1] - Rpoint[i];
        if (rsz > cracked) {
            rsz = cracked;
        }
        printf("Resource[%d] (Size: %d):\n", i, rsz);

        // Decrypt and print resource content
        for (j = 0; j < rsz; j++) {
            printf("%c", Data[Rpoint[i] + j] ^ keystream[j]);
        }
        printf("\n");
    }
    exit(0);
}

Related entries: