2012-05-05 26 views
7

Penso che la mia domanda potrebbe sembrare un po 'strana, ma qui va; Sto provando a creare un programma in modo dinamico in C++ (principalmente per il gusto di farlo, ma anche per una ragione programmatica) e non è così difficile come potrebbe sembrare. Per fare questo è necessario utilizzare assembly in fase di esecuzione in questo modo:Un modello valido in assembly per argomenti variadici

byte * buffer = new byte[5]; 
*buffer = '0xE9'; // Code for 'jmp' 
*(uint*)(buffer + 1) = 'address destination'; // Address to jump to 

Questo è molto più facile di quanto possa sembrare, perché a indirizzare una sola piattaforma e compilatore; GCC con Linux 32bit (e anche solo una convenzione di chiamata, cdecl). Quindi sto cercando di creare una funzione di assembly dinamico per reindirizzare le chiamate da trigger, quindi posso usare i metodi di classe come callback (anche con librerie API C (con cdecl ovviamente)). Ho solo bisogno di questo per supportare puntatori e tipi nativi (char, int, short ecc ...).

ANYTHING MyRedirect(ANY AMOUNT ARGUMENTS) 
{ 
    return MyClassFunc('this', ANY AMOUNT ARGUMENTS); 
} 

La funzione precedente è quella che voglio creare in assembly puro (in memoria con C++). Poiché la funzione è molto semplice, il suo ASM è semplice (dipende dagli argomenti).

55      push %ebp 
89 e5     mov %esp,%ebp 
83 ec 04    sub $0x4,%esp 
8b 45 08    mov 0x8(%ebp),%eax 
89 04 24    mov %eax,(%esp) 
e8 00 00 00 00   call <address> 
c9      leave 
c3      ret 

Quindi, nel mio programma, ho creato un generatore di pattern ASM (dato che non so ASM particolarmente bene, cerco modelli). Questa funzione può generare codice assembly (in byte, per il caso esatto sopra, cioè una funzione che reindirizza e restituisce) specificando la quantità di argomenti di cui la funzione ha bisogno. Questo è uno snippet dal mio codice C++.

std::vector<byte> detourFunc(10 + stackSize, 0x90); // Base is 10 bytes + argument size 

// This becomes 'push %ebp; move %esp, %ebp' 
detourFunc.push_back(0x55);  // push %ebp 
detourFunc.push_back(0x89);  // mov 
detourFunc.push_back(0xE5);  // %esp, %ebp 

// Check for arguments 
if(stackSize != 0) 
{ 
    detourFunc.push_back(0x83);  // sub 
    detourFunc.push_back(0xEC);  // %esp 
    detourFunc.push_back(stackSize); // stack size required 

    // If there are arguments, we want to push them 
    // in the opposite direction (cdecl convention) 
    for(int i = (argumentCount - 1); i >= 0; i--) 
    { 
     // This is what I'm trying to implement 
     // ... 
    } 

    // Check if we need to add 'this' 
    if(m_callbackClassPtr) 
    { 

    } 
} 

// This is our call operator 
detourFunc.push_back(0xE8);  // call 

// All nop, this will be replaced by an address 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 
detourFunc.push_back(0x90);  // nop 

if(stackSize == 0) 
{ 
    // In case of no arguments, just 'pop' 
    detourFunc.push_back(0x5D); // pop %ebp 
} 

else 
{ 
    // Use 'leave' if we have arguments 
    detourFunc.push_back(0xC9); // leave  
} 

// Return function 
detourFunc.push_back(0xC3);  // ret 

Se specifico zero come stackSize questo sarà l'output:

55      push %ebp 
89 e5     mov %esp,%ebp 
e8 90 90 90 90   call <address> 
5d      pop %ebp 
c3      ret 

