파일처리_24 바이너리 파일에 포함된 문자열 열기

 
  • 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어

명령어: strings, grep
키워드: 바이너리 파일, 검색, 실행 파일
사용처: 에러 메시지를 바탕으로 그 메시지를 출력하는 명령어를 찾고 싶을 때


실행예제

$ ./strings.sh
/home/user1/myapp/bin/start: error: Unknown Error
/home/user1/myapp/bin/kill: Unknown Error at %s
/home/user1/myapp/bin/kill: Unknown Error at %s:%d

스크립트

#!/bin/sh

# 검색할 에러 메시지
message="Unknown Error"

strings -f /home/user1/myapp/bin/* | grep "$message"  #---- 1

   

해설

이 스크립트는 에러 메시지를 단서로 바이너리 파일에서 해당하는 에러 메시지를 출력하는 명령어를 찾습니다.

시스템을 운용하다 보면 어떤 에러 메시지가 출력되는데 그게 어떤 명령어가 출력하는지 알지 못하는 경우가 있습니다. 프로그램을 펄이나 루비 같은 스크립트 언어로 작성하면 에러 메시지로 프로그램 파일을 grep하면 됩니다. 하지만 C 언어 등으로 작성하면 실행 파일은 컴파일된 바이너리 파일이므로 단순한 텍스트 검색이 불가능합니다. 그럴 때 사용하는 방법이 strings 명령어 입니다.

strings 명령어는 바이너리 파일에서 문자열을 추출합니다. 일반적으로 C 언어로 컴파일된 바이너리 파일이라도 문자열 상수는 파일 안에 그대로 저장되어 있습니다. 따라서 strings 명령어로 프로그램의 에러 메시지를 찾을 수 있습니다. 이 예제는 트러블 슈팅 때 그런 에러 메시지를 출력하는 명령어를 찾아내는 스크립트 입니다.

strings 명렁어에 -f 옵션을 써서 문자열 표시 때 파일명도 같이 표시합니다. -f 옵션은 대상 파일을 와일드카드(*)로 지정해서 지정한 경로 안에 있는 모든 파일을 대상으로 삼습니다. strings 명령어 출력은 파이프로 grep 명령어에 넘겨서 셸 변수 message로 지정한 문자열에 일치할 때만 표시합니다. 이 예제는 결과로 “Unknown Error”라는 문자열을 포함한 파일을 추출합니다.

예를 들어 운용 중인 애플리케이션이 본적이 없는 메시지를 로그 파일에 출력할 때 사용할 수 있습니다. 로그 파일에 출력된 본적 없는 메시지를 셸 변수 message에 설정하고, strings 명령어에 -f 옵션으로 지정한 경로를 애플리케이션의 디렉터리에 설정해서 실행해 보기 바랍니다.

   

od 명령어와 hexdump 명령어

바이너리 파일 내용을 직접 살펴볼 때는 파일을 8진수로 덤프하는 od 명령어를 자주 사용합니다. od 명령어는 다양한 옵션을 지원하는데, 단순히 바이너리 파일에 있는 문자열만 본다면 ASCII 문자열 출력을 하는 -c 옵션만 기억하면 됩니다.

다음은 리눅스 커널 파일을 od 명령어로 덤프한 실행 옝비니다. “Direct floppy boot…” 이라는 문자열이 보일 겁니다.

$ od -c vmlinuz-3.10.0-229.el7.x86_64
00000000	M	Z	352	\a	\0	300	\a	214	310	216	330	216	300	216	320	1
00000020	344	373	374	276	@	\0	254	300	t	\t	264	016	273	\a	\0
00000040	315	020	353	362	1	300	315	026	315	031	352	360	377	\0	360	\0
00000060	\0	\0	\0	\0	\0	\0	\0	\0	\0	\0	\0	\0	270	\0	\0	\0
00000100	D	i	r	e	c	t		f	l	o	p	p	y		b	o
00000120	o	t		i	s		n	o	t		s	u	p	p	o	r
00000140	t	e	d	.		U	s	e		a		b	o	o	t
(생략)

그리고 od 명령어는 비교적 간단한 명령어 입니다. 더 고성능인 hexdump 명령어도 자주 사용합니다. hexdump 명령어는 -C 옵션을 써서 파일 내용을 ‘16진수 덤프와 ASCII 문자열 셋’으로 표시할 수 있습니다.

  • 리눅스 커널을 hexdump 명령어로 덤프
    $ hexdump -C vmlinuz-3.10.0-229.el7.x86_64
    00000000	4d	5a	ea	07	00	c0	07	8c	c8	8e	d8	8e	c0	8e	d0	31	|MZ.............1|
    00000010	e4	fb	fc	be	40	00	ac	20	c0	74	09	b4	0e	bb	07	00	|....@.. .t......|
    00000020	cd	10	eb	f2	31	c0	cd	16	cd	19	ea	f0	ff	00	f0	00	|....1...........|
    00000030	00	00	00	00	00	00	00	00	00	00	00	00	b8	00	00	00	|................|
    00000040	44	69	72	65	63	74	20	66	6c	6f	70	70	79	20	62	6f	|Direct floopy bo|
    00000050	6f	74	20	69	73	20	6e	6f	74	20	73	75	70	70	6f	72	|ot is not suppor|
    00000060	74	65	64	2e	20	55	73	65	20	61	20	62	6f	6f	74	20	|ted. Use a boot |
    00000070	6c	6f	61	64	65	72	20	70	72	6f	67	72	61	6d	20	69	|loader program i|
    00000080	6e	73	74	65	61	64	2e	0d	0a	0a	52	65	6d	6f	76	65	|nsted.....Remove|
    00000090	20	64	69	73	6b	20	61	6e	64	20	70	72	65	73	73	20	| disk and press |
    (생략)
    

   

주의사항

  • 프로그램에서 동적으로 에러 메시지를 조합할 때는 strings 명령어로 바이너리 파일을 찾을 수 없습니다.

  • Mac strings 명령어에는 -f 옵션이 없으므로 파일명을 표시할 수 없습니다. 따라서 1부분은 for 반복문을 써서 파일마다 처리하면 됩니다.

    for filename in /usr/local/bin/*
    do
      echo "$filename:"
      strings $filename | grep "$message"
    done