testdisk/src/file_sig.c

464 lines
10 KiB
C

/*
File: file_sig.c
Copyright (C) 2010 Christophe GRENIER <grenier@cgsecurity.org>
This software is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write the Free Software Foundation, Inc., 51
Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#if !defined(SINGLE_FORMAT) || defined(SINGLE_FORMAT_sig)
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdio.h>
#include <ctype.h>
#if defined(__FRAMAC__)
#include "__fc_builtin.h"
#endif
#include "types.h"
#include "filegen.h"
#include "common.h"
#include "log.h"
static void register_header_check_sig(file_stat_t *file_stat);
const file_hint_t file_hint_sig= {
.extension="custom",
.description="Own custom signatures",
.max_filesize=PHOTOREC_MAX_FILE_SIZE,
.recover=1,
.enable_by_default=1,
.register_header_check=&register_header_check_sig
};
#define WIN_PHOTOREC_SIG "\\photorec.sig"
#define DOT_PHOTOREC_SIG "/.photorec.sig"
#define PHOTOREC_SIG "photorec.sig"
typedef struct signature_s signature_t;
struct signature_s
{
struct td_list_head list;
const char *extension;
unsigned char *sig;
unsigned int sig_size;
unsigned int offset;
};
static signature_t signatures={
.list = TD_LIST_HEAD_INIT(signatures.list)
};
#ifndef __FRAMAC__
static
#endif
int signature_cmp(const struct td_list_head *a, const struct td_list_head *b)
{
const signature_t *sig_a=td_list_entry_const(a, const signature_t, list);
const signature_t *sig_b=td_list_entry_const(b, const signature_t, list);
int res;
if(sig_a->sig_size==0 && sig_b->sig_size!=0)
return -1;
if(sig_a->sig_size!=0 && sig_b->sig_size==0)
return 1;
res=sig_a->offset-sig_b->offset;
if(res!=0)
return res;
if(sig_a->sig_size<=sig_b->sig_size)
{
res=memcmp(sig_a->sig,sig_b->sig, sig_a->sig_size);
if(res!=0)
return res;
return 1;
}
else
{
res=memcmp(sig_a->sig,sig_b->sig, sig_b->sig_size);
if(res!=0)
return res;
return -1;
}
}
static void signature_insert(const char *extension, unsigned int offset, unsigned char *sig, unsigned int sig_size)
{
/* FIXME: small memory leak */
signature_t *newsig=(signature_t*)MALLOC(sizeof(*newsig));
newsig->extension=extension;
newsig->sig=sig;
newsig->sig_size=sig_size;
newsig->offset=offset;
td_list_add_sorted(&newsig->list, &signatures.list, signature_cmp);
}
static int header_check_sig(const unsigned char *buffer, const unsigned int buffer_size, const unsigned int safe_header_only, const file_recovery_t *file_recovery, file_recovery_t *file_recovery_new)
{
struct td_list_head *pos;
td_list_for_each(pos, &signatures.list)
{
const signature_t *sig = td_list_entry(pos, signature_t, list);
if(memcmp(&buffer[sig->offset], sig->sig, sig->sig_size)==0)
{
reset_file_recovery(file_recovery_new);
file_recovery_new->extension=sig->extension;
return 1;
}
}
return 0;
}
static FILE *open_signature_file(void)
{
#if defined(__CYGWIN__) || defined(__MINGW32__)
{
char *path;
path = getenv("USERPROFILE");
if (path == NULL)
path = getenv("HOMEPATH");
if(path!=NULL)
{
FILE*handle;
char *filename=NULL;
filename=(char*)MALLOC(strlen(path)+strlen(WIN_PHOTOREC_SIG)+1);
strcpy(filename, path);
strcat(filename, WIN_PHOTOREC_SIG);
handle=fopen(filename,"rb");
if(handle!=NULL)
{
log_info("Open signature file %s\n", filename);
free(filename);
return handle;
}
free(filename);
}
}
#endif
#ifndef DJGPP
{
const char *home = getenv("HOME");
if (home != NULL)
{
FILE*handle;
char *filename=(char*)MALLOC(strlen(home)+strlen(DOT_PHOTOREC_SIG)+1);
strcpy(filename, home);
strcat(filename, DOT_PHOTOREC_SIG);
handle=fopen(filename,"rb");
if(handle!=NULL)
{
log_info("Open signature file %s\n", filename);
free(filename);
return handle;
}
free(filename);
}
}
#endif
{
FILE *handle=fopen(PHOTOREC_SIG,"rb");
if(handle!=NULL)
{
log_info("Open signature file %s\n", PHOTOREC_SIG);
return handle;
}
}
return NULL;
}
static char *str_uint(char *src, unsigned int *resptr)
{
unsigned int res=0;
if(*src=='0' && (*(src+1)=='x' || *(src+1)=='X'))
{
for(src+=2;;src++)
{
if(*src>='0' && *src<='9')
res=res*16+(*src)-'0';
else if(*src>='A' && *src<='F')
res=res*16+(*src)-'A'+10;
else if(*src>='a' && *src<='f')
res=res*16+(*src)-'a'+10;
else
{
*resptr=res;
return src;
}
}
}
else
{
for(;*src>='0' && *src<='9';src++)
res=res*10+(*src)-'0';
*resptr=res;
return src;
}
}
static char *parse_signature_file(file_stat_t *file_stat, char *pos)
{
const unsigned int signatures_empty=td_list_empty(&signatures.list);
while(*pos!='\0')
{
/* skip comments */
while(*pos=='#')
{
while(*pos!='\0' && *pos!='\n')
pos++;
if(*pos=='\0')
return pos;
pos++;
}
/* each line is composed of "extension offset signature" */
{
char *extension;
unsigned int offset=0;
unsigned char *tmp=NULL;
unsigned int signature_max_size=512;
unsigned int signature_size=0;
{
const char *extension_start=pos;
while(*pos!='\0' && !isspace(*pos))
pos++;
if(*pos=='\0')
return pos;
*pos='\0';
extension=strdup(extension_start);
pos++;
}
/* skip space */
while(isspace(*pos))
pos++;
/* read offset */
pos=str_uint(pos, &offset);
/* read signature */
tmp=(unsigned char *)MALLOC(signature_max_size);
while(*pos!='\n' && *pos!='\0')
{
if(signature_size==signature_max_size)
{
unsigned char *tmp_old=tmp;
signature_max_size*=2;
tmp=(unsigned char *)realloc(tmp, signature_max_size);
if(tmp==NULL)
{
free(extension);
free(tmp_old);
return pos;
}
}
if(isspace(*pos) || *pos=='\r' || *pos==',')
pos++;
else if(*pos== '\'')
{
pos++;
if(*pos=='\0')
{
free(extension);
free(tmp);
return pos;
}
else if(*pos=='\\')
{
pos++;
if(*pos=='\0')
{
free(extension);
free(tmp);
return pos;
}
else if(*pos=='b')
tmp[signature_size++]='\b';
else if(*pos=='n')
tmp[signature_size++]='\n';
else if(*pos=='t')
tmp[signature_size++]='\t';
else if(*pos=='r')
tmp[signature_size++]='\r';
else if(*pos=='0')
tmp[signature_size++]='\0';
else
tmp[signature_size++]=*pos;
pos++;
}
else
{
tmp[signature_size++]=*pos;
pos++;
}
if(*pos!='\'')
{
free(extension);
free(tmp);
return pos;
}
pos++;
}
else if(*pos=='"')
{
pos++;
for(; *pos!='"' && *pos!='\0'; pos++)
{
if(signature_size==signature_max_size)
{
unsigned char *tmp_old=tmp;
signature_max_size*=2;
tmp=(unsigned char *)realloc(tmp, signature_max_size);
if(tmp==NULL)
{
free(extension);
free(tmp_old);
return pos;
}
}
if(*pos=='\\')
{
pos++;
if(*pos=='\0')
{
free(extension);
free(tmp);
return pos;
}
else if(*pos=='b')
tmp[signature_size++]='\b';
else if(*pos=='n')
tmp[signature_size++]='\n';
else if(*pos=='r')
tmp[signature_size++]='\r';
else if(*pos=='t')
tmp[signature_size++]='\t';
else if(*pos=='0')
tmp[signature_size++]='\0';
else
tmp[signature_size++]=*pos;
}
else
tmp[signature_size++]=*pos;;
}
if(*pos!='"')
{
free(extension);
free(tmp);
return pos;
}
pos++;
}
else if(*pos=='0' && (*(pos+1)=='x' || *(pos+1)=='X'))
{
pos+=2;
while(isxdigit(*pos) && isxdigit(*(pos+1)))
{
unsigned int val=(*pos);
if(*pos>='0' && *pos<='9')
val-='0';
else if(*pos>='A' && *pos<='F')
val=val-'A'+10;
else if(*pos>='a' && *pos<='f')
val=val-'a'+10;
pos++;
val*=16;
val+=(*pos);
if(*pos>='0' && *pos<='9')
val-='0';
else if(*pos>='A' && *pos<='F')
val=val-'A'+10;
else if(*pos>='a' && *pos<='f')
val=val-'a'+10;
pos++;
tmp[signature_size++]=val;
}
}
else
{
free(extension);
free(tmp);
return pos;
}
}
if(*pos=='\n')
pos++;
if(signature_size>0)
{
/* FIXME: Small memory leak */
unsigned char *signature=(unsigned char *)MALLOC(signature_size);
log_info("register a signature for %s\n", extension);
memcpy(signature, tmp, signature_size);
register_header_check(offset, signature, signature_size, &header_check_sig, file_stat);
if(signatures_empty)
signature_insert(extension, offset, signature, signature_size);
}
else
{
free(extension);
}
free(tmp);
}
}
return pos;
}
static void register_header_check_sig(file_stat_t *file_stat)
{
char *pos;
char *buffer;
size_t buffer_size;
struct stat stat_rec;
FILE *handle;
handle=open_signature_file();
if(!handle)
return;
#ifdef __FRAMAC__
buffer_size=1024*1024;
#else
if(fstat(fileno(handle), &stat_rec)<0 || stat_rec.st_size>100*1024*1024)
{
fclose(handle);
return;
}
buffer_size=stat_rec.st_size;
#endif
buffer=(char *)MALLOC(buffer_size+1);
if(fread(buffer,1,buffer_size,handle)!=buffer_size)
{
fclose(handle);
free(buffer);
return;
}
fclose(handle);
#if defined(__FRAMAC__)
Frama_C_make_unknown(buffer, buffer_size);
#endif
buffer[buffer_size]='\0';
pos=buffer;
pos=parse_signature_file(file_stat, pos);
if(*pos!='\0')
{
log_warning("Can't parse signature: %s\n", pos);
}
free(buffer);
}
#endif