Newsgroup: it.comp.lang.c++
Titolo: [slightly OT] meta-check

Sareste in grado, in linea di principio, di scrivere un programma che, 
senza accedere ai file, stampi l'hash (per. es. l'SHA512) del proprio 
(intero) eseguibile?
(Se a questo punto si accedesse al file eseguibile, si sarebbe in grado 
di controllarne l'integritą.)
Kiuhnm

 di controllarne l'integrità.)
L'assunzione che del file eseguibile così come presente sul disco ne
esista una esatta copia in memoria credo sia sbagliata. Il program
loader usa il file eseguibile per creare immagini nella memoria per il
programma. Molti byte saranno identici tra file e memoria, ma altri no.
In linea di principio, dovresti prendere gli indirizzi di tutte le
funzioni, sapere quanto sono "lunghe" in termini di byte, fare i chksum
di quelle aree di memoria, sperare che non ci siano state relocation al
loro interno durante il caricamento, e fare la stessa cosa per i
contenuti degli stessi simboli definiti in .text del tuo eseguibile.
Questo per solo la parte di codice. Per le altre sezioni, non ne ho
idea.
In pratica, no.
Saluti,
alef

Kiuhnm ha scritto:

Devi farlo offline.
Crei una zona di memoria che inizi con byte difficilmente presenti 
naturalmente, es. "####@@@##@" seguita da una serie di zeri (quelli che 
ti servono per l'hash).
La zona iniziale serve per individuare il punto in cui scrivere l'hash 
(preferibilmente tramite altro programma, sicuramente non direttamente 
nel codice, a meno di prove iniziali).
Calcoli l'hash del file e lo scrivi direttamente nell'eseguibile.
Durante l'esecuzione hai il puntatore a quella zona e quindi sai qual'č 
l'hash precalcolato, accedi al file e rifai il calcolo (tenendo conto 
ovviamente dell'aggiunta dell'hash).
Per inciso, č una tecnica che uso per cifrare/decifrare file di testo 
abbinati ad un programma, dove al posto del tuo hash ho la chiave di 
decodifica.
Calcolare l'hash in memoria durante l'esecuzione non ha molto senso. 
Ogni modifica al binario te la ritrovi in memoria.
Ciao

 nel codice, a meno di prove iniziali).
No, si vuole l'hash dell'*intero* file. Se scrivi l'hash sul file, 
allora devi considerare anche tale stringa nel calcolo dell'hash.
Kiuhnm

 In pratica, no.
Ok, questo approccio è da scartare. Ma sei sicuro che sia l'unico?
Kiuhnm

Kiuhnm ha scritto:

nel binario iniziale la zona va inizializzata a zero (o qualunque valore 
si voglia).
es. per 8 byte, inizializzazione a zero
char Hash[18] = "####@@@##@";
es. per 8 byte, inizializzazione con ' '
char Hash[] = "####@@@##@        ";
Si calcola l'hash sul binario (tramite programma esterno).
Si scrive nel binario, alla posizione predisposta, l'hash.
A runtime basta caricare il file in memoria, rirovare la zona dell'hash 
e ripristinarla al valore iniziale, poi calcolare.
Ciao

Kiuhnm ha scritto:

forse non mi ero espresso bene :-)
scritta nel codice come seconda compilazione, dopo aver calcolato l'hash 
(non toccando nulla e sperando che il compilatore non cambi qualcosa)

Kiuhnm ha scritto:

Rispondendo anche all'altro post: avevo capito che volessi controllare
l'integrità dell'immagine in memoria dell'eseguibile con quella del
file eseguibile dal quale il processo è partito.
Invece qui mi pare di capire che:
1. produci un eseguibile
2. ne fai il cksum
3. lanci l'eseguibile
4. in main() rifai il cksum del file eseguibile
5. verifichi 4 con 2
Il tuo problema è dove mettere 2. logicMind ti ha suggerito un
approccio, che prevede
1. produci un eseguibile
2. ne fai il cksum
3. metti 2 in un area del file eseguibile vuota e/o predisposta
4. in main() rifai il cksum del file eseguibile togliendo prima 2
dall'area predisposta
5. verifichi 4 con 2
Quindi il calcolo di 2 non contiene 2 stesso. Quando fai il cksum devi
filtrare il pattern e rimpiazzare l'area successiva con zeri o altri di
prefissato.
L'approccio che vedo usato in giro è avere un file di testo contenente
hash e nomi dei file.
Comunque, prova a vedere se magari la tua piattaforma prevede già
qualcosa di simile. Mi pare che Windows ti permetta di farlo, con
quella storia dei certificati negli eseguibili, quindi forse risolvi il
problema delegandolo.
Saluti,
alef

 (non toccando nulla e sperando che il compilatore non cambi qualcosa)
