2014-09-10 3 views
4

메모리에서 일부 코드를 실행하고 싶습니다. 장기적 목표는 자체 해독 앱을 만드는 것입니다. 내가 뿌리부터 시작한 문제를 이해하기. 은 다음 코드 생성 : 내가 segfault의-오류 그러나C : 메모리에서 기계 코드를 실행하십시오.

#define UNENCRYPTED true 

#define sizeof_function(x) ((unsigned long) (&(endof_##x)) - (unsigned long) (&x)) 
#define endof_function(x) void volatile endof_##x() {} 
#define DECLARE_END_OF_FUNCTION(x) void endof_##x(); 

#include <unistd.h> 
#include <signal.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <errno.h> 
#include <string.h> 
#include <sys/mman.h> 
#include <sys/types.h> 

unsigned char *bin; 

#ifdef UNENCRYPTED 
void hexdump(char *description, unsigned char *toDump, unsigned long length) { 
    printf("Hex-dump of \"%s\":\n", description); 
    for (int i = 0; i < length; i++) { 
     printf("%02x", toDump[i]); 
    } 
    printf("\n"); 
} 


void hello_world() { 
    printf("Hello World!\n"); 
} 
endof_function(hello_world); 
#endif 

int main (void) { 
    errno = 0; 
    unsigned long hello_worldSize = sizeof_function(hello_world); 
    bin = malloc(hello_worldSize); 

    //Compute the start of the page 
    size_t pagesize = sysconf(_SC_PAGESIZE); 
    uintptr_t start = (uintptr_t) bin; 
    uintptr_t end = start + (hello_worldSize); 
    uintptr_t pagestart = start & -pagesize; 
    bin = (void *)pagestart; 

    //Set mprotect for bin to write-only 
    if(mprotect(bin, end - pagestart, PROT_WRITE) == -1) { 
     printf("\"mprotect\" failed; error: %s\n", strerror(errno)); 
     return(1); 
    } 

    //Get size and adresses 
    unsigned long hello_worldAdress = (uintptr_t)&hello_world; 
    unsigned long binAdress = (uintptr_t)bin; 

    printf("Address of hello_world %lu\nSize of hello_world %lu\nAdress of bin:%lu\n", hello_worldAdress, hello_worldSize, binAdress); 

    //Check if hello_worldAdress really points to hello_world() 
    void (*checkAdress)(void) = (void *)hello_worldAdress; 
    checkAdress(); 

    //Print memory contents of hello_world() 
    hexdump("hello_world", (void *)&hello_world, hello_worldSize); 

    //Copy hello_world() to bin 
    memcpy(bin, (void *)hello_worldAdress, hello_worldSize); 

    //Set mprotect for bin to read-execute 
    if(mprotect(bin, end - pagestart, PROT_READ|PROT_EXEC) == -1) { 
     printf("\"mprotect\" failed; error: %s\n", strerror(errno)); 
     return(1); 
    } 

    //Check if the contents at binAdress are the same as of hello_world 
    hexdump("bin", (void *)binAdress, hello_worldSize); 

    //Execute binAdress 
    void (*executeBin)(void) = (void *)binAdress; 
    executeBin(); 

    return(0); 
} 

을; 프로그램의 출력은 다음과 같다 :

(OS의 X에, i86-64) : 4,294,970,639

Size of hello_world 17 
Adress of bin:4296028160 
Hello World! 
Hex-dump of "hello_world": 
554889e5488d3d670200005de95a010000 
Hex-dump of "bin": 
554889e5488d3d670200005de95a010000 
Program ended with exit code: 9 

그리고 내 Raspi (32 비트 ARM 리눅스)에 그래서 hello_world의

ADRESS :

Adress of hello_world 67688 
Size of hello_world 36 
Hello World! 
Hello World! 
Hex-dump of "hello_world": 
00482de90db0a0e108d04de20c009fe512ffffeb04008de50bd0a0e10088bde8d20b0100 
Hex-dump of "bin": 
00482de90db0a0e108d04de20c009fe512ffffeb04008de50bd0a0e10088bde8d20b0100 
Speicherzugriffsfehler //This is german for memory access error 

내 실수는 어디에서 왔습니까?


문제가 있었다, 그래서 hello_world에서 printf와 통화는 물론 복사 기능이 작동하지 않습니다 상대 점프 주소, 기준입니다. 테스트 목적으로 나는에 그래서 hello_world 변경 :

int hello_world() { 
    //_printf("Hello World!\n"); 
    return 14; 
} 

과에 "// binAdress 실행"아래의 코드 :

int (*executeBin)(void) = (void *)binAdress; 
int test = executeBin(); 
printf("Value: %i\n", test); 

이되는 출력한다 (14) : D ARM에

+5

x64로 컴파일 된 스 니펫은 'e95a010000'으로 끝나며 다른 주소로 점프합니다. 이 점프의 대상은 점프 명령 자체의 주소와 관련하여 계산됩니다. 어셈블리 코드를 메모리의 다른 위치에 복사 할 때 점프 대상이 엉망이되었습니다. – DCoder

+0

좋아, 내가이 권리를 얻는다면 printf를 printf에 대한 절대적인 참조로 대체해야 할 필요가 있겠는가? –

+1

예,이 방법으로이 문제를 해결할 수 있습니다. – DCoder

답변

3

을, 당신은 cacheflush과 같은 함수를 사용하여 명령어 캐시를 비우거나 코드가 제대로 실행되지 않을 수 있습니다. 이는 자체 수정 코드 및 JIT 컴파일러에 필요하지만 일반적으로 x86에는 필요하지 않습니다.

또한 한 위치에서 다른 위치로 코드를 이동하는 경우 상대 점프를 수정해야합니다. 일반적으로 라이브러리 함수 호출은 재배치 섹션으로의 점프로 구현되며 종종 상대적입니다.

점프를 픽스 업하지 않으려면 링커 트릭을 사용하여 코드를 컴파일하여 다른 오프셋에서 시작하도록 할 수 있습니다. 그런 다음 해독 할 때 해독 된 코드를 해당 오프셋에로드하기 만하면됩니다. 두 단계의 컴파일 과정이 일반적으로 사용됩니다 : 실제 코드를 컴파일하고, 생성 된 기계 코드를 해독 스텁에 추가하고, 전체 프로그램을 컴파일합니다.