- 출처 : 유닉스 리눅스 쉘스크립트 예제사전_한빛미디어
명령어: 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