L'hash diventa invalido non appena lo scrivi nel file.
Qui si parla di hash dell'*intero* file (cioč di ogni suo singolo bit).
Kiuhnm

alef wrote:
[...]
Stai commettendo lo stesso errore di logicMind.
Kiuhnm

Kiuhnm ha scritto:

non capisco se stai ragionando in termini filosofici o č un problema 
reale da risolvere.
E' logico che, nel momento in cui si va a modificare il binario, l'hash 
precedentemente calcolato non sia piů valido per il nuovo binario.
Ma basta riportare il binario alle condizioni iniziali (leggi il file in 
memoria e ripristini la zona su cui era scritto l'hash al volore iniziale).
Oppure, come ha suggerito alef, metti il tutto su un file esterno.
Ma, probabilmente, stai cercando di fare qualcosa con la 
metaprogrammazione. Un programma che si autogenera e, quindi, si calcola 
da solo il proprio hash?

 
In linea di principio, forse si', ma non sono sicuro che l'immagine (del 
processo) in memoria corrisponda esattamente a quella su file.
Suppongo che cio' dipenda dal sistema operativo (ergo l'argomento 
sarebbe OT).
-- 
Roal Zanazzi

 reale da risolvere.
Prendilo come un piccolo rompicapo. Non è un mio problema reale (questo 
spiega la presenza del tag OT).
Kiuhn

 sarebbe OT).
Assumiamo pure (a causa di restrizioni poste dal sistema operativo) che 
il programma non sia in grado di leggere la propria immagine.
Kiuhnm

Kiuhnm ha scritto:

tu hai la possibilità di emettere codici macchina in linea (come 
faceva la vecchia __emit(hexadecimal byte) ?
Se ce l'hai, potresti provare una cosa (che tanto non funge, he 
he he he ... vabbè ci provo lo stesso). Marcare inizio e fine 
del codice di una funzione con codici macchina innoqui, tipo 
"NOP", in numero noto, oppure "DB...." (istruzioni di 
allocazione assemply, con sequenza di byte che rappresentano 
aree di dati nel segmento codice).
La prima è innoqua, NOP non causa nessuna istruzione, la seconda 
dipende (dipende dal fatto che il compilatore codifichi l'entry 
point della funzione a monte o a valle del tuo codice macchina 
emesso in linea : se lo considera a valle, tutto OK, 
l'esecuzione non lo riguarda, altrimenti, gli effetti dipendono 
dai codici che metti).
Questo in teoria vorrebbe circoscrivere i corpi delle funzioni 
con zone di memoria riconoscibili, dal contenuto noto a priori.
Il mio notevole dubbio è questo : si può assumere che 
immediatamente dopo la graffa di apertura del function body e
immediatamente prima della graffa di chiusura del function body 
il compilatore non abbia inserito alcuna istruzione trasparente 
al programmatore ? Beh, penserei di no, ma se fosse si, allora 
potresti effettivamente tracciare il layout delle varie funzioni 
in memoria. Suggerivo __emit perchè scrive inline nel codice 
(.CODE) invece le dichiarazioni di dati (locali, static, etc) a 
livello sorgente il compilatore presumo le compatti e le 
disponga dove meglio crede (segmento stack, .DATI, etc etc).
Almeno in parte qualcosa di simile si può fare. Anni fa allocavo 
spazio dati con emit, ci copiavo dei parametri e chiamavo delle 
funzioni (che il compilatore vedeva prive di parametri) che 
usavano questi parametri passati a mano. Così, non serviva a 
niente, volevo solo scrivere una cosa ricorsiva che non 
esaurisse lo stack.
Non so se sarà possibile adattarlo al tuo problema, che è più 
stringente.
Ciao
Soviet_Mario

Kiuhnm ha scritto:

