Pergunta

É possível escrever uma função C que faz o seguinte?

  1. Alocar um monte de memória no heap
  2. Grava código de máquina nele
  3. Executa essas instruções máquinas

É claro que eu teria de restaurar o estado da pilha para o que era antes da execução dessas instruções de máquina manualmente, mas eu quero saber se isso é possível em primeiro lugar.

Foi útil?

Solução

É certamente possível . Por várias razões, nós passamos um grande esforço dos últimos 30-40 anos tentando fazê-lo o mais difícil possível, mas é possível. Na maioria dos sistemas de agora, existem mecanismos de hardware e software que tentam espaço de dados proteger de ser executada.

O básico, porém, são bastante simples: você constrói um pedaço de código, e montá-lo, quer por OR4 mão através de um compilador. Você precisa então um fragmento de espaço de código, para que você inserir o código em seu programa

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

desde que você queria usar o pilha você precisa malloc o espaço

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

Agora, o que você precisa é uma maneira de obter o contador de programa para apontar para esse pedaço de dados que também é o seu pedaço de código. Aqui é onde você precisa de um pouco de astúcia. Definir o contador de programa não é grande coisa; isso é apenas uma instrução de salto para a sua máquina subjacente. Mas como fazer isso?

Uma das maneiras mais fáceis é por mexer propositalmente com a pilha. A pilha, mais uma vez, conceitualmente, é algo como isto (os detalhes dependem tanto do seu sistema operacional e compilador pares, e no seu hardware):

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

O truque básico aqui é conseguir sneakily o endereço do seu código para o endereço de retorno; quando uma rotina retorna, ele basicamente salta para que addrfess retorno. Se você pode fingir-lo, o PC será definido para onde quiser.

Então, o que você precisa é uma rotina, vamos chamá-lo "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
}

Será que vai funcionar? Bem, talvez, dependendo do hardware e sistema operacional. A maioria dos modernos de OS irá proteger a pilha (via mapeamento de memória ou similar) a partir do PC movendo para ele. Isso é uma coisa útil para fins de segurança, porque tínhamos acabado bem não deixá-lo tomar esse tipo de controle completo.

Outras dicas

Isto é muito semelhante ao esta questão :)

Leia código de chamada armazenado na pilha do vc ++ . Em POSIX, mprotect parece ser apropriada (olhar para 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

No entanto, ele diz:

Se PROT_EXEC tem qualquer efeito diferente de PROT_READ é Architecture e do kernel dependente versão. Em algumas arquiteturas de hardware (por exemplo, i386), PROT_WRITE implica PROT_READ.

Então é melhor, verifique primeiro se do seu sistema operacional, que funciona.

RE: restaurar manualmente a pilha

Se você seguir as convenções de chamada usadas por sua plataforma / compilador dentro do código de máquina que você gera, então você não deve ter que fazer qualquer pilha manual de restauração. O compilador irá fazer isso por você, quando você faz

* pFunc (args)

deve adicionar qualquer etapas de manipulação da pilha de chamadas pós que são necessários pré apropriado ou.

Apenas certifique-se que você siga as convenções corretas dentro do código gerado, no entanto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top