bestsource

메모리에서 컴퓨터 코드 실행 중

bestsource 2023. 9. 26. 22:26
반응형

메모리에서 컴퓨터 코드 실행 중

메모리에 저장된 기계 코드를 실행하는 방법을 찾고 있습니다.

다음 코드가 있습니다.

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

int main(int argc, char* argv[])
{
    FILE* f = fopen(argv[1], "rb");

    fseek(f, 0, SEEK_END);
    unsigned int len = ftell(f);
    fseek(f, 0, SEEK_SET);

    char* bin = (char*)malloc(len);
    fread(bin, 1, len, f);

    fclose(f);

    return ((int (*)(int, char *)) bin)(argc-1, argv[1]);
}

위의 코드는 GCC에서는 잘 컴파일되지만, 명령줄에서 다음과 같이 프로그램을 실행하려고 하면 다음과 같습니다.

./my_prog /bin/echo hello

프로그램에 오류가 발생합니다.저는 문제가 마지막 줄에 있다는 것을 알았습니다. 댓글을 달면 segfault가 중지되기 때문입니다.

아직 기능 포인터에 대한 이해가 잘 안 가는 것 같아요.

그 문제는 깁스 불량인가요, 아니면 다른 건가요?

쓰기 실행 권한이 있는 페이지가 필요합니다.unix에 있는 경우 mmap(2) 및 mprotect(2)를 참조하십시오.malloc로 하면 안 됩니다.

또한 다른 사람들의 말을 읽어보면 로더를 사용해서만 원시 머신 코드를 실행할 수 있습니다.만약 당신이 ELF 헤더를 실행하려고 한다면 그것은 아마 모두 똑같이 결함이 될 것입니다.

회신 및 다운모드 내용 관련:

1- OP가 머신 코드를 실행하려고 한다고 해서 실행 파일을 실행하기보다는 그 부분에 대해 답변했습니다.

2- malloc과 mman 기능을 혼용하지 않는 이유 보기:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>

int main()
{
    char *a=malloc(10);
    char *b=malloc(10);
    char *c=malloc(10);
    memset (a,'a',4095);
    memset (b,'b',4095);
    memset (c,'c',4095);
    puts (a);
    memset (c,0xc3,10); /* return */

    /* c is not alligned to page boundary so this is NOOP.
     Many implementations include a header to malloc'ed data so it's always NOOP. */
    mprotect(c,10,PROT_READ|PROT_EXEC);
    b[0]='H'; /* oops it is still writeable. If you provided an alligned
    address it would segfault */
    char *d=mmap(0,4096,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_PRIVATE|MAP_ANON,-1,0);
    memset (d,0xc3,4096);
    ((void(*)(void))d)();
    ((void(*)(void))c)(); /* oops it isn't executable */
    return 0;
}

Linux x86_64에서 이 동작을 정확히 표시합니다. 다른 구현에서 발생할 수 있는 추한 동작입니다.

malloc을 사용하면 효과가 좋습니다.