Non conosco la matematica che c'è dietro all'algoritmo di hashing ma se 
fosse interrompibile/frazionabile, cioè del tipo:
f(tot) == f(a) + f(b) + f(c)
allora si potrebbe precalcolare la prima parte fino alla zona prefissata 
a contenere gli hash parziali, la terza parte dal termine della zona 
fino alla fine del file e calcolare la zona a runtime.
Col brute force non sarebbe fattibile, specie con 64 byte di chiave.
Comunque, capisco la tua mente matematica (e ti invidio :-)) e forse per 
te sono cose ovvie, ma un pelino meno sibillino? :-)
Non sarò una cima ma non mi reputo nemmeno una schiappa, eppure ci ho 
messo un po' a capire il senso delle tue parole (es. che il controllo di 
corrispondenza viene fatto esternamente e non dall'eseguibile stesso).
Ciao

 la vecchia __emit(hexadecimal byte) ?
[...]
E poi ti lamenti che "astrai" poco? :)
Guarda fin dove sei sprofondato... __emit???
Pensa a una soluzione senza fare riferimento né al linguaggio né al 
sistema operativo. Pensa più in generale.
Kiuhnm

 corrispondenza viene fatto esternamente e non dall'eseguibile stesso).
Il check non fa parte del problema. Il programma deve semplicemente 
stampare il proprio hash.
Ho aggiunto tra parentesi che a quel punto si potrebbe, se fosse 
permesso accedere al file, fare un controllo, ma questa parte puoi 
tranquillamente ignorarla... (forse avrei fatto meglio a non scriverla).
Kiuhnm

 spiega la presenza del tag OT).
Quindi stai parlando di un programma che fa solo quello, non di un
accessorio ad uno generico.
alef

 accessorio ad uno generico.
(continuo)
... e che inoltre deve solo stamparlo, quindi forse nemmeno calcolarlo.
Quindi di un numero, una costante generate dal compilatore stesso.
Magari attraverso un metaprogramma.
alef


Spero solo non sia un gioco di parole perché in nessun momento hai
scritto che deve calcolare l'hash di se stesso mi pare, ma solo
stamparlo =) Pertanto ho la soluzione: un programma che lo avvi, ti
chiede il proprio hash come input e poi lo stampa :DDDD Per calcolarlo
ovviamente faccio "md5 programma.exe" :D Come vedi neanche un accesso a
un solo file da parte del programma, a meno che consideri STDIN un file :D
Andrea.

 un solo file da parte del programma, a meno che consideri STDIN un file :D
Ma così non è deterministico :D
Idea simpatica, ma no, non offenderei mai la vostra intelligenza con un 
quesito del genere. Non c'è l'inganno.
Kiuhnm

alef ha scritto:

Ok. Stai proponendo una variante di quelle cose tipo quine program.
Auguro buon diverimento agli altri.
Ciao.

Kiuhnm ha scritto:

Probabilmente sono io che scontrandomi più con la realtà pensavo in 
termini di trovare una soluzione più o meno comoda al problema :-)
Se
  f(tot) != f(a) + f(b) + f(c)
se non si può leggere dall'immagine né dal file,
se non credo che si possa fare a compile time (il compilatore e/o il 
linker potrebbero spostare i byte a loro piacimento)
resta la scrittura post link.
Ripeto che non conosco l'algoritmo, ma l'altra soluzione che mi viene in 
mente è quella di avere due zone predisposte. Nella prima ci andrà il 
risultato del calcolo, nella seconda dei valori che annullino i valori 
precedenti.
E mi arrendo :-)
Ciao

 ... e che inoltre deve solo stamparlo
Esatto, non si richiede altro.
Kiuhnm

 Ok. Stai proponendo una variante di quelle cose tipo quine program.
Che tra l'altro non conosco...
Kiuhnm

 Auguro buon diverimento agli altri.
Sì, squalificato!!! ;)
Kiuhnm

 di controllarne l'integrità.)
