2010-10-10 19 views
19

Ho avuto un po 'troppo tempo a disposizione e ho iniziato a chiedermi se potevo scrivere un programma auto-modificante. A tal fine, ho scritto un "Hello World" in C, quindi ho usato un editor esadecimale per trovare la posizione della stringa "Hello World" nell'eseguibile compilato. È possibile modificare questo programma per aprirsi e sovrascrivere la stringa "Hello World"?Un programma C può modificare il suo file eseguibile?

char* str = "Hello World\n"; 

int main(int argc, char* argv) { 

    printf(str); 

    FILE * file = fopen(argv, "r+"); 

    fseek(file, 0x1000, SEEK_SET); 
    fputs("Goodbyewrld\n", file);  
    fclose(file);  

    return 0; 
} 

Questo non funziona, sto assumendo che c'è qualcosa impedendole di aprendosi dato che posso dividere questo in due programmi separati (A "Ciao mondo" e qualcosa per modificarlo) e funziona benissimo.

EDIT: La mia comprensione è che quando il programma viene eseguito, viene caricato completamente nella ram. Quindi l'eseguibile sul disco fisso è, a tutti gli effetti, una copia. Perché dovrebbe essere un problema modificarlo da solo?

C'è una soluzione?

Grazie

+0

Cheat Engine ftw !! –

+1

Haha, no. Solo curiosità oziosa. – Joel

+0

Cheat Engine funziona rilevando i valori nei programmi e permettendoti di cambiarli. Può darti vite extra nei videogiochi, ecc., Ma non penso che tu voglia veramente un cheat engine come risposta. –

risposta

29

In Windows, quando un programma viene eseguito l'intero file *.exe è mappati in memoria utilizzando il memory-mapped-file functions in Windows. Ciò significa che il file non viene necessariamente caricato tutto in una volta, ma invece le pagine del file vengono caricate su richiesta al momento dell'accesso.

Quando il file viene mappato in questo modo, un'altra applicazione (incluso se stessa) non può scrivere sullo stesso file per modificarlo mentre è in esecuzione. (Inoltre, su Windows non è possibile rinominare l'eseguibile in esecuzione, ma può farlo su Linux e altri sistemi Unix con file system basati su inode).

È possibile modificare i bit mappati in memoria, ma se lo si fa il sistema operativo utilizza la semantica "copy-on-write", il che significa che il file sottostante non viene modificato sul disco, ma una copia della pagina (s) in memoria è fatto con le tue modifiche. Prima di essere autorizzati a farlo, di solito devi armeggiare con i bit di protezione sulla memoria in questione (ad esempio VirtualProtect).

Un tempo, era comune per i programmi di assemblaggio di basso livello che si trovavano in ambienti di memoria molto limitati per utilizzare il codice auto-modificante.Tuttavia, nessuno lo fa più perché non stiamo correndo negli stessi ambienti vincolati, ei processori moderni hanno pipeline lunghe che diventano molto turbate se si inizia a cambiare il codice da sotto.

+0

Si noti che su Unix/Linux, mentre è possibile rinominare o addirittura eliminare un eseguibile in esecuzione su disco, viene mantenuto intatto in memoria fino alla morte del processo. –

+0

E quando è necessario, si può anche creare una copia di file della versione modificata, avviare un nuovo processo per eseguire la sostituzione del file e terminare se stesso. –

0

Se si operano su Windows, credo che blocca il file per evitare che vengano modificati, mentre il suo essere corsa. Ecco perché è spesso necessario uscire da un programma per installare un aggiornamento. Lo stesso non è vero su un sistema Linux.

+0

È possibile modificare il codice di un programma mentre è in esecuzione. (Su Windows, questo sarebbe 'WriteProcessMemory()'). Ecco come funziona il tuo debugger. Detto questo, è una pessima idea. – asveikau

+0

Non penso di aver mai affermato che fosse una buona cosa: P. Ma perché Windows dovrebbe bloccarlo? La mia comprensione è che quando il programma viene eseguito, viene caricato completamente nella ram. Quindi l'eseguibile sul disco fisso è, a tutti gli effetti, una copia. Perché dovrebbe essere un problema modificarlo? – Joel

+0

@asveikau questo ha a che fare con un file su disco non in memoria, ma tu sei corretto su cosa può essere fatto in memoria. –

4

È molto dipendente dal sistema operativo. Alcuni sistemi operativi bloccano il file, quindi puoi provare a imbrogliare facendone una nuova copia da qualche parte, ma stai solo eseguendo un'altra compy del programma.

Altri sistemi operativi eseguono controlli di sicurezza sul file, ad es. iPhone, quindi scrivere sarà molto lavoro, in più risiede come un file di sola lettura.

Con altri sistemi non si può nemmeno sapere dove si trova il file.

1

Ci sono modi non portatili per farlo su molte piattaforme. In Windows è possibile farlo con WriteProcessMemory(), ad esempio. Tuttavia, nel 2010 di solito è una pessima idea farlo. Questo non è il periodo in cui DOS viene codificato in assembly e lo fanno per risparmiare spazio. È molto difficile avere ragione, e fondamentalmente stai chiedendo problemi di stabilità e sicurezza. A meno che tu non stia facendo qualcosa di molto basso come un debugger, direi di non preoccuparti di questo, i problemi che presenterai non valgono il guadagno che potresti avere.

1