네, 이게 제 최종 답변입니다. 제가 원래 포스터의 코드를 사용한 것을 참고해주세요.원래 코드가 그랬던 것처럼, 이 코드의 컴파일된 버전인 디스크에서 힙 할당된 영역 "bin"으로 로드하고 있습니다(이름은 argv를 사용하지 않고 고정되며, 값 0x674는 from;

objdump -F -D foo|grep -i hoho
08048674 <hohoho> (File Offset: 0x674):

이는 실행 시 BFD(Binary File Descriptor library) 또는 다른 것을 통해 검색할 수 있으며, 동일한 lib 집합에 정적으로 연결되어 있는 경우에는 다른 바이너리(자신 뿐만 아니라)를 호출할 수 있습니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

unsigned char *charp;
unsigned char *bin;

void hohoho()
{
   printf("merry mas\n");
   fflush(stdout);
}

int main(int argc, char **argv)
{
   int what;

   charp = malloc(10101);
   memset(charp, 0xc3, 10101);
   mprotect(charp, 10101, PROT_EXEC | PROT_READ | PROT_WRITE);

   __asm__("leal charp, %eax");
   __asm__("call (%eax)" );

   printf("am I alive?\n");

   char *more = strdup("more heap operations");
   printf("%s\n", more);

   FILE* f = fopen("foo", "rb");

   fseek(f, 0, SEEK_END);
   unsigned int len = ftell(f);
   fseek(f, 0, SEEK_SET);

   bin = (char*)malloc(len);
   printf("read in %d\n", fread(bin, 1, len, f));
   printf("%p\n", bin);

   fclose(f);
   mprotect(&bin, 10101, PROT_EXEC | PROT_READ | PROT_WRITE);

   asm volatile ("movl %0, %%eax"::"g"(bin));
   __asm__("addl $0x674, %eax");
   __asm__("call %eax" );
   fflush(stdout);

   return 0;
}

달리기...

co tmp # ./foo
am I alive?
more heap operations
read in 30180
0x804d910
merry mas

UPX를 사용하여 파일의 로드/수정/실행을 관리할 수 있습니다.

추신: 이전에 링크가 끊어진 것에 대해 죄송합니다:|

내가 보기에 당신은 ELF 이미지를 로드한 다음 바로 ELF 헤더로 뛰어들려고 하는 것 같은데요?http://en.wikipedia.org/wiki/Executable_and_Linkable_Format

만약 다른 바이너리를 실행하려고 한다면, 어떤 플랫폼을 사용하든 프로세스 생성 기능을 사용해 보는 것은 어떨까요?

일반적인 실행 파일은 다음과 같습니다.

  • 머리말
  • main(int, char **)

첫 번째는 일반적으로 파일의 바이트 0이 실행 가능하다는 것을 기대할 수 없다는 것을 의미합니다. 대신 헤더에 있는 정보는 메모리에 파일의 나머지 부분을 로드하는 방법과 실행을 시작하는 위치를 설명합니다.

두 때 C 합니다를 C수 합니다.(int, char **)하지 않는 할 수 됩니다 매개 변수를 사용하지 않는 기능으로 사용할 수 있습니다(따라서 호출하기 전에 아무것도 누르지 않아도 됩니다).그러나 메인으로 전달되는 명령줄 문자열을 구성하기 위해 항목 코드에서 사용할 환경을 채워야 합니다.

주어진 OS 아래에서 손으로 이 작업을 수행하는 것은 저를 뛰어넘는 깊이가 있겠지만, 여러분이 하려는 일을 훨씬 더 잘 수행할 수 있는 방법이 있을 것이라고 확신합니다.외부 파일을 on-off 작업으로 실행하거나, 외부 바이너리를 로드하여 프로그램의 일부로 기능을 처리하려고 합니까?둘 다 유닉스의 C 라이브러리에서 제공됩니다.

통화 자체보다는 함수 포인터를 통해 통화로 점프하는 코드가 segfault를 유발할 가능성이 높습니다.게시한 코드에서 빈에 로드된 코드가 유효하다고 판단할 수 있는 방법은 없습니다.디버거를 사용하고, 어셈블러 보기로 전환하고, 반환문을 깨고 함수 호출에 들어가서 실행할 코드가 실제로 실행되고 있고, 실행할 코드가 유효한지 확인하는 것이 최선의 방법입니다.

또한 코드를 실행하려면 위치가 독립적이어야 하며 완전히 해결해야 합니다.

게다가 프로세서/OS가 데이터 실행 방지를 가능하게 한다면, 아마도 그 시도는 실패할 것입니다.이것은 기껏해야 어떤 경우에도 잘못된 조언입니다. 로딩 코드가 OS의 목적입니다.

당신이 하려는 것은 통역사가 하는 것과 비슷한 것입니다.통역사가 파이썬처럼 통역된 언어로 작성된 프로그램을 읽고, 그 코드를 즉석에서 컴파일하고, 실행 가능한 코드를 메모리에 넣고 실행하는 것을 제외하고는 말입니다.

Just-in-time 컴파일에 대한 자세한 내용도 읽어 볼 수 있습니다.

때맞춰 컴파일
Java HotSpot JIT 런타임

GNU 라이트닝이나 libJ와 같은 JIT 코드 생성을 위해 사용 가능한 라이브러리가 있습니다.관심이 있으시다면 IT.하지만 단순히 파일을 읽고 코드를 실행하는 것 이상의 일을 해야 합니다.사용 시나리오의 예는 다음과 같습니다.

  1. 스크립팅 언어로 작성된 프로그램(사용자 자신의 프로그램)을 읽습니다.
  2. JIT 라이브러리가 이해하는 중간 언어로 소스를 구문 분석하고 컴파일합니다.
  3. JIT 라이브러리를 사용하여 대상 플랫폼의 CPU에 대한 이 중간 표현에 대한 코드를 생성합니다.
  4. JIT 생성 코드를 실행합니다.

그리고 코드를 실행하려면 mmap()을 사용하여 실행 코드를 프로세스의 주소 공간에 매핑하고 해당 페이지를 실행 가능으로 표시하고 해당 메모리로 점프하는 기술을 사용해야 합니다.이것보다 더 복잡하지만 Python, Ruby 등 스크립트 언어의 모든 인터프리터 아래에서 무슨 일이 일어나고 있는지 이해하기 위해서는 좋은 시작입니다.

온라인판 "Linkers and Loader"에서는 객체 파일 형식, 프로그램을 실행할 때 뒤에서 일어나는 일, 링커와 로더의 역할 등에 대한 자세한 정보를 제공합니다.아주 잘 읽었습니다.

dlopen() 파일을 열고 기호 "main"을 찾아 0, 1, 2 또는 3개 인수(모두 유형 char*)로 호출할 수 있습니다.

운영 체제를 사용하여 프로그램을 로드하고 실행합니다.

유닉스에서는 exec 호출이 이 작업을 수행할 수 있습니다.

질문의 토막글을 다시 쓸 수 있습니다.

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

int main(int argc, char* argv[])
{
    return execv(argv[1],argv+2);
}

실행 파일에는 단순한 코드 이상의 것이 포함되어 있습니다.헤더, 코드, 데이터, 더 많은 데이터, 이러한 것들은 OS와 라이브러리에 의해 서로 다른 메모리 영역으로 분리되어 로드됩니다.프로그램 파일을 메모리 한 덩어리에 로드할 수 없고 첫 번째 바이트로 이동할 수 없습니다.

자신의 임의 코드를 실행하려는 경우 동적 라이브러리를 살펴보아야 합니다. 이는 동적 라이브러리가 정확히 목적이기 때문입니다.

언급URL : https://stackoverflow.com/questions/2019923/executing-machine-code-in-memory

반응형