E' possibile creare una funzione f definita come segue:
f(buffer, str)
{
   scrive in buffer del codice sorgente C++
   di una funzione g che scriva la stringa str
   in un buffer.
   La funzione g avrà la forma
   g(buffer)
   {
     str = "......";
     scrive str in buffer;
   }
}
Il codice sorgente del programma P contiene due parti: A e B.
La parte A è costituita da una funzione g che restituisce (in un buffer) 
il codice C++ di B. Tale funzione viene generata passando il codice 
sorgente di B alla funzione f.
La parte B esegue la funzione g in A e così si ritrova un buffer 
contenente il codice sorgente di sé stesso. La parte B generà quindi la 
parte A passando il proprio codice alla funzione f.
A questo punto B ha il codice sorgente dell'intero programma.
Passa il tutto a una funzione compile(...) contenuta sempre in B, e in 
questo modo ottiene il codice dell'eseguibile. Non gli rimane che 
calcolare l'hash di tale codice e quindi stamparlo.
Contorto eh?
Kiuhnm

 
Parecchio!
Un tizio ha fatto un proggramma che stampa il proprio MD5:
http://www.madore.org/~david/computers/quine.html
E questo è il sorgente del programma
ftp://quatramaran.ens.fr/pub/madore/misc/selfmd5.c
Si tratta di un quine (come diceva alef)
BB


Mi stai dicendo che il tuo programma è un compilatore? Ah, che facile!
Perché non implementi anche tutte le ottimizzazioni del compilatore? :D
Non c'è un modo di scrivere nell'exe dopo la compilazione un
hash-parziale che viene poi "aggiunto" a sé stesso e stampato a run-time?
Mi spiego:
- Genero l'exe
- Calcolo un hash parziale saltando i byte che dovranno contenerlo nell'exe
- Prendo il valore dell'hash parziale e lo scrivo nell'exe
- A run-time, leggo il valore dell'hash parziale dalla mia variabile e
gli sommo anche i suoi byte, poi lo stampo
Ora non sono un esperto di algoritmi di hash, ma se fanno qualcosa di
"accumulabile" e "indipendente dall'ordine" dovrebbe funzionare, no? Ho
detto la ca**ata?
Byebye,
Andrea.

 
ftp://quatramaran.ens.fr/pub/madore/misc/selfmd5.c
Capito il meccanismo, si tratta di cose molto facili.
Per es. il "tizio" chiede come sfida di costruire un bequine cioč un 
programma P tale che
"P.exe" stampa P
mentre
"P.exe -fratello" stampa Q
dove Q č tale che
"Q.exe" stampa Q
mentre
"Q.exe -fratello" stampa P.
P e Q sono quindi, in un certo senso, fratelli.
Senza parametri stampano sé stessi, mentre con il parametro "-fratello" 
stampano l'altro.
Come si fa a creare P?
In questo preciso momento non lo so... vediamo...
La freccia significa "stampa" e X' indica il programma X chiamato col 
parametro "-fratello". Ok?
E' facile creare P t.c.
P->P
P'->Q
Q->Q
Il problema č adesso fare in modo che
Q'->P
Come fare?
Trovato!
Assumiamo che A sia un codice sorgente che contenga varie chiamate alla 
funzione getMyCode(char* buf). Cosě com'č, il codice non funziona perché 
l'implementazione della getMyCode manca: ci sono solo le chiamate da 
parte delle altre funzioni.
E' possibile scrivere un programma Z che prende il codice A e lo 
trasforma in A2, dove A2 č semanticamente equivalente ad A ed in piů 
contiene la funzione mancante. (A2 č diverso da A, quindi getMyCode 
restituisce il codice di A2, non di A, ma questo č OK).
E' facile costruire Z seguendo lo schema riportato nell'altro mio post.
Z fa automaticamente quello che io farei manualmente...
Costruiamo P tale che
P'->Q
Per farlo procediamo cosě:
P' acquisisce il proprio codice CODE chiamando getMyCode (per ora NON 
implementata) quindi crea un Q siffatto:
Q:
P_CODE = "......"; 	// CODE
if (params == "-fratello")
   print P_CODE;
else
   getMyCode(buf);
   print buf;
P' trasforma a questo punto Q in Q2 usando il programma Z.
Adesso Q2 č tale che
Q2->Q2
Q2'->P
Adesso trasformiamo finalmente P in P2 tramite Z!
Abbiamo ottenuto un P tale che
P2->P2
P2->Q2
dove
Q2->Q2
Q2->P2
Kiuhnm

 hash-parziale che viene poi "aggiunto" a sé stesso e stampato a run-time?
