#include <sys/mman.h> // For mmap(2)
#include <sys/stat.h> // For stat(2)
#include <unistd.h> // For everything else
#include <fcntl.h> // O_RDONLY
#include <stdio.h> // printf!
#include <string.h> // str*, mem*
#include <stdlib.h> // exit..
/**
* Imagine: A rudimentary (decrypted) img3 file format dumper,
* With specific focus on device tree files
*
*
* (No, this will NOT decrypt the files - you'll need xpwntool or other
* utility to do that)
*
* Coded by Jonathan Levin - http://www.newosxbook.com
*
* Possible improvements:
* - Refactor into a library
* - Tidy up the (very dirty) code
* - Show tree values, not just names (left as an exercise)
*
*/
typedef unsigned int uint32_t;
#include "dt.h" // for DeviceTree
typedef struct img3 {
uint32_t magic;
uint32_t fullSize;
uint32_t sizeNoPack;
uint32_t sigCheckArea;
uint32_t ident;
} img3;
typedef struct tag {
uint32_t magic;
uint32_t total_length;
uint32_t data_length;
unsigned char data[0];
} tag;
#define IMG3_MAGIC 0x496d6733
#define TAG_TYPE 0x54595045
#define TAG_DATA 0x44415441
#define TAG_VERS 0x56455253
#define TAG_SEPO 0x5345504f
#define TAG_CHIP 0x43484950
#define TAG_BORD 0x424f5244
#define TAG_KBAG 0x4b424147
#define TAG_SHSH 0x53485348
#define TAG_CERT 0x43455254
#define TYPE_DTRE 0x65727464
int g_Dump = 0;
void
dump (unsigned char *data, int len)
{
int i;
for (i = 0 ; i < len; i++)
{
printf ("%02x ", data[i]);
}
printf ("\n");
}
void copyValue (char *dest, char *src, int length)
{
char temp[1024];
int i = 0;
for (i = 0; src[i] || i < length; i++);
if (i != length){ strcpy(dest, "(null)"); return;}
memcpy(dest, src,length);
}
uint32_t
dumpTreeNode(DeviceTreeNode *Node, int indent)
{
char buffer[40960];
char temp[10240];
char *name;
int prop = 0, child = 0;
int i = 0;
memset(buffer, '\0', 4096);
DeviceTreeNodeProperty *dtp = (DeviceTreeNodeProperty * ) ((char*)Node + sizeof(DeviceTreeNode));
char *offset = 0;
for (prop = 0; prop < Node->nProperties; prop++)
{
char *val;
temp[0] = '\0'; // strcat will do the rest
for (i=0; i< indent ; i++) { strcat(temp,"| "); }
strcat (temp, "+--");
strncat (buffer, temp, 1024);
sprintf (temp, "%s %d bytes: ", dtp->name, dtp->length);
strncat (buffer, temp, 1024);
if (strcmp(dtp->name,"name") == 0)
{
name = (char *) &dtp->length + sizeof(uint32_t);
strncat(buffer, name, dtp->length);
strcat (buffer,"\n");
}
else
{
copyValue (temp, ((char *) &dtp->length) + sizeof(uint32_t), dtp->length);
// Yeah, Yeah, Buffer overflows, etc.. :-)
strcat (buffer, temp);
strcat(buffer, "\n");
}
dtp = ((char *) dtp) + sizeof(DeviceTreeNodeProperty) + dtp->length ;
// Align
dtp = (((long) dtp %4) ? ((char *) dtp) + (4 - ((long)dtp) %4) : dtp);
offset = (char *) dtp;
}
for (i=0; i< indent-1; i++) { printf(" "); }
if (indent>1) printf ("+--");
printf ("%s:\n", name);
printf (buffer);
// Now do children:
for (child = 0; child < Node->nChildren; child++)
{
offset+= dumpTreeNode ( (DeviceTreeNode *) offset, indent+1 );
}
return ( (char *) offset - (char*) Node);
}
void
doData (char *data, int tag, int len)
{
printf ("\tData of type 0x%x and length %d bytes\n", tag, len);
switch (tag)
{
case TYPE_DTRE:
{
DeviceTreeNode *dtn = (DeviceTreeNode *) data;
DeviceTreeNode *root = (DeviceTreeNode *) data;
int prop = 0;
if (dtn->nProperties > 20)
{
printf ("\tMore than 20 properties? Did you hand me an encrypted file?\n");
return;
}
printf ("\tDevice Tree with %d properties and %d children\n",
dtn->nProperties, dtn->nChildren);
if (g_Dump)
{
printf ("Properties:\n");
dumpTreeNode (dtn,1);
}
else { printf("\tUse -d to dump the device tree\n");}
}
}
}
int
main(int argc, char **argv)
{
struct stat stbuf;
char *filename;
int rc;
int fd;
int filesize;
char *mmapped;
img3 *img3Header;
tag *tag;
char ident[5];
char type[5];
int i;
// Usage/arguments could be better. This is just a simple quick and dirty
// example. Excuse my brevity..
if (argc < 2)
{ fprintf (stderr,"Usage: %s [-d] _filename_\n", argv[0]); exit(0);}
if (strcmp(argv[1], "-d") == 0) { g_Dump++; }
filename = argv[argc -1];
rc = stat(filename, &stbuf);
if (rc == -1) { perror (filename); exit (1); }
filesize = stbuf.st_size;
fd = open (filename, O_RDONLY);
if (fd < 0) { perror (filename); exit(2);}
mmapped = mmap(NULL,
filesize, // size_t len,
PROT_READ, // int prot,
MAP_SHARED | MAP_FILE, // int flags,
fd, // int fd,
0); // off_t offset);
if (!mmapped) { perror ("mmap"); exit(3);}
img3Header = (img3 *) mmapped;
if (img3Header->magic != IMG3_MAGIC)
{
fprintf(stderr,"%s is not an IMG3 file!\n", filename);
exit(1);
}
ident[4] ='\0';
for (i = 0; i < 4; i++)
{
ident[i] = * (((char *)&(img3Header->ident)) + 3-i);
}
printf ("Ident: %s\n", ident);
tag = (struct tag *) (mmapped + sizeof(img3));
while ( ((char *)tag) - ((char *) mmapped) < filesize ) {
for (i = 0; i < 4; i++)
{
ident[i] = * (((char *)&(tag->magic)) + 3-i);
}
printf ("Tag: %s (%x) Length 0x%x\n", ident, tag->magic, tag->total_length);
switch (tag->magic)
{
case TAG_TYPE:
printf ("\tType: ");
for (i = 0; i < 4; i++)
{
type[i] = * (((char *)&(tag->data)) + 3-i);
}
printf ("%s\n", type);
break;
case TAG_BORD:
printf ("\tBoard: ");
dump (tag->data,tag->data_length);
break;
case TAG_VERS:
printf ("\tVersion: ");
printf ("%s\n", tag->data + 4);
break;
case TAG_SEPO:
printf ("\tSecurity Epoch: ");
dump (tag->data,tag->data_length);
break;
case TAG_CHIP:
printf ("\tChip: ");
dump (tag->data,tag->data_length);
break;
case TAG_DATA:
doData(tag->data, *((int *) type), tag->data_length);
break;
default:
break;
}
tag = (( (char *) tag) + (tag->total_length));
}
return 0;
}