바이너리 명령어를 소스코드에 삽입하여 이를 사용하는 방법을 소개한다.

준비운동 하기

한가지 기능을 구현할 때, 여러가지 방법이 있는 것처럼, 꼭 정답이 있지는 않다.

물론 프로그램이 실행될 환경에 적합한 프로그래밍이어야 한다는 가정을 전제로 말이다.

내가 지금껏 만들어 온 프로그램들은 문자열 처리 기능이 대부분을 차지한다.

이런 기능의 경우도 구현하는 방법은 여러가지가 될 수 있다.

내가 자주 사용한 방법은 유닉스 명령어들을 사용한 방법으로, 쉘 프로그래밍과 system 함수를 호출하는 것이다.

물론 이 방법이 가장 좋은 방법은 아닐지도 모른다. 하지만, 나름대로 가장 빠르게 원하는 결과물을 얻을 수 있고, 동작하는 데 큰 문제가 없어왔다.

명령어를 사용한 프로그래밍의 특징

이 방법의 장단점을 꼽아 보겠다.

  1. '빠른 시간 내에 구현이 가능하다' → 복잡한 문자열 처리도 간단한 명령어 정규식으로 나타낼 수 있다.
  2. '안정적인 동작이 가능하다' → 코딩 전에 쉘에서 명령어로 동작확인이 가능하기 때문에 검증이 용이하다. 또한 각각 명령어들의 동작은 검증이 되었다.
  1. '실행 시, 필요한 명령어가 존재해야 한다. 따라서 시스템 사이즈가 증가될 수 있다' → 명령어가 정해진 경로에 존재해야 한다. 임베디드 시스템의 경우, 해당 명령어를 포팅해야 할 수도 있다.
  2. '속도가 느려질 수 있다' → system 함수의 경우, fork() 와 exec() 함수가 동반되기 때문에 짧은 시간에 빈번하게 호출될 경우, 느려질 수 있다.

앞서 살펴본 특징들 중에 단점을 커버할 수 있는 방법을 고민하던 중에 명령어 바이너리를 소스코드에 built-in 하는 방법을 시도하게 되었다.
이 방법 역시, 장단점은 존재한다.
'장점으로는 명령어 코드 은닉이 가능하다는 점이다. 최종 실행 바이너리 파일 내에 명령어 바이너리 코드가 들어있기 때문에 외부에서는 하나의 파일로 보일 뿐이다.'
'단점으로는 명령어 바이너리를 포함하기 때문에 용량이 커진다는 것이고, 바이너리 파일을 built-in 하기전에 포팅을 해야하기 때문에 최종 포팅한 시스템 이외에서 실행할 경우, 이식성이 떨어질 수 있다.'

그대로 따라하기

간단한 예제를 통해 바이너리파일을 built-in 시켜 보겠다.
따라하기 전에 'm4' 라는 프로그램을 설치한다. 명령어 바이너리를 HEX 배열로 만들어주는 역할을 한다.
참고로 여기서는 비교적 크기가 작은 바이너리 명령어인 'echo' 를 사용할 것이다. echo 파일을 소스코드가 있는 같은 디렉토리로 복사한다.

아래의 내용으로 tmp.c.m4 파일을 작성한다.

m4_include(hexdump.m4)
#include <stdio.h>
 
const unsigned char image[] = { 
  m4_hexdump([[echo]])              // echo 라는 명령어를 사용할 것이다
};

아래의 내용으로 hexdump.m4 을 작성한다.

m4_divert(-1)
m4_changequote([[,]])
m4_define([[m4_hexdump]], [[m4_esyscmd(cat $1 | xxd -i)]])
m4_divert

이제 echo 바이너리를 HEX 배열로 추출해낸다.

$ m4 -P tmp.c.m4 > tmp.c

tmp.c 파일을 열어보자.

const unsigned char array[] = {
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x60, 0x16, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0xb0, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x09, 0x00, 0x40, 0x00,
  0x1c, 0x00, 0x1b, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
  0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...
...
};

위의 소스코드에 선언된 array 배열이 echo 바이너리 HEX 값을 가지고 있다. 이 배열을 소스코드에서 사용하면 된다.

앞서 생성한 배열을 사용한 프로그램을 작성한다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
 
const unsigned char array[] = {
    0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x60, 0x16, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0xb0, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...
...
};
int main(void) {
 
      FILE *out;
        int i;
 
          // 새로 만들 이진파일의 내용을 바이트 배열로 표현
//        unsigned char array[] = { 0x02, 0x74, 0x21, 0x6A, 0x02, 0x6A, 0xFF, 0x53, 0xE8, 0x8F, 0x0E, 0x00, 0x00, 0x83, 0xC4, 0xFF }; 
 
 
            // 이진파일 쓰기용으로 열기, 없다면 새로 만들고
            if ( (out = fopen("out.bin", "wb")) == NULL ) {
                    fputs("파일 열기 에러!", stderr);
                        exit(1);
                          }
 
 
              // 이진 파일에, array[] 배열의 요소를 하나씩 쓰기
              for (i = 0; i < ( sizeof(array) / sizeof(array[0]) ); i++) {
                      if (fputc(array[i], out) == EOF) {
                                fputs("파일 쓰기 에러!", stderr);
                                      exit(2);
                                          }
                        }
 
                fclose(out); // 파일 닫기
 
            if( chmod("out.bin", 0777) == -1)
                printf("error!\n");
 
            system("./out.bin abc");
 
                  return 0;
}

위의 코드는 array 배열의 HEX 데이터를 파일에 쓰고, 실행 권한을 변경 후, 실행하도록 한다.
추가하자면, 실행 후, 실행 파일(out.bin)을 삭제하면, 흔적이 남지 않게 된다.

  • computer/programming/바이너리_파일을_소스코드에_추가하기.txt
  • Last modified: 4 years ago
  • by likewind