Come potete vedere, questo è del tutto valido a 32 bit ASM, e fungerà da 'MyRedirect' se aveva zero argomenti e nessuna necessità di un puntatore "this". Il problema è che voglio implementare la parte in cui genera il codice ASM, a seconda della quantità di argomenti che specifica che la funzione 'reindirizzamento' riceverà. L'ho fatto con successo nel mio piccolo programma in C++ (crackato il pattern).

#include <stdio.h> 
#include <stdlib.h> 

int main(int argc, char * argv[]) 
{ 
    int val = atoi(argv[1]); 

    printf("\tpush %%ebp\n"); 
    printf("\tmov %%esp,%%ebp\n"); 

    if(val == 0) 
    { 
     printf("\tcall <address>\n"); 
     printf("\tpop %%ebp\n"); 
    } 

    else 
    { 
     printf("\tsub $0x%x,%%esp\n", val * sizeof(int)); 

     for(int i = val; i > 0; i--) 
     { 
      printf("\tmov 0x%x(%%ebp),%%eax\n", i * sizeof(int) + sizeof(int)); 
      printf("\tmov %%eax,0x%x(%%esp)\n", i * sizeof(int) - sizeof(int)); 
     } 

     printf("\tcall <address>\n"); 
     printf("\tleave\n"); 
    } 

    printf("\tret\n"); 
    return 0; 
} 

Questa funzione stampa lo stesso modello del codice ASM generato da 'objdump'. Quindi la mia domanda è; sarà valido in tutti i casi se I solo desidera una funzione di reindirizzamento come quella precedente, indipendentemente dagli argomenti, se è solo sotto Linux 32bit, o ci sono delle insidie ​​che devo conoscere? Per esempio; l'ASM generato sarebbe diverso con "cortometraggi" o "caratteri" o funzionerà (l'ho solo verificato con numeri interi), e anche se chiamo una funzione che restituisce 'void' (come influirebbe sull'ASM)?

avrei potuto spiegato tutto un po 'sfocata, quindi si prega di chiedere, invece di eventuali incomprensioni :)

NOTA: Non voglio sapere alternative, mi piace il mio attuale implementazione e penso che sia molto interessante, ho apprezzerei molto il tuo aiuto sull'argomento.

EDIT: In caso di interesse, qui ci sono alcune discariche per quanto sopra codice C++: link

+6

Un'alternativa molto buona alle istruzioni di codifica a mano è la libreria [asmjit] (http://code.google.com/p/asmjit/) (contrariamente al nome, questo non è un compilatore JIT, solo qualcosa che I compilatori JIT possono usare). –

+0

@afishwhoswimsaround Quella libreria sembrava fantastica. Letteralmente. Questo sembra essere esattamente ciò di cui ho bisogno. Domanda veloce; posso creare dinamicamente una funzione con questa libreria (con malloc o qualcosa del genere) così posso 'reindirizzare' (con assembly 'jmp') altre funzioni ('agganciarle') alle funzioni generate? –

+1

Sì, guarda gli esempi http://code.google.com/p/asmjit/wiki/Examples –

risposta

1

Come suggerisce Dan, è necessario contrassegnare la memoria come eseguibile. I wrote some code you can use. (Funziona su GNU/Linux e Windows.) Se non si intende mai supportare ARM, x86-64 o altre piattaforme, allora non vedo alcun calo del codice (con la parte eseguibile aggiunta) e sembra che sia dovrebbe "funzionare sempre". (Supponendo che tutto il resto funzioni correttamente, naturalmente.)

#include <sys/mman.h> 

... 

n = <size of code buffer>; 
p = mmap(0, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, 0, 0); 

'pesce' suggerito di utilizzare asmjit. Devo essere d'accordo con questo; è più portatile del tuo metodo. Tuttavia, hai detto che non ti interessano le alternative.

Potrebbe essere interessato a qualcosa chiamato "Thunking" (tipo di). Cerca fondamentalmente di eseguire "la sostituzione di una callback C con un metodo C++". Questo è in realtà piuttosto utile, ma non è davvero un buon progetto per le tue applicazioni.

Spero che questo aiuti.

Problemi correlati