1. 범용적인 디스어셈블 도구 기능 만들기
// Architecture type
typedef enum cs_arch {
CS_ARCH_ARM = 0, // ARM architecture (including Thumb, Thumb-2)
CS_ARCH_ARM64, // ARM-64, also called AArch64
CS_ARCH_MIPS, // Mips architecture
CS_ARCH_X86, // X86 architecture (including x86 & x86-64)
CS_ARCH_PPC, // PowerPC architecture
CS_ARCH_SPARC, // Sparc architecture
CS_ARCH_SYSZ, // SystemZ architecture
CS_ARCH_XCORE, // XCore architecture
CS_ARCH_MAX,
CS_ARCH_ALL = 0xFFFF, // All architectures - for cs_support()
} cs_arch;
// Mode type
typedef enum cs_mode {
CS_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
CS_MODE_ARM = 0, // 32-bit ARM
CS_MODE_16 = 1 << 1, // 16-bit mode (X86)
CS_MODE_32 = 1 << 2, // 32-bit mode (X86)
CS_MODE_64 = 1 << 3, // 64-bit mode (X86, PPC)
CS_MODE_THUMB = 1 << 4, // ARM's Thumb mode, including Thumb-2
CS_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series
CS_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM
CS_MODE_MICRO = 1 << 4, // MicroMips mode (MIPS)
CS_MODE_MIPS3 = 1 << 5, // Mips III ISA
CS_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA
CS_MODE_MIPSGP64 = 1 << 7, // General Purpose Registers are 64-bit wide (MIPS)
CS_MODE_V9 = 1 << 4, // SparcV9 mode (Sparc)
CS_MODE_BIG_ENDIAN = 1 << 31, // big-endian mode
CS_MODE_MIPS32 = CS_MODE_32, // Mips32 ISA (Mips)
CS_MODE_MIPS64 = CS_MODE_64, // Mips64 ISA (Mips)
} cs_mode;
C
복사
1.
Disasm할 실행 파일 기준으로 정리
a.
load_binary 함수 내에서 파일 로드 시 자체적으로 아키텍쳐, 운영체제 비트를 체크함.
2.
캡스톤의 아키텍쳐, 모드 정의된 타입을 binary 객체에서도 동일하게 사용하여 중복 제거.
basic_capstone_linear.cc
// basic_capstone_linear
/* Linearly disassemble a given binary using Capstone. */
#include <stdio.h>
#include <string>
//#include <capstone/capstone.h>
#include "../inc/loader.h" // 캡스톤 해더를 로더 안에서 받아서 사용
int
disasm(Binary *bin)
{
csh dis;
cs_insn *insns;
Section *text;
size_t n;
text = bin->get_text_section();
if(!text) {
fprintf(stderr, "Nothing to disassemble\n");
return 0;
}
if(cs_open(bin->csarch, bin->csmode, &dis) != CS_ERR_OK) { // 변경
fprintf(stderr, "Failed to open Capstone\n");
return -1;
}
C++
복사
loader.h
#ifndef LOADER_H
#define LOADER_H
#include <stdint.h>
#include <string>
#include <vector>
#include <capstone/capstone.h> // 추가함
...
class Binary {
public:
enum BinaryType {
BIN_TYPE_AUTO = 0,
BIN_TYPE_ELF = 1,
BIN_TYPE_PE = 2
};
// 기존의 arch, mode 타입을 삭제하고 capstone에서 사용하는 타입으로 통일
Binary() : type(BIN_TYPE_AUTO), csarch(CS_ARCH_ALL), csmode(CS_MODE_LITTLE_ENDIAN), entry(0) {}
Section *get_text_section() { for(auto &s : sections) if(s.name == ".text") return &s; return NULL; }
std::string filename;
BinaryType type;
std::string type_str;
cs_arch csarch;
std::string arch_str;
cs_mode csmode;
uint64_t entry;
std::vector<Section> sections;
std::vector<Symbol> symbols;
};
C++
복사
loader.cc
static int
load_binary_bfd(std::string &fname, Binary *bin, Binary::BinaryType type)
{
int ret;
bfd *bfd_h;
const bfd_arch_info_type *bfd_info;
bfd_h = NULL;
bfd_h = open_bfd(fname);
if(!bfd_h) {
goto fail;
}
bin->filename = std::string(fname);
bin->entry = bfd_get_start_address(bfd_h);
bin->type_str = std::string(bfd_h->xvec->name);
switch(bfd_h->xvec->flavour) {
case bfd_target_elf_flavour:
228,3 70%
switch(bfd_h->xvec->flavour) {
case bfd_target_elf_flavour:
bin->type = Binary::BIN_TYPE_ELF;
break;
case bfd_target_coff_flavour:
bin->type = Binary::BIN_TYPE_PE;
break;
case bfd_target_unknown_flavour:
default:
fprintf(stderr, "unsupported binary type (%s)\n", bfd_h->xvec->name);
goto fail;
}
bfd_info = bfd_get_arch_info(bfd_h);
bin->arch_str = std::string(bfd_info->printable_name);
switch(bfd_info->mach) {
case bfd_mach_i386_i386:
bin->csarch = Binary::CS_ARCH_X86;
bin->csmode = Binary::CS_MODE_32;
break;
case bfd_mach_x86_64:
bin->csarch = Binary::CS_ARCH_X86;
bin->csmode = Binary::CS_MODE_64;
break;
default:
fprintf(stderr, "unsupported architecture (%s)\n",
bfd_info->printable_name);
goto fail;
}
/* Symbol handling is best-effort only (they may not even be present) */
load_symbols_bfd(bfd_h, bin);
load_dynsym_bfd(bfd_h, bin);
if(load_sections_bfd(bfd_h, bin) < 0) goto fail;
ret = 0;
goto cleanup;
fail:
ret = -1;
cleanup:
if(bfd_h) bfd_close(bfd_h);
return ret;
}
C++
복사
2. 코드 겹침 난독화 기법 탐지 기능 구현
예제의 선형 디스어셈블 도구는 코드 겹침 기법을 통한 난독화를 대응할 수 있음을 보였다.
그런데 코드 겹침 기법이 존재한다는 명시적인 안내 문구를 출력하지는 않는다.
해당 예제를 개선해, 만약 코드 겹침 난독화의 흔적이 발견됐다면 사용자가 참고할 수 있도록 경고 메시지 를 출력하라.
1.
선형 디스어셈블이 코드 겹침을 체크할 수 있다고...?
•
해당 논문 에 의하면 linear Disasmbler의 경우 Code coverage가 99%로 확인 되었음
◦
실제로 코드 겹침에 의해 A행의 인스트럭션이 오류가 나더라도. A 행 및 A+1행이 오류, A+2부터는 다시 정상적으로 해석이 가능한 것으로 확인 됨.
2.
오피코드가 비정상적인 어셈블리부터 확인
a.
비정상적인 것을 어떻게 체크 할 것인가
i.
조건문 jump 구문(i)인 mnemonic 값을 찾는다
1.
i의 op_str이 0x로 시작되는 직접 점프(immidate jump)인지 확인
a.
op_str을 uint64_t로(점프 주소값) 변환하여 i+1의 address보다 작은지 비교
i.
i의 address가 점프 주소값보다 큰지 비교
1.
클 경우 찾은 i의 op_str에 warning Instruction Overlapping 문자열 추가
명령어 겹침(instruction overlapping) ≠ Break Alignment일 가능성이 있음
6장에서 사용했던 샘플을 이용하여 다시 테스트
원본
objdump -d ori_alignment
0000000000400526 <main>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: bf d4 05 40 00 mov $0x4005d4,%edi
40052f: e8 cc fe ff ff callq 400400 <puts@plt>
400534: bf ec 05 40 00 mov $0x4005ec,%edi
400539: e8 c2 fe ff ff callq 400400 <puts@plt>
40053e: b8 00 00 00 00 mov $0x0,%eax
400543: 5d pop %rbp
400544: c3 retq
400545: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40054c: 00 00 00
40054f: 90 nop
C++
복사
alignment 부순 샘플
./basic_capstone_linear a.out
0x0000000000400526: 55 push rbp
0x0000000000400527: 48 89 e5 mov rbp, rsp
0x000000000040052a: bf d4 05 40 00 mov edi, 0x4005d4
0x000000000040052f: e8 cc fe ff ff call 0x400400
0x0000000000400534: eb 01 jmp 0x400537
0x0000000000400536: e8 bf ec 05 40 call 0x4045f1fa // 처음 깨진 라인 A
0x000000000040053b: 00 e8 add al, ch // A2
0x000000000040053d: bf fe ff ff b8 mov edi, 0xb8fffffe // A3
0x0000000000400542: 00 00 add byte ptr [rax], al // A4
0x0000000000400544: 00 00 add byte ptr [rax], al // A5
0x0000000000400546: 5d pop rbp
0x0000000000400547: c3 ret
C
복사
책에서 말하는 코드 겹침(명령어 겹침)은 단순히 Break alignment 뿐만 아니라, 책의 overlapping 예시처럼 해당 분기문을 기점으로 또다른 명령을 수행하는 것을 원하는듯
0x00000000004005f6: 55 push rbp
0x00000000004005f7: 48 89 e5 mov rbp, rsp
0x00000000004005fa: 89 7d ec mov dword ptr [rbp - 0x14], edi
0x00000000004005fd: c7 45 fc 00 00 00 00 mov dword ptr [rbp - 4], 0
0x0000000000400604: 8b 45 ec mov eax, dword ptr [rbp - 0x14]
0x0000000000400607: 83 f8 00 cmp eax, 0
0x000000000040060a: 0f 85 02 00 00 00 jne 0x400612
0x0000000000400610: 83 f0 04 xor eax, 4
0x0000000000400613: 04 90 add al, 0x90
0x0000000000400615: 89 45 fc mov dword ptr [rbp - 4], eax
0x0000000000400618: 8b 45 fc mov eax, dword ptr [rbp - 4]
0x000000000040061b: 5d pop rbp
0x000000000040061c: c3 ret
C
복사
소스코드 작성 완료
int
disasm(Binary *bin)
{
csh dis;
cs_insn *insns;
Section *text;
size_t n;
text = bin->get_text_section();
if(!text) {
fprintf(stderr, "Nothing to disassemble\n");
return 0;
}
if(cs_open(bin->csarch, bin->csmode, &dis) != CS_ERR_OK) {
fprintf(stderr, "Failed to open Capstone\n");
return -1;
}
n = cs_disasm(dis, text->bytes, text->size, text->vma, 0, &insns);
if(n <= 0) {
fprintf(stderr, "Disassembly error: %s\n", cs_strerror(cs_errno(dis)));
return -1;
}
for(size_t i = 0; i < n; i++) {
printf("0x%016jx: ", insns[i].address);
for(size_t j = 0; j < 16; j++) {
if(j < insns[i].size)printf("%02x ", insns[i].bytes[j]);
else printf(" ");
}
printf("%-12s %s\n", insns[i].mnemonic, insns[i].op_str);// 해당 부분 밑으로 추가
if(insns[i].mnemonic[0] == 'j' && insns[i].mnemonic[1] != 'm') {
if(insns[i].op_str[0] == '0' && insns[i].op_str[1] == 'x') {
uint64_t addr = (uint64_t)strtol(insns[i].op_str,NULL,0);
for(size_t fi = i; fi < n; fi++){
if(insns[fi+1].address > addr){
if(insns[fi].address < addr) {
char wrongstring[45] = "\t[Instruction Overlapping Detection!]";
strcat(insns[fi].op_str,wrongstring);
break;
}
}
}
}
}
}
cs_free(insns, n);
cs_close(&dis);
return 0;
}
./basic_capstone_linear overlapping_bb
0x00000000004005eb: 48 89 e5 mov rbp, rsp
0x00000000004005ee: ff d0 call rax
0x00000000004005f0: 5d pop rbp
0x00000000004005f1: e9 7a ff ff ff jmp 0x400570
0x00000000004005f6: 55 push rbp
0x00000000004005f7: 48 89 e5 mov rbp, rsp
0x00000000004005fa: 89 7d ec mov dword ptr [rbp - 0x14], edi
0x00000000004005fd: c7 45 fc 00 00 00 00 mov dword ptr [rbp - 4], 0
0x0000000000400604: 8b 45 ec mov eax, dword ptr [rbp - 0x14]
0x0000000000400607: 83 f8 00 cmp eax, 0
0x000000000040060a: 0f 85 02 00 00 00 jne 0x400612
0x0000000000400610: 83 f0 04 xor eax, 4 [Instruction Overlapping Detection!]
0x0000000000400613: 04 90 add al, 0x90
0x0000000000400615: 89 45 fc mov dword ptr [rbp - 4], eax
0x0000000000400618: 8b 45 fc mov eax, dword ptr [rbp - 4]
0x000000000040061b: 5d pop rbp
0x000000000040061c: c3 ret
0x000000000040061d: 55 push rbp
0x000000000040061e: 48 89 e5 mov rbp, rsp
0x0000000000400621: 48 83 ec 10 sub rsp, 0x10
0x0000000000400625: 89 7d fc mov dword ptr [rbp - 4], edi
0x0000000000400628: 48 89 75 f0 mov qword ptr [rbp - 0x10], rsi
C
복사
정렬이 조금 깨졌지만, 정상적으로 Instruction overlapping을 탐지하는 것을 확인할 수 있음.
Reference
•
선형 디스어셈블의 코드 겹침 탐지 관련 문서
https://csrc.kaist.ac.kr/blog/2020/06/29/바이너리-역어셈블의-의미와-원리-파헤쳐보기/
•
PE Header Machine 값 (winnt.h)
#define IMAGE_FILE_MACHINE_UNKNOWN 0
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386.
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
JavaScript
복사
•
arm elf 파일 구조