wow, questo si è rivelato molto più doloroso di quanto mi aspettassi. Il 100% del dolore era la protezione del programma da sovrascrittura e/o esecuzione dei dati.
Due soluzioni illustrate di seguito. E un sacco di google è stato coinvolto in modo che il semplice mettere alcuni byte di istruzioni ed eseguirli fosse mio, il mprotect e l'allineamento sulla dimensione della pagina sono stati scelti dalle ricerche di google, cose che ho dovuto imparare per questo esempio.
Il codice di auto modifica è semplice, se si prende il programma o almeno solo le due semplici funzioni, si compila e poi si disassembla si ottengono gli opcode per tali istruzioni. o usare nasm per compilare blocchi di assembler, ecc. Da questo ho determinato il codice operativo per caricare un immediato in eax e poi tornare.
Idealmente si mettono semplicemente quei byte in una ram e si esegue quella ram. Per fare in modo che Linux faccia cambiare la protezione, significa che devi inviarlo a un puntatore che è allineato su una pagina mmap. Quindi allocare più del necessario, trovare l'indirizzo allineato all'interno di quell'allocazione che si trova sul limite della pagina e mprotect da tale indirizzo e utilizzare quella memoria per inserire i codici op e quindi eseguirli.
il secondo esempio prende una funzione esistente compilata nel programma, ancora una volta a causa del meccanismo di protezione non è possibile semplicemente indicarlo e modificare i byte, è necessario proteggerlo dalle scritture. Pertanto, è necessario eseguire il backup della pagina precedente con la chiamata mprotect con quell'indirizzo e abbastanza byte per coprire il codice da modificare. Quindi puoi cambiare i byte/opcode per quella funzione nel modo che preferisci (a condizione che non si riversi in alcuna funzione che vuoi continuare ad usare) ed eseguirla. In questo caso puoi vedere che fun()
funziona, quindi lo cambio semplicemente per restituire un valore, richiamarlo di nuovo e ora è stato modificato.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
unsigned char *testfun;
unsigned int fun (unsigned int a)
{
return(a+13);
}
unsigned int fun2 (void)
{
return(13);
}
int main (void)
{
unsigned int ra;
unsigned int pagesize;
unsigned char *ptr;
unsigned int offset;
pagesize=getpagesize();
testfun=malloc(1023+pagesize+1);
if(testfun==NULL) return(1);
//need to align the address on a page boundary
printf("%p\n",testfun);
testfun = (unsigned char *)(((long)testfun + pagesize-1) & ~(pagesize-1));
printf("%p\n",testfun);
if(mprotect(testfun, 1024, PROT_READ|PROT_EXEC|PROT_WRITE))
{
printf("mprotect failed\n");
return(1);
}
//400687: b8 0d 00 00 00 mov $0xd,%eax
//40068d: c3 retq
testfun[ 0]=0xb8;
testfun[ 1]=0x0d;
testfun[ 2]=0x00;
testfun[ 3]=0x00;
testfun[ 4]=0x00;
testfun[ 5]=0xc3;
ra=((unsigned int (*)())testfun)();
printf("0x%02X\n",ra);
testfun[ 0]=0xb8;
testfun[ 1]=0x20;
testfun[ 2]=0x00;
testfun[ 3]=0x00;
testfun[ 4]=0x00;
testfun[ 5]=0xc3;
ra=((unsigned int (*)())testfun)();
printf("0x%02X\n",ra);
printf("%p\n",fun);
offset=(unsigned int)(((long)fun)&(pagesize-1));
ptr=(unsigned char *)((long)fun&(~(pagesize-1)));
printf("%p 0x%X\n",ptr,offset);
if(mprotect(ptr, pagesize, PROT_READ|PROT_EXEC|PROT_WRITE))
{
printf("mprotect failed\n");
return(1);
}
//for(ra=0;ra<20;ra++) printf("0x%02X,",ptr[offset+ra]); printf("\n");
ra=4;
ra=fun(ra);
printf("0x%02X\n",ra);
ptr[offset+0]=0xb8;
ptr[offset+1]=0x22;
ptr[offset+2]=0x00;
ptr[offset+3]=0x00;
ptr[offset+4]=0x00;
ptr[offset+5]=0xc3;
ra=4;
ra=fun(ra);
printf("0x%02X\n",ra);
return(0);
}
http://linux.die.net/man/2/mprotect dovrebbe spiegare quali sono gli argomenti per mprotect. L'ID funzione da chiamare viene passato in EAX e gli argomenti successivi vengono passati in EBX ECX ed EDX. – KitsuneYMG