티스토리 뷰

728x90

리눅스 시스템에서 프로그래밍을 하다 보면 프로그램 빌드와 실행 이외에도 여러 가지 분석이 필요한 경우가 있다. 좋은 도구들을 활용할 줄 아는 것은 시간을 아끼는 것뿐만 아니라, 새로운 아이디어와 지식의 기반이 되기도 한다. 몇 가지 도구들을 메모해 둔다.

 

■ 파일 형태 파악하기(file)

위의 그림에서 보듯이 "which" 명령은 특정 실행 파일이 어디에 위치하고 있는지 확인할 수 있다. "file"명령의 인수로 file 실행 파일의 위치를 전달하면 ELF(Executable and Linkable Format) 파일, 즉 바이너리 파일이란 것을 알려주고 운영체제에 호스트 이름을 등록하는 /etc/hosts 파일을 인수로 전달하면 단순 아스키 텍스트 파일임을 알 수 있다. 실행 파일처럼 동작하지만 실제로는 바이너리 파일이 아닌 쉘 스크립트는 아스키 텍스트 파일 또는 "Perl script text executable"등으로 표시하고 실제 파일이나 폴더가 아닌 심볼릭 링크는 "symbolic link to......" 식으로 표시해 준다. 파일 형태를 파악하는 것이 분석의 시작이다.

 

osboxes@osboxes:~$ file -bi /usr/bin/autoconf
text/x-shellscript; charset=us-ascii

위의 예제처럼 "-bi" 옵션을 주면 단순 ascii 인지 utf-8인지 텍스트 파일의 인코딩도 확인할 수 있다.

 

■ 사용하는 공유 라이브러리 파악하기(ldd)

대부분의 리눅스 바이너리 실행 프로그램들은 공유 라이브러리를 사용하기 마련인데, 프로그램이 사용하는 공유 라이브러리에는 어떠한 것들이 있는지 확인할 수 있는 명령이다.

 

■ 파일 내용 파악하기(hexdump, strings)

쉘 프로그램이나 단순한 텍스트 파일이라면 vi, vim. emacs와 같은 도구로 파일을 열어 볼 수 있지만, 실행 프로그램이나 데이터베이스 파일과 같은 바이너리 파일의 경우에는 텍스트 편집기로 열어 볼 수 없다. 이럴 때 사용할 수 있는 것이 hexdump나 strings 도구로 모두 사람이 인식할 수 있는 형태로 변환하거나 사람이 인식할 수 있는 문자열만 뽑아내는 도구이다. 위의 그림처럼 "-C" 옵션으로 hexdump를 수행하면 파일을 위치, 16진수 내용, 문자 내용의 형태로 표시한다. strings 도구는 그림과 같이 이진 파일에서 사람이 볼 수 있는 문자열을 차례대로 모두 뽑아낸다.

 

■ 디버깅 연관 도구(nm, gdb)

실행 프로그램을 제작하는 과정에서 디버깅을 위해 "-g" 옵션을 부여한 경우에 유효하게 사용할 수 있는 도구들이다. nm 도구는 프로그램 영역별로 어떤 심벌들이 있는지 알려준다. 위의 그림에서는 C 프로그램의 시작점인 main이 코드 영역에 있음을 보여준다. "-g" 옵션으로 빌드한 프로그램은 gdb 도구를 활용하여 소스 코드를 확인하며 스텝 단위로 실행하며 수행 과정을 확인할 수 있고, 실행 중에 프로그램에 접속하여 현재 수행 상태를 확인할 수도 있다. 자세한 사용법은 생략한다.

 

■ 시스템 호출 과정 추적하기(strace)

