어셈블리어와 gdb란? (어셈블리어의 문법 및 GDB 명령어 소개)

어셈블리어와 gdb란? - 1편 (어셈블리어의 문법 및 GDB 명령어 소개)

C언어, 혹은 C++로 대표되는 컴파일 언어들은 해당되는 언어로 작성된 뒤, 컴파일러를 통해 기계어로 번역되는 특징을 갖고 있다. 그리고 실행이 될 때에는, 이 기계어가 cpu위에 올라가, 저번 포스트 에서 다루었던 cpu의 레지스터들을 기계어 명령어를 통해 순차적으로 실행된다.

어셈블리어

기계어는, 0, 1(전기가 통하지 않는다 / 전기가 통한다, 혹은 전기가 약하게 흐른다 / 전기가 세게 흐른다)로만 표현하기 때문에 사람이 알아보기 힘들다는 문제점이있다.
이런 기계어와 1:1 매칭시켜 사람이 비교적 알아보기 쉽게 한 언어가 있는데, 이 언어는 “어셈블리”(Assembly)라고 부른다.
어셈블리어의 문법은 통일된 규격이 없어 아키텍쳐(architecture)에 따라 달라지는데, 우리가 흔히 사용하는 x86 계열의 cpu에서는 주로 intel 방식과 at&t 방식이 있다.
이 둘의 차이점을 간단히 다루자면 다음과 같다.
  • 레지스터 접근법
    • intel: eax
    • at&t: %eax
  • 피연산자(operand)의 순서: eax 레지스터 안에 0이라는 값을 넣는 명령어
    • intel: mov eax, 0
    • at&t: mov 0, %eax
      intel은 왼쪽에 오른쪽의 값을 넣는 형태이고, at&t는 왼쪽의 값을 오른쪽에 넣는 형태이다.
      이외에도 몇가지 명령어의 명칭이 다르다던가 하는 점이 있다. 다 다루지는 않겠으나, 더 깊게 알고싶다면 상단의 참고한곳의 맨 위 링크를 참고하는것을 추천한다.
      본 글의 작성자는 이러한 문법의 선호도는 전적으로 취향의 영역이라고 생각하며, 본인에게 유리하고 편한 것을 사용하면 된다.

GDB

gdb는, GNU Debugger의 줄임말로써, unix-like(예를들자면, 리눅스 혹은 맥os. 편의성을 위해 이하 리눅스라 부르겠음) 시스템에서 작동하는 디버거이다.
디버거란, 디버그(debug)를 하기 위해 사용되는 소프트웨어이다. 디버그는 버그를 잡는다는 뜻을 갖고있다.
gdb는 기계어를 어셈블리어로 바꾸어서 보여주는 것을 기본으로, 흐름을 멈추어 문제인 부분을 찾게 도와주는 등의 기능을 갖고있다.
윈도우에서는 비슷한 역할을 하는 소프트웨어로 ollydbg(올리디버거라고 불린다)를 주로 사용한다고 한다.
어셈블리어의 문법에 대해 말하자면, gdb에서는 at&t 문법이 기본값으로 설정되어있고, 올리디버거에서는 intel 문법이 기본값으로 설정되어있다. (따라서 기본값으로 제공되는 어셈블리어 문법을 사용할 경우 정보를 찾고 사용함에 있어 유리한 부분이 있을것이다)
gdb는 cui위에서 돌아가는 프로그램이기 때문에 명령어로 동작하는데, 모든 명령어를 다루기엔 꽤 분량이 길어지니, 리버스 엔지니어링을 하는데 주로 사용되는 내용들만 이곳에 서술하도록 하겠다.
여기서 말하는 메모리는
quit: gdb를 종료
q 으로 줄여 쓸 수도 있다.
set disassembly intel: set(설정)+disassembly(해체) intel 문법으로 변경
set disassembly att: set(설정)+disassembly(해체) at&t 문법으로 변경
run: gdb 안에서 프로그램을 실행
break <메모리 주소="">: 실행 도중 흐름을 멈추게 할 위치를 정함
nexti: 흐름을 멈추었을때, 다음 명령어(메모리에 있는 명령어)로 넘김
ni로 줄여 쓸 수도 있다.
disas <함수 이름="">: disassembly(해체) <함수 이름=""> 이라는 명령어에서 알 수 있듯, 한 함수의 어셈블리어 코드를 출력
info registers <레지스터 이름="">: 레지스터안에 들어있는 값을 출력. 레지스터 이름이 입력되지 않으면 모든 레지스터안에 들어있는 값을 출력
i r <레지스터 이름=""> 으로 줄여 쓸 수도 있다.
x/<표기법> <메모리 주소="">: 해당하는 메모리주소의 메모리를 조사한다.레지스터 이름을 적을경우 해당하는 레지스터 안에있는 값이 변수처럼 메모리주소로 사용된다.
표기법으로는 개수를 가르키는 숫자와(기본값 1),
진법을 가르키는 알파벳,
  • o: 8진법으로 출력
  • x: 16진법으로 출력, 기본값
  • u: unsigned 표준 10진법으로 출력
  • t: 2진법으로 출력
    그리고 출력 하나당 크기를 가르키는
  • b: byte
  • h: half-word(2 byte)
  • w: word(4 byte), 기본값
  • g: giant(8byte)
    이해가 가지 않는다면 다음 명령어를 보자: x/2xw $eip는 eip안에 들어있는 메모리주소부터(c언어의 포인터를 생각하면 좋다.) 두개의 메모리 영역의 값을 하나당 4바이트씩 16진수로 출력한다.
이정도만 알아도, gdb를 사용하는것에는 문제가 없을것이다. 2편에서는 어셈블리어에 대해 알아보도록 하겠다.

Comments

Popular Posts