Domanda

È possibile scrivere una funzione C che procede come segue?

  1. Alloca un mucchio di memoria nell'heap
  2. Scrive il codice macchina in esso
  3. Esegue le istruzioni di tali macchine

Naturalmente, dovrei ripristinare lo stato dello stack su quello che era prima dell'esecuzione manuale di quelle istruzioni della macchina, ma voglio sapere se questo è fattibile in primo luogo.

È stato utile?

Soluzione

È certamente possibile . Per vari motivi, negli ultimi 30-40 anni abbiamo lavorato molto per renderlo il più difficile possibile, ma è possibile. Nella maggior parte dei sistemi ora esistono meccanismi hardware e software che tentano di proteggere l'esecuzione dello spazio dati.

Le basi, tuttavia, sono abbastanza semplici: costruisci un pezzo di codice e lo assembli, a mano o 4 tramite un compilatore. È quindi necessario un frammento di spazio del codice, quindi inserire il codice nel programma

unsigned int prgm[] = { 0x0F, 0xAB, 0x9A ... };  // Random numbers, just as an example

poiché volevi usare heap devi mallocare lo spazio

void * myspace ;
if((myspace= malloc(sizeof(prgm))) != NULL) {
     memcpy(myspace, pgrm, sizeof(pgrm));
} else { // allocation error
}

Ora, ciò di cui hai bisogno è un modo per fare in modo che il contatore programmi punti a quel blocco di dati che è anche il tuo blocco di codice. Ecco dove hai bisogno di un po 'di astuzia. L'impostazione del contatore del programma non è un grosso problema; questa è solo un'istruzione JUMP per la tua macchina sottostante. Ma come farlo?

Uno dei modi più semplici è fare scherzi intenzionali con lo stack. Lo stack, ancora una volta concettualmente, assomiglia a questo (i dettagli dipendono sia dal sistema operativo che dalle coppie del compilatore e dall'hardware):

    | subroutine return addr |
    | parameters ...         |
    | automatic variables    |

Il trucco di base qui è ottenere di nascosto l'indirizzo del tuo codice nell'indirizzo di ritorno; quando una routine ritorna, sostanzialmente passa a quel ritorno addrfess. Se riesci a simularlo, il PC verrà impostato dove preferisci.

Quindi, ciò di cui hai bisogno è una routine, chiamiamola " goThere () "

void goThere(void * addr){
    int a ;     // observe above; this is the first space 
                // on the stack following the parameters
    int * pa;   // so we use it's address

    pa = (&a - (sizeof(int)+(2*sizeof(void*))) ;  // so use the address
                // but back up by the size of an int, the pointer on the
                // stack, and the return address
    // Now 'pa' points to the routine's return add on the stack.
    *pa = addr; // sneak the address of the new code into return addr
    return ;    // and return, tricking it into "returning"
                // to the address of your special code block
}

Funzionerà? Bene, forse, a seconda dell'hardware e del sistema operativo. La maggior parte dei sistemi operativi moderni proteggerà l'heap (tramite mappatura della memoria o simili) dal PC che si sposta al suo interno. Questa è una cosa utile per motivi di sicurezza, perché non ti lascerebbe prendere quel tipo di controllo completo.

Altri suggerimenti

Questo è molto simile a questa domanda :)

Leggi il codice di chiamata memorizzato nell'heap da vc ++ . Su posix, mprotect sembra appropriato (guarda in man mprotect ):

char *mem = malloc(sizeof(code));
mprotect(mem, sizeof(code), PROT_READ|PROT_WRITE|PROT_EXEC);
memcpy(mem, code, sizeof(code));
// now arrange some code to jump to mem. But read the notes here on casting 
// from void* to a function pointer:
// http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html

Tuttavia, dice:

  

Il fatto che PROT_EXEC abbia effetti diversi da PROT_READ dipende dall'architettura e dalla versione del kernel. Su alcune architetture hardware (ad es. I386), PROT_WRITE implica PROT_READ.

Quindi, per prima cosa, controlla se sul tuo sistema operativo funziona.

RE: ripristino manuale dello stack

Se segui le convenzioni di chiamata utilizzate dalla tua piattaforma / compilatore all'interno del codice macchina che generi, non dovresti fare alcun ripristino manuale dello stack. Il compilatore lo farà per te, quando lo fai

* pfunc (args)

dovrebbe aggiungere tutti gli opportuni passaggi di manipolazione dello stack pre o post chiamata necessari.

Assicurati solo di seguire le giuste convenzioni all'interno del codice generato, tuttavia.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top