un metodo empirico, ma abbastanza buono, sarebbe quello di implementare una serie di "filtri".
Ciascun file binario ha un particolare header, che contiene spesso dei magic numbers, informazioni sulla lunghezza del file e altri dati. Beh, quasi tutti...
Il metodo consiste nell'implementare, ad esempio, un certo numero di lettori di headers (ad esempio uno per i file pcx, uno per i jpg, uno per gli avi, ecc...), lanciarli tutti verso un certo file, e contare il numero di "errori" o incongruenze che vengono rilevati.
Ad esempio, se ho un file exe, mi aspetto che da qualche parte nell'header ci sia la sequenza di caratteri MZ (appena all'inizio dell'header), un campo che mi dice la lunghezza totale del file, ecc... Se testando un file con il filtro per gli exe non trovo la sequenza MZ, allora posso scartare l'ipotesi dell'exe e testare con il prossimo filtro (magari potrebbe essere una jpg o altro...) finchè non trovo un filtro che mi dice "Hey, questo file è un XXX valido!"
L'inconveniente di questo metodo è che devi studiare ed implementare i filtri di lettura di vari tipi di formati diversi, implementando correttamente tutti gli eventuali check di integrità (alcuni usano CRC, altri usano un banale checksum, altri ancora potrebbero avere metodi customizzati, ad esempio nelle immagini PCX devi controllare che base x altezza x bytePerPixel corrispondano al dataSize, e questi sono tutti dati presenti nell'header).
Nel vecchio AmigaOS questo problema era risolto dal meccanismo dei Datatypes. Un componente centralizzato del sistema operativo conteneva tutti i filtri per tutti (o quasi) i tipi di files gestiti dal sistema, e ogni programma, invece di implementare N volte il codice per leggere un certo tipo di file, poteva chiamare le routine di apertura e salvataggio per quel file direttamente dal Datatype del sistema operativo (stiamo parlando di tecnologia avanzata, eh! cose che windows, linux e mac si sognano a distanza di ormai più di 20 anni!).
Spero di averti dato qualche spunto utile per i tuoi esperimenti
ciao!