Non se si tratta dell'SHA512 come ho scritto nel post iniziale.
Kiuhnm

 questo modo ottiene il codice dell'eseguibile.
La funzione "compile" te la scrivi tu o la trovi in una libreria C++?
Non e' che ti confondi con il Lisp o qualche altro linguaggio 
"introspettivo"?
Per ottenere il risultato, e' risultato necessario aggiungere ai 
sorgenti del programma un intero compilatore C++, raddoppiare il codice 
sorgente risultante, e, in fase di esecuzione, ricompilare tutto il 
programma risultante. Decisamente inefficiente.
Infine, il difetto principale.
Notoriamente, la relazione tra sorgenti ed eseguibili non e' univoca in 
nessuno dei due sensi. Cioe' sorgenti diversi possono produrre lo stesso 
eseguibile (basta aggiungere un commento) e lo stesso eseguibile puo' 
produrre eseguibili diversi (basta cambiare le opzioni di compilazione).
In realta', con piu' di un compilatore C++ (ho provato MS VC++ e GCC) ho 
constatato che ricompilando lo stesso sorgente due volte con l'identico 
comando di compilazione ottengo file eseguibili diversi.
Se la compilazione dei sorgenti di A e B e' fatta con un compilatore 
diverso da quello contenuto nella funzione "compile", si ottiene un 
eseguibile completamente diverso da quello generato in memoria dalla 
funzione "compile" durante l'esecuzione. Pertanto, il programma 
stampera' un hash diverso da quello corrispondente al file eseguibile.
Ma anche supponendo di compilare i sorgenti di A e B con lo stesso 
compilatore incapsulato nella funzione "compile", e con le stesse 
opzioni di compilazione, con molti compilatori si otterrebbe comunque un 
risultato discordante.
-- 
     Carlo Milanesi
 
http://digilander.libero.it/carlmila

 risultato discordante.
Sinceramente non capisco queste tue critiche.
E' possibile scrivere un compilatore perfettamente deterministico?
Sì, niente di più facile. Bene, usa questo.
Era in discussione l'efficienza dell'approccio?
No, si discutiva dell'esistenza di una soluzione.
Tale esistenza è legata a uno dei più importanti teoremi della teoria 
della calcolabilità.
Vogliamo maggiore efficienza?
Usiamo il linguaggio macchina al posto del C++.
Kiuhnm

 Kiuhnm

Kiuhnm ha scritto:
Questa cosa intrigante, mi ha fatto venire un'altra idea, che al 
solito non so se ti garba.
Ma prima chiedo : tu puoi operare con due programmi distinti ?
Non ti indignare, non era specificamente vietato dalle clausole !
Soviet

 Usiamo il linguaggio macchina al posto del C++.
Per trattare questioni di informatica teorica, sarebbe meglio il 
newsgroup it.scienza.informatica.


 Ma prima chiedo : tu puoi operare con due programmi distinti ?
In che modo?
Kiuhnm

 cioè molto vicino all'OS, spero di sbagliarmi)
Marcare il codice non serve perché il programma deve stampare l'hash 
dell'intero file. E se calcoli prima l'hash e poi la scrivi in qualche 
locazione del file, il file cambia e quindi l'hash non è più valido.
Kiuhnm

 Kiuhnm

 un po' quello che gli pare dell'eseguibile su disco)
Infatti la soluzione generale prescinde da ogni dettaglio riguardante la 
piattaforma usata. Trovi la soluzione in fondo a questo thread.
Kiuhnm

 di controllarne l'integrità.)
Avrei fatto meglio a chiedere di scrivere un programma C++ che stampasse 
l'hash del proprio codice sorgente.
Kiuhnm

Kiuhnm ha scritto:

Grazie dell'interessante quesito, anche se ci ho messo un giorno a
capire che lo era.
Ho trovato interessante seguire il thread e interpretare la soluzione.
Ogni tanto è bello uscire dal torpore della programmazione artigianale
e cercare di capire quelli che si guardano allo specchio e pensano
°Perché pensiamo?".
http://en.wikipedia.org/wiki/Quine
http://c2.com/cgi/wiki?QuineProgram
Obviously impossible,
alef

 alef
La prima risposta è quasi sempre precipitosa e questo quesito è tutto 
tranne che intuitivo ;)
Kiuhnm

 Kiuhnm