osboxes@osboxes:~$ strace /usr/bin/file /etc/hosts
execve("/usr/bin/file", ["/usr/bin/file", "/etc/hosts"], 0x7ffc5c75bb78 /* 64 vars */) = 0
brk(NULL)                               = 0x5571dcb97000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=122047, ...}) = 0
mmap(NULL, 122047, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8eec27c000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libmagic.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340:\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=137448, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8eec27a000
mmap(NULL, 2233320, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8eebe4f000
mprotect(0x7f8eebe6f000, 2093056, PROT_NONE) = 0
mmap(0x7f8eec06e000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1f000) = 0x7f8eec06e000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20\35\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030928, ...}) = 0
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8eeba5e000
mprotect(0x7f8eebc45000, 2097152, PROT_NONE) = 0
mmap(0x7f8eebe45000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f8eebe45000
mmap(0x7f8eebe4b000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f8eebe4b000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libz.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220\37\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=116960, ...}) = 0
mmap(NULL, 2212016, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8eeb841000
mprotect(0x7f8eeb85d000, 2093056, PROT_NONE) = 0
mmap(0x7f8eeba5c000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b000) = 0x7f8eeba5c000
close(3)                                = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8eec277000
arch_prctl(ARCH_SET_FS, 0x7f8eec277740) = 0
mprotect(0x7f8eebe45000, 16384, PROT_READ) = 0
mprotect(0x7f8eeba5c000, 4096, PROT_READ) = 0
mprotect(0x7f8eec06e000, 8192, PROT_READ) = 0
mprotect(0x5571dc23b000, 4096, PROT_READ) = 0
mprotect(0x7f8eec29a000, 4096, PROT_READ) = 0
munmap(0x7f8eec27c000, 122047)          = 0
brk(NULL)                               = 0x5571dcb97000
brk(0x5571dcbb8000)                     = 0x5571dcbb8000
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=4326016, ...}) = 0
mmap(NULL, 4326016, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8eeb420000
close(3)                                = 0
stat("/home/osboxes/.magic.mgc", 0x7ffdc5e6b190) = -1 ENOENT (No such file or directory)
stat("/home/osboxes/.magic", 0x7ffdc5e6b190) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/magic.mgc", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/etc/magic", {st_mode=S_IFREG|0644, st_size=111, ...}) = 0
openat(AT_FDCWD, "/etc/magic", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=111, ...}) = 0
read(3, "# Magic local data for file(1) c"..., 4096) = 111
read(3, "", 4096)                       = 0
close(3)                                = 0
openat(AT_FDCWD, "/usr/share/misc/magic.mgc", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=4961856, ...}) = 0
mmap(NULL, 4961856, PROT_READ|PROT_WRITE, MAP_PRIVATE, 3, 0) = 0x7f8eeaf64000
close(3)                                = 0
mprotect(0x7f8eeaf64000, 4961856, PROT_READ) = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache", O_RDONLY) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=26376, ...}) = 0
mmap(NULL, 26376, PROT_READ, MAP_SHARED, 3, 0) = 0x7f8eec293000
close(3)                                = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8eec176000
lstat("/etc/hosts", {st_mode=S_IFREG|0644, st_size=222, ...}) = 0
stat("/etc/hosts", {st_mode=S_IFREG|0644, st_size=222, ...}) = 0
openat(AT_FDCWD, "/etc/hosts", O_RDONLY) = 3
fcntl(3, F_GETFL)                       = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fcntl(3, F_SETFL, O_RDONLY|O_LARGEFILE) = 0
read(3, "127.0.0.1\tlocalhost\n127.0.1.1\tos"..., 1048576) = 222
pread64(3, "127.0.0.1\tlocalhost\n127.0.1.1\tos"..., 512, 0) = 222
brk(0x5571dcbd9000)                     = 0x5571dcbd9000
brk(0x5571dcbfa000)                     = 0x5571dcbfa000
brk(0x5571dcc1b000)                     = 0x5571dcc1b000
brk(0x5571dcc3c000)                     = 0x5571dcc3c000
brk(0x5571dcc5d000)                     = 0x5571dcc5d000
brk(0x5571dcc7e000)                     = 0x5571dcc7e000
brk(0x5571dcc9f000)                     = 0x5571dcc9f000
brk(0x5571dccc0000)                     = 0x5571dccc0000
brk(0x5571dcc9c000)                     = 0x5571dcc9c000
munmap(0x7f8eec176000, 1052672)         = 0
close(3)                                = 0
write(1, "/etc/hosts: ASCII text\n", 23/etc/hosts: ASCII text
) = 23
munmap(0x7f8eeaf64000, 4961856)         = 0
exit_group(0)                           = ?
+++ exited with 0 +++
osboxes@osboxes:~$

실행 프로그램의 동작 과정은 많은 경우 운영체제에게 무언가를 요청하고 응답받는 과정으로 이루어지므로 프로그램의 시스템 호출 과정을 분석하는 것만으로도 프로그램의 동작 과정을 상당 부분 파악할 수 있다. 위의 내용은 strace 도구로 "/usr/bin/file /etc/hosts" 명령이 수행되는 과정의 시스템 호출을 분석한 것이다.

 

728x90
댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함