디스어셈블 라이브러리 "Capstone" 빌드 및 사용법
뻘짓

디스어셈블 라이브러리 "Capstone" 빌드 및 사용법

 자... 지난번에는 디스어셈블 라이브러리로 Zydis를 소개 했었다. 이 라이브러리는 속도가 빠르고 가벼우며, 인스트럭션의 상세정보를 확인하기가 비교적 수월하다는 평을 받는다. 이 라이브러리를 빌드해서 사용 해 보고 싶다면, 이 글이 도움이 될 것이다. https://nitwit.tistory.com/15

 

디스어셈블 라이브러리 "Zydis" 빌드하기

분석가들과 개발자, 해커들의 영원한 친구 OllyDBG 그리고 그의 친구 X64DBG 의 그것이다. 얘네들은 내부적으로 Zydis, XED 라는 디스어셈블/어셈블 오픈소스 라이브러리를 사용하여 사용자에게 UI로

nitwit.tistory.com

 

 하지만 단점을 굳이 뽑자면, X86-64 가 아닌 아키텍쳐들은 지원하지 않는다는 점 정도일 것이다. ARM 이나 MIPS 같은 다른 아키텍쳐의 인스트럭션을 해석하고 싶다면 다른 대안이 있다. https://github.com/capstone-engine/capstone

 

GitHub - capstone-engine/capstone: Capstone disassembly/disassembler framework: Core (Arm, Arm64, BPF, EVM, M68K, M680X, MOS65xx

Capstone disassembly/disassembler framework: Core (Arm, Arm64, BPF, EVM, M68K, M680X, MOS65xx, Mips, PPC, RISCV, Sparc, SystemZ, TMS320C64x, Web Assembly, X86, X86_64, XCore) + bindings. - GitHub -...

github.com

우선 위 링크에서 라이브러리를 다운로드 받는다. 이 라이브러리는 Zydis 보다 빌드하기가 더 쉽고 친절하다.

msvc 폴더 밑에 비주얼스튜디오로 빌드 할 수 있도록, 이미 솔루션이 제공되어있다. 

 

 솔루션 파일을 열어 프로젝트 목록을 확인해 보면 다양한 아키텍쳐별로 테스트코드와 함께 메인 라이브러리 2종이 제공됨을 확인 할 수 있다. 각각 정적/동적 라이브러리이다. 입맛대로 빌드해서 쓰면 된다.

 그대로 빌드 하면 아무 문제 없이 잘 빌드된다. static 라이브러리로 사용하고 싶으면 capstone_static 프로젝트, dll 링크 방식으로 사용하고 싶으면 capstone_dll 만 따로 빌드해도 된다. 잘 빌드 되었다면 아래와 같은 디렉터리에 출력물이 생긴다. 나는 64비트 릴리즈모드로 빌드 했으므로 ~capstone-master/msvc/x64/Release 경로에 출력되었다.

capstone.lib 파일은 static한 라이브러리 이고, capstone_dll.lib 는 dll과 같이 배포되는 동적 라이브러리이다.

 


 

 그럼 이번엔 테스트 프로젝트를 확인해보자. 이번엔 ARM 디스어셈블러를 테스트 해보자.  test_arm.c 파일을 열어보면, 아래와 같이 임의의 기계어코드를 넣어 둔 것을 확인 할 수 있다. 실제로 사용할때엔, ELF, PE 등을 해석해서 엔트리포인트로부터 기계어코드를 해독 하면 될 것이다. ARM 에는 일반 운용모드와 THUMB 모드 등으로 전환될 수도 있는데, 이러한 모드들에도 전부 대응이 가능한 강력한 라이브러리이다.

ARM의 다양한 모드에서의 기계어코드

 X86-64와는 다르게 레지스터 이름도 다르다. ARM 인스트럭션은 명령어 길이가 고정이라서 분석이 더 편하지 않을까 예상 했었는데, 오히려 그 고정된 길이안에 다양한 코드를 표현해야하므로 복잡성이 더 증가되는 것 같다.

capstone 라이브러리는 cs_open 함수로 디스어셈블러를 초기화하고, cs_disasm 함수로 디스어셈블 한다.

 cs_open 함수는 cs_arch cs_mode 에 맞는 디스어셈블러를 초기화하여 csh에 던져주게된다. 앞으로 cs_disasm 함수를 이용해 디스어셈블을 수행할땐, 이 csh 핸들이 필요하다. cs_option 함수는 디스어셈블러의 세부 설정을 적용하는 함수인데, 나는 오퍼랜드 해석, 주소지정방식 등의 상세정보가 필요하여 해당 옵션을 켜주었다. 일단은 그냥 넘어가자.

 실질적으로 디스어셈블을 수행하는 cs_disasm 함수의 원형이다.

handle 로는 아까 cs_open 함수로 얻어냈던 핸들을 넣어주면되고

code 인자에는 디스어셈블할 데이터가 들어있는 포인터를 넘겨주면 된다.

code_size 에는 해당 데이터의 전체 크기를 넣어주면 된다.

address 인자는 주소 해석을 위해 사용하는 옵션인데 0을 넣어도 되지만 실제로 메모리에 올라간 실행파일을 디스어셈블 할때는 메모리 주소를 고려하여 디스어셈블 하는것이 좋을것이다. 그때 사용하는 옵션이다. 이 명령어가 어느 주소에 있는지 일러주면 된다.

count 는 해석할 명령어의 갯수를 넣어주면 되는데, code_size 에 맞게 되는대로 전부 디스어셈블 하고 싶으면 0을, 그렇지 않으면 원하는 갯수를 넣어주면 된다. (한개씩 디스어셈블 하고 싶으면 당연히 1을 넣으면 된다.)

insn 에는 cs_insn구조체의 포인터를 반환받을 주소를 지정해주면 되는데, 이 구조체엔 디스어셈블된 결과가 들어간다. 해석된 인스트럭션의 중요한 정보를 다 담고있는데 대략 이런 정보들이 들어간다.

 인스트럭션의 니모닉과 오퍼랜드에 대한 정보, 그리고 cs_option 함수로 상세정보까지 받도록 설정했다면 detail 멤버에는 주소지정방식이나 읽기/쓰기모드에대한 정보까지도 기록된다.

 detail 멤버는 이러한 정보를 담고 있으며, x86 - arm64 - arm .... 부터 이어지는 멤버들은 union 으로 선언되어있다. 이 경우에는 ARM 32비트 코드에대한 인스트럭션이므로, arm 멤버만 참고하면 되고, 나머지 x86, arm64, mips 등등의 멤버는 유효하지 않으므로 무시하면 된다. 

 이런 정보를 잘 사용하고나면 이 insn 을 해제해주어야 한다. 내부적으로 cs_malloc 함수를 호출해서 할당받으므로, 이 과정을 빼먹으면 누수가 생길것이다. cs_free 함수로 해제하면 된다. 

 

insn 인자엔 아까전에 cs_disasm 에서 받은 녀석을 올려주면 되고

count 인자는 cs_disasm 이 해석한 인스트럭션의 갯수이다.

 

cs_disasm 함수는 해석한 인스트럭션 갯수를 반환하게 되는데, cs_free 할때도 이 값을 그대로 넘겨주면 누수는 없다.