codice automodificante viene utilizzato per le modifiche in memoria, non nel file (unpackers come run-time come UPX fanno). Inoltre, la rappresentazione del file di un programma è più difficile da gestire a causa di indirizzi virtuali relativi, possibili rilocazioni e modifiche agli header necessari per la maggior parte degli aggiornamenti (ad esempio cambiando lo Hello world! in longer Hello World è necessario estendere il segmento di dati nel file).

Ti suggerisco di imparare prima a farlo in memoria. Per gli aggiornamenti dei file, l'approccio più semplice e generico sarebbe eseguire una copia del programma in modo che modificasse l'originale.

EDIT: E non dimenticare le ragioni principali viene utilizzato il codice di auto-modifica:

1) offuscamento, in modo che il codice che viene eseguito in realtà non è il codice che vedrete con semplici analisi statica del file.

2) Prestazioni, qualcosa come JIT.

Nessuno di questi benefici della modifica dell'eseguibile.

0

Nelle versioni più recenti di Windows CE (almeno 5.x una più recente) in cui le app vengono eseguite nello spazio utente, (rispetto alle versioni precedenti in cui tutte le app venivano eseguite in modalità supervisore), le app non possono nemmeno leggere il proprio file eseguibile.

4

Tutte le risposte presenti più o meno ruotano attorno al fatto che oggi non è più possibile eseguire facilmente il codice macchina auto-modificante. Sono d'accordo che ciò è fondamentalmente vero per i PC di oggi.

Tuttavia, se si vuole veramente vedere proprio codice di auto-modifica in azione, si dispone di alcune possibilità disponibili:

  • provare microcontrollori, quelli più semplici non hanno pipelining avanzata. La scelta più economica e rapida che ho trovato è MSP430 USB-Stick

  • Se un'emulazione è corretta, è possibile eseguire un emulatore per una piattaforma non obsoleta.

  • Se si desidera il codice auto-modificante solo per il gusto di farlo, si può anche divertirsi con il codice auto-distruttivo (più esattamente distruggendo il nemico) allo Corewars.

  • Se si è disposti a passare da C a dire un dialetto Lisp, il codice che scrive il codice è molto naturale lì. Suggerirei Scheme che è intenzionalmente tenuto piccolo.

6

Se si utilizza Windows, è possibile effettuare le seguenti operazioni:

Step-by-Step Esempio:

  1. chiamata VirtualProtect() sulle pagine di codice che si desidera modificare, con il PAGE_WRITECOPY protezione.
  2. Modificare le tabelle codici.
  3. Chiama VirtualProtect() sulle code page modificate, con la protezione PAGE_EXECUTE.
  4. Chiama FlushInstructionCache().

Per ulteriori informazioni, vedere How to Modify Executable Code in Memory (Archiviata: agosto 2010)

2

Se stiamo parlando di fare questo in un ambiente x86 non dovrebbe essere impossibile. Dovrebbe essere usato con cautela perché le istruzioni x86 sono di lunghezza variabile. Un'istruzione lunga può sovrascrivere le seguenti istruzioni e una più breve lascerà i dati residui dall'istruzione sovrascritta che dovrebbe essere annullata.

Quando il x86 primo divenne protetto manuali di riferimento Intel raccomanda il seguente metodo per il debug accesso a XO (eseguire solo) aree:

  1. creare un nuovo selettore vuota ("alto" parte di puntatori)
  2. impostato i suoi attributi a quello dell'area XO
  3. proprietà di accesso del nuovo selettore devono essere impostati RO dATI se desideri solo guardare a ciò che è in esso
  4. se si desidera modificare i dati delle proprietà di accesso deve essere impostato su RW DATA

Quindi la risposta al problema è nell'ultimo passaggio. Il RW è necessario se vuoi essere in grado di inserire l'istruzione breakpoint che è ciò che fanno i debugger. Processori più moderni rispetto all'80286 dispongono di registri di debug interni per abilitare la funzionalità di monitoraggio non intrusivo che potrebbe causare l'emissione di un breakpoint.

Windows ha reso disponibili i blocchi predefiniti per eseguire questa operazione a partire da Win16. Probabilmente sono ancora sul posto. Penso che Microsoft chiama questa classe di manipolazione puntatore "thunking".


Una volta ho scritto un motore di database molto veloce a 16 bit in PL/M-86 per DOS. Quando Windows 3.1 è arrivato (in esecuzione su 80386s) l'ho portato nell'ambiente Win16. Volevo sfruttare la memoria a 32 bit disponibile ma non c'era PL/M-32 disponibile (o Win32 per questo).

per risolvere il problema il mio programma utilizzato thunk nel seguente modo

  1. definiti a 32 bit puntatori (sel_16: offs_32) utilizzando strutture
  2. allocate aree di dati a 32 bit (< =>> 64KB dimensione) utilizzando la memoria globale e ricevuto in formato 16-bit lontano puntatore (sel_16: offs_16)
  3. riempito i dati nelle strutture copiando il selettore, quindi calcolando l'offset utilizzando la moltiplicazione a 16 bit con risultati a 32 bit.
  4. caricato il puntatore/struttura in es: EBX usando l'override prefisso di dimensione istruzioni
  5. accessibili i dati utilizzando una combinazione delle dimensioni istruzione e operando prefissi formato

volta che il meccanismo è stato insetto libera ha funzionato senza un intoppo. Le aree di memoria più grandi utilizzate dal mio programma sono state 2304 * 2304 con una precisione doppia che arriva a circa 40 MB. Anche oggi, chiamerei questo "grande" blocco di memoria. Nel 1995 era il 30% di un tipico stick SDRAM (128 MB PC100).

Problemi correlati