쓰레드?

  • User-Level Thread
    • 애플리케이션이 쓰레드를 관리(생성, 소멸, 스케줄링)
    • 커널은 쓰레드 존재 여부를 알지 못함
    • 장점
      • 커널 권한을 요구하지 않기 때문에 context switching 빠름
      • 스케줄링 알고리즘을 커널과 독립적으로 구성 가능
      • 기존 OS 상관 없이 작동 가능, 하위 커널에 독립적
      • 정리 : concurrent processing, fast response, multiprocessing, resource sharing
    • 단점
      • 특정 쓰레드가 시스템콜을 호출할 경우 block되어서 모든 쓰레드도 멈춤
      • 따라서 시스템콜을 호출할 장점을 활용 못함
      • 또한 멀티 쓰레드라고 하더라도 프로세스엔 하나의 프로세서만 할당 가능, 따라서 장점을 완전히 활용은 불가능

 

  • Kernel-Level Thread
    • 커널이 쓰레드 생성, 소멸, 관리
    • 장점
      • 하나의 쓰레드가 시스템콜을 호출해서 block되어도 다른 쓰레드는 실행 가능(concurrency & parallel)
      • OS Kernel 자체도 멀티쓰레딩 가능 - 효율이 좋아짐
    • 단점
      • Context switching 오버헤드가 (커널 레벨로  계속 들어감)
      • 쓰레드 생성 오버헤드가 비교적 (커널 레벨로 들어가야 하기 때문) : 유저레벨 쓰레드의 10

 

  • Combined Approach
    • 장점을 합친 모델
    • 유저 레벨 쓰레드를 같거나 적은 수의 커널 레벨 쓰레드에 매핑됨

 

  • 쓰레드를 쓰냐?
    • 작업 단위가 프로세스일 경우
      • 예를들어 프로세스가 IO 기다리면 전체 작업의 진행이 멈춤
      • IO 기다리면서 다른 작업을 하기 위해선 새로운 프로세스를 생성 - 오버헤드 발생
      • 하나의 프로세스를 쓰레드라는 작업 단위로 나누고. 프로세스 자원을 공유하면서 CPU Utilization, 자원 활용을 높일 있음 

 

 

  • Application for Multicores
    • Multithreaded native application
      • 여러개의 쓰레드로 구성된 소수의 프로세스로 개발
      • IBM(Lotus) Domino, Oracle(Siebel) CRM(Customer Relationship Manager)
    • Multiprocess applications
      • 싱글 쓰레드 프로세스를 여러개로 구성
      • Oracle DB, SAP
    • Java applications
      • JVM 자체가 싱글 프로세스에 멀티 쓰레드로 구현되어 있음
      • Sun’s Java Application server, IBM’s websphere, J2EE (Java2 Enterprise Edition) application
    • Multi-instance applications
      • 하나의 애플리케이션을 여러 실행할 별도의 프로세스로 만들어지도록(e.g. 브라우저)

Multithreading

  • 하나의 프로세스 안에서 여러 개의 쓰레드를 통해 작업 단위를 나누는 것
    • 프로세스는 자원 할당의 단위
    • 쓰레드는 CPU 스케쥴링의 단위
      • 쓰레드의 실행 상태(execution state)는 프로세스보다 제한적임
      • ready, run, blocked 세 상태
      • suspend는 프로세스 단위
      • 쓰레드의 실행 상태를 관리할 수 있는 Thread Control Block이 필요함
        • 쓰레드별로 register context, 실행 스택을 관리 
  • Single-threaded approach
    • 한 개의 프로세스에 한 개의 쓰레드로 이루어진 전통적인 접근 방법
    • 쓰레드의 개념이 없음
    • MS-DOS, old UNIX
  • Multi-threaded appraoch
    • 하나의 프로세스가 여러 개의 쓰레드로 이루어짐
      • Java run-time env
    • 여려 개의 프로세스가 각자 여러 개의 쓰레드로 이루어짐
      • Windows, Solaris, modern UNIX
  • 장점
    • 자원을 공유하기 때문에 새로운 쓰레드 생성이 빠름 (프로세스 생성보다 10배 빠름)
    • 종료 시에는 자원을 release해줄 필요가 없기 때문에 종료도 빠름
    • 쓰레드 간의 Context switching이 빠름
    • 쓰레드 간의 communication 속도가 빠름
      • 프로세스 간의 communication은 시그널을 이용하기 때문에 커널의 개입이 필요
      • 쓰레드 간의 communication은 shared memory를 이용하기 때문에 빠름
    • Faster response
      • 하나의 쓰레드가 block 되어도 다른 쓰레드를 실행이 가능
    • Parallel processing 
      • 멀티코어 시스템에서 여러 프로세서가 여러 쓰레드를 병행 처리가 가능함

Single thread vs Multi thread

 

 

'Study > OS' 카테고리의 다른 글

3. Interrupt / Exception  (0) 2020.02.28
2. 프로세스  (0) 2020.02.28
1. 운영체제  (0) 2020.02.28
  • Interrupt / Exception : 예외적인 상황
    • Interrupt : 
      • 외부적인 이벤트, 현재 CPU에서 실행되는 프로세스와 상관 없음
      • keyboard interrupt, Direct Memory Access 완료를 CPU에게 알리는 상황
      • asynchronous(언제 발생할지 모름), external events
      • 인테럽트가 발생하면 현재 프로세스의 상태를 저장, 인테럽트 핸들러 실행 복구
      • 프로세서의 인테럽트 핀이 설정됨
        • #INT
        • #NMI (Non-maskable interrupt) : 우선순위가 #INT 보다 높음 (interrupt disable도 무시)
      • examples
        • I/O interrupt
          • 키보드 ctrl + c 입력, 키보드 입력
          • 네트워크 패킷 수신
          • DMA(Direct Memory Accessing) 완료
        • Hard reset interrupt : reset 버튼 누르기
        • Soft reset interrupt : ctrl + alt + delete
      • 정리
        • 1. 명령어 실행
        • 2. interrupt 핀 확인
        • 3. system bus의 interrupt vector 확인
        • 4. interrupt vector code 실행 (control transfer)
        • 5. handler returns to next instruction

 

    • Exception : 특정 실행중인 프로세스의 CPU 핸들링할 없는 특정 명령어가 발생한 상황(e.g. pagefault) 
      • synchronous(항상 같은 위치에서 동일 조건에서 발생), internal events
      • 종류
        • Traps
          • 의도적인 상황 (명령어 실행을 마치고 의도적으로 멈추는 상황)
          • 명령어 실행을 마치자 마자 발생하게 하는
          • ex) debugging, breakpoints, system calls
        • Faults
          • 의도적이지 않은 상황
          • 현재 특정 명령어의 실행을 마치지 못함, 명령어 실행이 중단됨
          • NaN, divide by zero 
          • Recover 가능할 경우 해당 명령어를 다시 실행
        • Aborts
          • 심각한 하드웨어 에러
          • 현재 프로세스를 종료 또는 전체 시스템이 종료될 있음
          • MCA(Machine Check Abort), Parity error

'Study > OS' 카테고리의 다른 글

4. 쓰레드  (0) 2020.02.28
2. 프로세스  (0) 2020.02.28
1. 운영체제  (0) 2020.02.28

프로세스?

  • 실행중인 프로그램의 인스턴스
    • 프로그램은 디스크에 저장된 실행 파일, 이게 실행이 되면 프로세스
  • Provides two key abstractions
    • Logical Control Flow
      • 프로세스는 프로세서를 독점적으로 사용함
    • Private Address Space
      • 프로세스는 메모리를 독점적으로 사용함
  • Life & Scope
    • Life : 변수가 메모리에 있으면 살아있는
    • Scope : access 가능한 영역
  • Context Switching
    • Context?
      • PC, General Purpose Register ,Floating Pointer Register, Status Registers, and Various kernel data structures (page table, file table)
      • 이걸 PCB라고 , 중에서 register block context라고
    • Switching?
      • 현재 실행하는 프로세스의 Context 저장하고, 다음에 실행할 프로세스의 state 복구
      • 어떤 프로세스를 실행할지? - Scheduler 또는 Dispatcher

 

    • PCB (Process Control Block)
      • Process Identifier
        • 프로세스 ID 숫자로 부여됨
      • Process State Information
        • User-Visible Registers
          • General Purpose Register라고도
          • ex) Integer register
        • Control and Status Registers
          • Program counter
          • Condition codes : 산술연산의 결과 (sign, zero, carry, equal, overflow)
          • Status information : (interrupt enable/disable flag, execution mode)
        • Stack Pointers
      • Process Control Information
        • Scheduling and State information : 스케줄링과 관련된 정보를 보관
          • process state 
          • priority
          • scheduling-related information
          • event
        • Data structuring
          • Parent - Child relation
        • Interproces communication (signal)
        • Process privileges
        • Memory management
        • Resource ownership and utilization
    • PSW (Program Status Word)
      • 프로세스의 실행 상태(status) 관리하는 레지스터
      • 현재 실행중인 프로세스의 condition code 상태 정보를 저장함
      • ex) x86 EEFLAGS 레지스터

 

  • Process Creation and Termination
    • Process spawning
      • OS 새로운 프로세스를 만듬
      • 새로운 프로세스는 부모 프로세스에서 만들어짐(fork)
    • Process termination
      • EXIT 시스템콜 호출로 스스로 종료 가능
      • 과거 batch 시스템에서는 HALT라는 명령어 사용
      • 프로세스가 할당되지 않은 영역을 참조하거나, 메모리 초과, 오버플로우, 언더플로우, 부모 프로세스가 죽을 같이 종료될 있음

 

  • Suspended processes
    • Swapping
      • 메모리 상의 모든 프로세스가 IO 기다릴 있음
      • 이런 상황에서 메모리 상의 일부 프로세스를 디스크로 옮기고, 디스크 상의 다른 프로세스를 메모리로 가져옴
    • Suspended Process
      • Swap으로 실행이 불가능한 프로세스

'Study > OS' 카테고리의 다른 글

4. 쓰레드  (0) 2020.02.28
3. Interrupt / Exception  (0) 2020.02.28
1. 운영체제  (0) 2020.02.28

운영체제?

  • 하드웨어와 애플리케이션 사이에 있는 시스템 소프트웨어
  • 목적?
    • 사용을 편하게 - Low-level 하드웨어를 조작할 있는 간단한, 통합된 인터페이스 제공
    • 공유 - 하드웨어 자원을 여러 프로세스/사용자가 공유할 있도록
    • 보호 - 하드웨어를 잘못된 사용으로부터 보호함
  • Key Interfaces
    • ISA (Instruction Set Architecture) : x86, ARM, MIPS (Machine Language)
      • SW  HW 사이의 인터페이스를 정의
    • ABI (Application Binary Interface)
      • 시스템콜 인터페이스를 정의함
    • API (Application programming interface)
  • CISC (Compex Instruction Set Computer)
    • 복잡한 명령어 집합
    • 명령어의 수가 많음
    • 80년대 중반까지 사용 (x86, VAX)
  • RISC (Reduced Instruction Set Computer)
    • 간단한 명령어 집합
    • 적은 수의 명령어
    • 80년대 이후의 대부분의 아키텍처에 적용
      • ex) PowerPC, Alpha, SPARC,  IA64, ARM
  • Revolution of OS (순서대로)
    • Serial Processing
      • Turing Machine (1936)
        • 새로운 수학적인 컴퓨팅 모델
        • 현대 컴퓨터의 이론적인 배경
        • 현존하는 모든 컴퓨터는 튜링 머신의 Subset
      • Bombe(1940)
        • 독일 에니그마를 해독하기 위해 만들어진 최초의 전자식 컴퓨터
      • Colossus(1943)
        • 1세대 Programmable Digital Computer (Hard-wired Programming)
      • ENIAC (Electronic Numerical Integrator And Computer)
        • 탄도 계산을 위해서 만들어짐
    • Simple Batch Systems
      • Monitor
        • 사용자가 작업(job) 카드 또는 테이프에 작성한 제출 - 순서대로 처리
        • 모니터란 프로그램은 메모리에 상주, 인풋을 하나씩 처리함
        • 프로세서에 비해 입출력이 굉장히 느리기 때문에 idle 상태가 오래 지속됨 - 비효율
    • Multiprogrammed Batch Systems
      • I/O 기다리지 않고 시간에 다른 작업(job) 처리함
      • Processor Utilization 좋음
      • 자신의 작업을 처리하기 위해 이전의 작업을 기다려야
    • Time Sharing Systems
      • 여러 사용자가 동시에 하나의 컴퓨터를 사용 가능
      • 사용자가 기다리는 response time 줄어듬 (Minimizing Response time is more important than maximizing throughput )
      • Time slice / Round Robin
      • Context Switching

 

Competing Processes

  • Sharing a global resources? 다음 문제가 발생 가능
    • Need for mutual exclusion
    • Deadlock
    • Starvation
  • Mutual Exclusion?
    • 상호 배제
    • 한번에 프로세스만 리소스를 사용하도록 제한하는
    • Critical Section 프로세스만 진입하도록 보장해야
    • 데커알고리즘
    • 간단하게 처리하는 방법?
      • 임계 영역에 들어갈 interrupt disable, 나올 enable

 

'Study > OS' 카테고리의 다른 글

4. 쓰레드  (0) 2020.02.28
3. Interrupt / Exception  (0) 2020.02.28
2. 프로세스  (0) 2020.02.28

리버스 엔지니어링 바이블


1. API 후킹 기본

단순히 API의 엔트리 포인트를 jmp 문으로 바꾸면 후킹 탐지에 쉽게 무력화 가능



ws2_32.dll의 send 함수


해당 함수는 위와 같이 mov edi, edi로 시작하며 일반적으로 API 후킹을 하게 되면 아래와 같이 해당 엔트리 포인트를 jmp 문으로 바꾸게됨


API 후킹 이후엔 위와 같이 엔트리 포인트 5바이트만 수정됨


이러한 방법으로는 다른 애플리케이션과 충돌이 가능..



그래서 이렇게 코드 중간에 점프 코드를 삽입하는 방법을 사용하면 코드 후킹 탐지도 피할 수 있고 다른 애플리케이션과 충돌도 피할 수 있음


*사진은 예시를 위해 임의의 명령어와 주소를 삽입함



2. DLL 인젝션


코드 후킹을 위해선 DLL 인젝션이 선행되어야 함.


대부분 흔히 널려있는 DLL 인젝션 툴들은 백신에 악성코드로 진단되기 때문에 직접 만들어서 구현해보는 것도 좋음


2.1. CreateRemoteThread 

 DLL 인젝션의 핵심 원리는 CreateRemoteThread API임

 해당 API는 타 프로세스의 메모리에 스레드를 생성할 수 있는 기능을 지원함

 일반적으로 DLL 로딩은 다음과 같은 코드를 사용함



 위 코드는 C:\Windows\system32\ws2_32.dll을 메모리에 로딩하는 코드임

LoadLibrary() 를 호출해 DLL 파일의 경로를 전달하면, 그 프로세스 메모리에 위 DLL이 로딩됨

CreateRemoteThread() 호출하고 해당 프로세스에서 LoadLibrary() 실행, 인자로 들어가는 DLL 파일의 전체 경로만 지정하면 DLL 인젝션 가능


구현은 다음과 같은 과정으로 가능함



void InjectionDll(DWORD pid, LPCSTR dll)
cs

위와 같이 타겟이 될 프로세스의 pid와 인젝션 할 dll을 두 번째 인자로 받는 구조로 선언
 

HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
cs

타겟 프로세스의 핸들이 필요하므로 OpenProcess()로 pid를 지정한 후 핸들 hProcess를 얻음


LPVOID lpAddr = VirtualAllocEx(hProcess, NULL, strlen(dll)+1, MEM_COMMIT, PAGE_READWRITE);
cs

타겟 프로세스에 LoadLibrary()로 넘겨줄 dll의 주소를 써 넣기 위해 메모리를 할당해줌
* VirtualAlloc은 자신의 프로세스에 할당, VirtualAllocEx는 타 프로세스에 메모리 할당
크기는 dll 경로 문자열이 들어갈 공간이면 충분하므로 Null 문자를 위한 strlen(dll)+1 로 설정
위 함수의 반환 값인 lpAddr에는 DLL의 전체 경로가 담긴 메모리 번지가 되고 이를  LoadLibrary()와  CreateRemoteThread() 에 전달함



WriteProcessMemory(hProccess, lpAddr, dll, strlen(dll)+1NULL);
cs

할당해준 메모리에 dll의 전체 경로를 써줌



LPTHREAD_START_ROUTINE pfnLoadLibraryA =
 (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");

cs


  CreateRemoteThread() 에 사용할  LoadLibrary()의 주소를  GetProcAddress()를 이용하여 구해옴



HANDLE hThread = CreateRemoteThread(hProcess, NULL0, pfnLoadLibraryA, lpAddr, 0NULL);
cs


  CreateRemoteThread() 를 호출하면 타겟 프로세스에서는   LoadLibrary() 가 호출되며 인젝션할 DLL를 로드한다




3. 코드 후킹 

 3.1 후킹 기본

 실제 Lws_32.dll()  파일의 send()  API는 다음과 같이 후킹할 수 있다.


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
void InitHooking1()
{
    typedef void (WINAPI *Tsend)(SOCKET, const char *intint);
    Tsend fnsend;
    TCHAR szWs2_32Dll[MAX_PATH];
    GetSystemDirectory(szWs2_32Dll, MAX_PATH);
    _tcscat(szWs2_32Dll, _T("\\ws2_32.dll"));
    HMODULE hMod = LoadLibrary(szWs2_32Dll);
    fnsend = (Tsend)GetProcAddress(hMod, "send");
 
 // 엔트리부터 0x16 바이트 떨어진 지점을 대상 주소로 설정
    LPVOID lpTargetAdr = (LPVOID)((DWORD)fnsend + 0x16); 
    TRACEB(_T("lpTargetAdr: %x"), lpTargetAdr);
    DWORD dwOldProtect = 0;
    VirtualProtect(lpTargetAdr, 6, PAGE_READWRITE, &dwOldProtect);
    
    // 기초 설명을 위한 하드코딩
    //719E4C3D           E9 BEC391C1     jmp 33301000
    //719E4C42           90              nop
    *((LPBYTE)lpTargetAdr + 0= 0xE9;
    *((LPBYTE)lpTargetAdr + 1= 0xBE;
    *((LPBYTE)lpTargetAdr + 2= 0xC3;
    *((LPBYTE)lpTargetAdr + 3= 0x91;
    *((LPBYTE)lpTargetAdr + 4= 0xC1;
    *((LPBYTE)lpTargetAdr + 5= 0x90;
}
 
cs


 이를 통해 send() 함수가 호출될 경우 0x33301000에 있는 코드를 실행하게 할 수 있음


 0x33301000에 필요한 DLL를 로드하면 원하는 DLL를 실행시킬 수 있음


#pragma comment(linker, "/base:0x33300000 /fixed")
cs


위와 같은 코드를 소스코드의 위쪽에 배치하면 0x33300000번지가 ImageBase가 됨



그러나 위와 같이 주소를 하드코딩하는 방식은 DLL의 주소가 그 주소에 로딩되지 못하면 무의미함

주소가 변경됐을 때도 주소를 찾아 연결시킬 수 있는 방법이 필요함


위의 코드에서 20~26번째 라인을 다음과 같이 변경하여 동적 주소를 구할 수 있음



1
2
3
    *((LPBYTE)lpTargetAdr + 0= 0xE9;
    DWORD dwBufferAdr = (DWORD)HookSend - (DWORD)fnsend - 0x16 - 5;    
    *((LPDWORD)((LPBYTE)lpTargetAdr + 1)) = dwBufferAdr;
cs



 먼저 타겟 jmp 명령어를 의미하는 0xE9는 타겟 주소에 넣어줌

 주소부분 계산은 삽입할 주소의 위치와 jmp 대상이 되는 주소의 차이와 5바이트의 op code를 고려하여 계산해주면 됨

 

 719E4C3D E9 BEC391C1     jmp 33301000


 앞서 이 명령어의 operand도 이렇게 계산됨


0x33301000 - 0x719E4C3D - 5 = 0xFFFFFFFFC191C3BE

 이를 4바이트 주소인 DWORD로 타입 캐스팅 하면 앞의 0xFFFFFFFF는 제거되고 0xC191C3BE만 남고

 이를 리틀엔디안 방식으로 하면 명령어의 operand 부분인 BEC391C1이 됨


 마지막에는 ((LPBYTE)(lpTargetAdr + 1))  의 위치를 LPDWORD 타입으로 4바이트 묶어서 계산된 주소를 넣어줌



3.2 Send 후킹

 

 send 함수의 프로토 타입을 참고하여 버퍼를 출력하는 코드를 작성할 수 있음


1
2
3
4
5
6
int send(
    __in SOCKET s,
    __in const char *buf,
    __in int len,
    __in int flags
);

cs


 2번째 인자로 전송할 버퍼를 전달받고, 버퍼의 길이를 3번째 인자로 전달받음


 이를 참고하여 버퍼를 OutputDebugString을 통해 출력하는 코드를 다음과 같이 어셈블리 코드로 작성

 

1
2
3
4
5
6
7
8
9
10
11
__declspec(naked) HookSend()
{
    __asm
    {
        mov eax, [ebp+0xC]
        push eax
        call ds:OutputDebugString
        mov eax, 0x719E4C43
        jmp eax
    }
}
cs

 

 [ebp+0xC]에는 함수로 전달되는 두 번째 인자가 존재하니까, 그걸 eax에 넣어주고 OutputDebugString 함수의 인자로 전달하기 위해 스택에 넣음


 그리고 원래 코드로 돌아오기 위한 점프문 배치하고 이 함수를 0x33301000 번지에 올라가게 하면 됨



 그러나 단순히 이렇게 해버리면, send 함수를 통해 나가는 모든 패킷을 모니터링하게됨...


 send  함수를 호출한 부모 함수를 식별해서 어느 함수에서 호출한 send인지 출력 가능

 또한 모니터링 하고자 하는 패킷만 모니터링 할 수 있음


 부모 함수를 식별하기 위한 방법은 send 함수의 return 주소인 [ebp + 0x4]를 참조하면 식별이 가능함



 먼저, 어셈블리 코드에서 사용할 ebx, eax, edx 레지스터가 망가지지 않게 스택에 넣어두고, 각각 레지스터에 메시지의 길이, 메시지 내용, return 주소를 넣음 


1
2
3
4
5
6
push ebx
push eax
push edx
mov ebx, [ebp+0x10] // 패킷 길이
mov ebx, [ebp+0xC] // 패킷 버퍼
mov ebx, [ebp+0x4] // return 주소
cs

 이렇게 원래의 레지스터에 있는 값을 저장하고 레지스터를 마음대로 쓴 이후 다시 pop을 통해 복원해줌

 다음과 같은 코드로 send 함수를 호출한 부모 함수의 주소와, 버퍼를 문자열과 덤프 형식으로 출력이 가능함
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
void PseudoFunc(int retaddr, char *buf, int len)
{
    if (len != 1 && len > 0)
    {
        //TRACEB("[%x] send : [%d]", retaddr, len);
 
        int len_temp = 0;
        int len_temp2 = 0;
        char szMsg1[MAX_PATH] = {0,};
        char szMsg2[MAX_PATH] = {0,};
        for (int i=0; i<len; ++i)
        {
            if (MAX_PATH < i)
                break;

            // null 바이트는 왜 "_"로 바꾸지...
            if (buf[len_temp] == '\0')
                len_temp += sprintf(szMsg1 + len_temp, "_");
            else
                len_temp += sprintf(szMsg1 + len_temp, "%c", buf[i]);
            
            // 200 바이트 까지 버퍼를 Hex dump
            if (len_temp2 < 200)
                len_temp2 += sprintf(szMsg2 + len_temp2, "%02X "*(buf + i));
        }
        
        TRACEB("[%x] send : [%d] %s", retaddr, len, szMsg1);
        TRACEB("     dump : %s", szMsg2);
    }
}
cs

 
 이처럼 작성한 함수를 호출하는 어셈블리 코드를 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__declspec(naked) HookSend()
{
    __asm
    {
        push ebx
        push eax
        push edx
        mov ebx, [ebp+0x10]
        mov eax, [ebp+0xC]
        mov edx, [ebp+0x4]
        push ebx
        push eax
        push edx
        call lpPseudoFunc
        add esp, 0xc
        pop edx
        pop eax
        pop ebx
        mov eax, 0x719E4C43
        jmp eax
    }
}
cs
 15번째 줄의 add esp, 0xc는 함수 호출 과정에서 늘어난 스택 크기를 조정해주기 위함

 







 

 

'Study > Reversing' 카테고리의 다른 글

우회 기법  (0) 2019.03.20
FSC_Level1 풀이  (0) 2019.03.20
Petya Ransomware 분석  (0) 2019.02.19
PE 헤더  (0) 2019.02.18
2. C 문법과 디스어셈블리  (0) 2019.02.14

13. 우회 방법

API Hooking을 통한 우회
  • API를 이용한 디버깅 탐지 기법들은 API Hooking을 이용하여 해당 API를 무력화 하는 방법으로 우회 가능

  • OllyDBG의 OllyAdvanced라는 안티 디버깅 플러그인이 대부분의 무력화를 지원함

    • FindWindow

      : FindWindow()를 Hooking -> Debuger process의 윈도우 핸들을 가리킬 경우 Null을 return

    • IsSoftIceLoaded

      : CreateFile() Hooking -> Softice 드라이버일 경우 null 또는 INVALID_HANDLE 리턴

    • CheckRemoteDebuggerPresent

      : CheckRemoteDebuggerPresent Hooking -> 호출 시 강제로 false 리턴





      함수 호출부를 아래와 같이 수정하여 강제로 0을 반환하도록 함

      xor eax, eax

      ret

    • Debug Oject Handle

      : NtQueryInformationProcess() API를 같은 방법으로 수정

      : 두 번째 인자가 0x1e (ProcessDebugObjectHandle)일 경우에 대해서만 반환하도록

    • NtQueryObject

      : NtQueryObject에서 NTSTATUS 실패를 리턴

      • NtQueryObject() 를 호출할 경우 모든 오브젝트 핸들을 얻어올 수 있음, 이 중 DebugObject가 포함됐을 경우 탐지하는 기법

    • NoDebugInherit

      : NtQueryInformationProcess() API를 같은 방법으로 수정

    • NtSetInformationThread

      : NtSetInformationThread() API를 같은 방법브로 수정

    • 프로세스 체크

      : Process32Next()API를 이용, 프로세스를 찾지 못하도록 수정

    • 버전 체크

      : GetFileVersionInfoSize() 를 실패하도록 수정

    • 부모 프로세스 체크

      : Process32Next()API를 이용, 프로세스를 찾지 못하도록 수정

      : NtQueryInformationProcess() 도 변조

    • SeDebugPrivilege 권한 체크

      : CsrGetProcessId() 를 변조, OpenProcess(), NtQueryObject()

    • 키보드 입력 봉쇄

      : BlockInput() 변조

    • OutputDebugString

      : OutputDebugString() 함수를 변조, 바로 반환하도록 설정해도 가능

    • WinDBG 검출

      : FindWindow() 무력화

    • CloseHandle()을 이용한 안티 리버싱

      : CloseHandle() 을 후킹, 정상 핸들을 Close하는 것을 걸러야 하는데 이 과정이 복잡

      : OllyAdvanced에서도CloseHandle()을 필터링하는 기능이 존재

      : Ignore and skip C0000008h (inv Handle) 기능 활성화

    • 타이머를 이용한 방법

      : GetTickCount() API를 후킹해 시간을 속임, rdtsc 를 사용하면 Win32 API 가 아니기 때문에 APi 후킹으로는 안됨, OllyAdvanced 에서 관련 기능을 제공함

      1. Anti-RDTSC 기능을 Enable

      2. 해당 기능을 사용하면 아래와 같이 커널 드라이버가 설치됨    


3. IDT 후킹을 이용한 rdtsc 우회 기법

4. rdtsc : 시스템 부팅 후 현재 실행 시점까지를 알려주는 명령어, CPU 사이클을 읽어서 계산함

5. CR4 레지스터의 TSD(Time Stamp Disable) 비트를 1로 설정할 경우 rdtsc가 호출될 때마다 GP(General Protection) 예외가 발생

6. 해당 예외를 처리하는 핸들러가 GPF(General Protection Fault)이며 0xD 인테럽트를 사용
7. 즉, rdtsc를 호출하면 0xD번 인테럽트가 호출됨
8. 0xD 인테럽트를 후킹하여 해당 인테럽트가 호출되면 그것이 rdtsc에 의해 호출되었는지 확인, rdtsc일 경우 시간 값 조작을 통해 우회

      그냥 opcode를 찾아 패치하는 방법도 있음

      1. rdtsc를 호출하는 명령어 (0x0F31)를 모두 찾아 0x90(nop)으로 수정함

    • 디버그 레지스터

      : ???

    • API Hook을 이용한 디버깅 감지

      : 디버거가 프로세스를 attach 할 때 주로 사용하는 DebugActiveProcess() 를 후킹해서 attach 를 못 하게 하는 기법으로, 후킹된 해당 DebugActiveProcess() 를 다시 후킹해서 원복시킴

    • Self Debugging

      : 부모 프로세스에서 WaitForDebugEvent() API를 호출함, 해당 API에 DebugActiveProcessStop() 함수를 삽입하여 부모 프로세스가 디버거를 놓아버리게 함

OllyDBG 옵션을 이용한 우회
  • int3을 이용한 디버거 감지

    : OllyDBG 의 메뉴에서 Option - Debugging Option 으로 가서 Exception 탭에서 Ignore memory access violations in KERNEL32 을 켜고 INT3 break 옵션과 Single-step break 옵션을 켜준다

    : 디버거 인테럽트가 발생해도 자동으로 예외 처리 핸들러를 호출하여 디버거 사용이 가능

  • SetUnhandledExceptionFilter

    : 예외 처리 관련 안티 디버깅에 대한 우회는 해당 옵션으로 대부분 우회 가능

플래그 수정으로 우회
  • PEB를 이용한 방법

    : PEBBeginDebugged 플래그 값을 0 으로 패치

  • 리모트 디버깅 감지

    : KD_DEBugGER_NOT_PRESENT 는 커널 전역 변수, 해당 변수를 TRUE 로 패치해버림

  • 0xCC 자체를 탐지

    : 코드 상의 0xCC 를 찾아서 패치


'Study > Reversing' 카테고리의 다른 글

코드 후킹  (0) 2019.03.21
FSC_Level1 풀이  (0) 2019.03.20
Petya Ransomware 분석  (0) 2019.02.19
PE 헤더  (0) 2019.02.18
2. C 문법과 디스어셈블리  (0) 2019.02.14
  1. 정보 수집

  • 실행하면 위와 같은 메시지를 출력하고 종료되는 실행파일이다.

  • PEiD를 이용하여 해당 바이너리의 패킹 여부를 확인한다.

  • UPX 패킹된 바이너리임을 확인할 수 있었다.

  • upx 리눅스 유틸리티를 이용하여 언패킹을 진행

  • 언패킹된 바이너리


  1. 분석

  • IAT 테이블을 살펴보니 다음과 같이 command line을 읽어오는 함수가 존재했다.

  • 따라서 해당 바이너리를 정상 작동시키기 위해선 command line의 실행 인자를 전달해줘야 할 것이라고 추측이 가능하다.

  • GetCommandLine 함수 직후에 특정 반복문이 존재하고 해당 반복문은 Command line으로부터 읽어들인 문자열을 edx 레지스터가 1바이트씩 이동하여 마지막 바이트를 가리키도록 한다.

  • 이후 AL 레지스터에 [EDX-5] 위치의 1 바이트를 옮기고 해당 값을 스택에 넣는다.

  • AL 레지스터에 0x20의 값을 넣고 스택의 최상단과 비교한다.

  • 즉 EDX-5 위치가 0x20(공백문자)와 일치한지를 비교한다.

  • 이후 일치하지 않을 0x004012FC 주소로 점프하며 해당 주소는 다음과 같이 MessageBox API를 호출하며 처음 출력되는 메시지가 출력된다.

  • 따라서 해당 출력문을 피하기 위해선 [EDX-5]가 공백문자여야 하므로 파라미터는 다음과 같이 뒤에서 5번째 문자가 공백문자인 4글자의 파라미터가 전달되어야 한다.

    C:\Users\Desktop\FSC_Level2.exe asdf

  • 인자로 "asdf"를 전달하고 해당 조건을 만족시키도록 하고 해당 분기문에 bp를 걸고 나머지를 분석한다.

  • 이후 또 다시 비교문과 해당 비교문이 만족하지 않을 경우 비정상 종료 루틴에 빠지는 부분을 발견했다.

  • 해당 위치에서 EAX에 0x6578652E(".exe")를 넣고 해당 값을 스택의 최상단에 있는 값과 비교한다.

  • 해당 위치로 오기까지 스택의 최상단에 어떤 값이 위치하는지 확인하기 위해 백트레이싱을 수행한다.

  • 인자로 전달한 0x66647361("asdf")을 0x5528566D과 xor 연산을 수행한 결과를 EAX에 저장한다.

  • EAX에는 0x334C2501이 저장됨

  • AH(0x25)와 BH(0xC0)를 XOR 연산을 수행하여 0x334CE50C이 EAX에 저장된다.

  • 이후 해당 값을 0x6578652E(".exe")와 비교하여 일치하지 않을 경우 실패 루틴으로 진입한다.

  • 따라서 0x6578652E의 값에 일련의 과정을 역연산해주고 나온 값을 파라미터로 전달할 경우 성공 루틴으로 진입이 가능하다.

  • 0x6578652E의 세 번째 바이트인 0x65와 0xC0를 xor한 결과인 0x6578A52E

  • 해당 값을 0x5528566D와 xor한 결과 = 0x3050F343

  • 확인한 결과 실행할 때마다 ebx의 값이 변함

  • ebx의 값을 설정하는 코드를 찾아본 결과 다음과 같이 entry 포인트에 존재

  • 그러나 해당 지점에 bp를 걸고 실행해도 해당 부분은 실행되지 않음

  • 원래대로라면 ebx에 고정된 값인 0으로 초기화되고 xor 연산에 영향을 주지 않아 "C3P0"가 정답이 되어야 하는데 안티디버깅 기법이 있는 것으로 볼 수 있음

  • 실행 과정에서 ebx를 강제로 초기화하고 인자를 "C3P0"로 전달할 경우 정상적으로 플래그가 출력됨

    정리

    • PEP와 OEP : Packing entry point, Original entry point

    • PEP 코드 실행 도중 디버거 사용 유무를 탐지하여 사용중일 경우엔 다른 OEP에서 시작하도록 하는 기법이었음

    • 해당 문제에서는 TLS(Thread Local Storage) callback을 이용한 기법을 사용함

    • TLS Callback이란 프로세스가 만들어질 때 메인 쓰레드가 초기화되기 전에 가동되는 코드

    
    • PE header를 참조한 결과 해당 TLS 코드가 0x4070DC 번지에 존재함

    • TLS callback 함수에 존재하는 디버거 탐지 코드 (IsDebuggerPresent())

    • 따라서 디버거가 붙을 경우에 xor ebx, ebx 구문을 피해서 ebx가 초기화되지 않도록 하여 정상적인 플래그 인증에 실패하도록 하고 디버거가 붙지 않았을 겨웅 xor ebx, ebx가 정상적으로 실행돼서 플래그 인증이 가능하다.




















  1. 사전 정보 수집
  • 해당 바이너리를 실행할 경우 위와 같이 키를 입력하라는 메시지가 출력된다.
  • 바이너리가 요구하는 키를 맞힐 경우 플래그가 출력되는 형식이라고 추측할 수 있다.
  • PEiD를 이용하여 확인해본 결과 해당 바이너리는 PEiD로는 알 수 없는 방법으로 패킹되었거나 패킹되지 않았음을 확인할 수 있다.
  • 패커가 사용되었는지 확실히 하기 위해 IAT를 봐서 추가적으로 확인할 수 있다.
  • 패커는 대부분 IAT를 망가뜨리기 때문에 PEView를 이용하여 해당 바이너리의 IAT를 확인했을 때 Import한 함수의 개수가 적으면 패킹이 되었을 것이라 의심해볼 수 있다.
  • PEView를 이용하여 해당 바이너리의 IAT를 확인해본다.

  • 해당 바이너리가 두 개의 dll로부터 충분히 많은 Import 함수가 존재하므로 패킹되지 않은 바이너리임을 알 수 있다.
  • 추가적으로 해당 바이너리의 Image Base는 일반적인 EXE 파일이 가지는 0x400000 번지가 아닌 0x69000000이었다. 따라서 해당 주소부터 분석을 진행한다.
  1. 분석
  • 먼저 실패할 경우 출력되는 메시지부터 백트레이싱을 수행한다.
  • 참조된 문자열들을 검색해본 결과 0x69001152번지에 해당 문자열이 있는 것을 확인했다.

  • 해당 위치의 윗부분에 문자열을 입력받고 특정 문자열과 비교를 하는 stricmp 함수를 발견할 수 있었다.
  • 해당 함수에 bp를 걸고 실행 시 어떤 문자열이 오는지 확인해본다.

  • 비교 대상 문자열은 "Asm07REC"이다.
  • 해당 문자열을 입력하면 다음과 같이 플래그가 출력된다

 




'Study > Reversing' 카테고리의 다른 글

코드 후킹  (0) 2019.03.21
우회 기법  (0) 2019.03.20
Petya Ransomware 분석  (0) 2019.02.19
PE 헤더  (0) 2019.02.18
2. C 문법과 디스어셈블리  (0) 2019.02.14

1. 메모리 포렌식

디지털 포렌식과 같은 범죄 수사를 위한 디지털 증거 수집에 있어서 메모리 포렌식은 매우 중요한 기술이다. 암호화된 파일이 실행 또는 읽기를 위해선 메모리에 올라가게 되는 이 때 데이터는 일반적으로 복호화 되어 메모리에 올라가게 된다. 메모리 덤프를 통해 메모리의 이미지를 얻으면 복호화 된 데이터를 확인하는 것은 물론 복호화 키 또한 얻을 수 있다.

브라우저 검색 기록, 메신저 사용 내역, 암호화된 문서 등을 준비하여 여러 흔적을 남겨두고, 메모리 덤프 이미지를 확보하여 실제 데이터 추출이 가능한지를 시도해본다.


2. 실험 대상 

2.1) Browser 사용 기록

브라우저를 통해 웹 사이트에 로그인한 사용자의 계정 정보를 메모리 덤프를 통해 추출할 수 있는지 여부를 점검하기 위해 여러 대상을 정하여 로그인 상태를 유지하였다. 가정한 시나리오는 사용자가 웹 사이트에 로그인하여 패스워드를 변경하여 변경 전 패스워드와 변경 후 패스워드를 메모리 영역에서 찾을 수 있는지 여부를 점검하기 위해 해당 시나리오를 각 사이트에서 각각 다른 패스워드로 변경했다. 대표적인 브라우저인 크롬 브라우저와 인터넷 익스플로러 브라우저에서 로그인 및 패스워드 변경을 수행하였고 대상 웹 사이트는 다음과 같다.

브라우저

웹 사이트

Chrome

https://www.naver.com

Chrome

https://www.daum.net

Chrome

https://www.facebook.com

Internet Explorer

https://www.naver.com

Internet Explorer

https://www.daum.net

Internet Explorer

https://www.facebook.com


각 사이트에 로그인 한 계정의 ID와 패스워드는 모두 동일하며 각 사이트별로 다른 패스워드로 변경하여 어느 사이트의 패스워드를 메모리 덤프를 통해 얻은 메모리 이미지에서 찾아볼 수 있는지 점검한다. 메모리 덤프를 통해 얻은 메모리 이미지로부터 각 사이트에 로그인 한 계정 정보를 찾아볼 수 있는지 여부를 점검하기 위해 위와 같은 정보를 설정했다.


2.2) 문서 암/복호화 키 (HWP)

대표적인 워드 프로세서인 MicrosoftMS WordHancomHWP는 모두 문서를 암호를 이용하여 암호화하는 기능을 제공한다. 암호화 된 문서가 사용자의 컴퓨터에서 열린 경우 입력된 암화 및 복호화 키는 문서 작성 프로세스의 메모리 영역에 남아있다. 따라서 메모리 덤프 후에 분석 대상 워드 프로세서의 메모리 영역으로부터 해당 문서 파일의 암/복호화 키를 추출할 수 있을 것이라고 기대할 수 있다. 이번 과제에서는 HWP 파일에 임의의 암호를 설정하여 저장하고 해당 문서를 열어 두고 메모리 덤프를 진행하였다.




2-3) 메신저 사용 내역

 국내에서 대표적으로 사용되는 카카오톡 메신저와 그 외의 텔레그램, 네이트온 메신저를 모두 실행하여 로그인하고 대화 기록을 남긴다. 메모리 덤프를 통해 얻은 이미지에서 해당 메신저의 대화 내용 및 전송한 사진 등을 찾아볼 수 있는지 점검한다. 사전에 설정한 정보는 각 메신저 애플리케이션마다 다음과 같이 설정하였다.


3. 분석

3-1) 메모리 덤프

메모리 데이터의 일부 또는 전체를 다른 저장매체에 저장하는 행위를 일컬으며 시스템 오류를 디버깅하기 위한 목적 또는 프로그램의 실행 도중의 메모리에 존재하는 데이터를 추출하기 위해 수행한다. 메모리에는 디스크에서는 찾을 수 없는 복호화 된 파일 콘텐츠나 사용자의 패스워드, 프로세스의 임시 저장 데이터 등 메모리에서만 찾을 수 있는 정보가 존재하며 메모리 덤프를 통해서 이러한 정보를 추출할 수 있다. 또한 최근 유포되는 디스크에 코드를 남기지 않고 메모리에서 바로 실행되는 파일리스(fileless) 악성코드를 분석하기 위해서 사용할 수 있다. 디지털 포렌식과 같은 범죄 수사의 관점에서 증거수집을 위해 사용할 수 있다. 메모리 덤프 방법은 크게 네 가지로 볼 수 있으며 다음과 같다.

방법

설명

Crash Dumps

시스템이 작업을 수행할 수 없는 치명적인 오류나 내부적인 조건 때문에 윈도우 크래시가 일어났을 때 대상 컴포넌트가 발생시킨 문제를 분석할 수 있 도록 시스템 메모리를 저장하는 것이다.

Hibernation Files

절전모드에 들어가게 되면 디스크에 ‘hiberfil.sys’라는 이름으로 메모리의 내용을 저장하고 시스템 전원을 차단하여 다음 부팅 시 시스템을 이전의 상태로 되돌릴 수 있도록 한다. 이러한 ‘hiberfil.sys’ 파일을 이용하는 방법이다.

Firewire

장치간의 연결 및 데이터 전송을 위해 Apple사에서 1986년 개발하여 1995 IEEE 1394 표준으로 채택된 기술로 데이터 전송 속도 향상을 위해 DMA(Direct Memory Access) 기술을 이용한다.

Virtual Machine Imaging

VMware 가상 시스템을 일시 정지할 경우 정지된 상황과 동일한 상황을 생성해 주기 위하여 메 모리의 모든 내용을 .vmem 파일에 저장하여 유지한다.


메모리 덤프 방법에 따라 위와 같이 크게 네 개의 방법으로 나눌 수 있으며 각 방법에 따라 덤프에 사용되는 도구 또한 차이가 있다

다음 표가 각 방법에 따른 사용 가능한 도구와 각각의 도구의 장단점을 나타낸다.

분 류

도구 및 명칭

장 점

단 점

Hardware

Tribble

특정 소프트웨어를 설치하지 않아도 되기 때문에 시스템에 영향이 적음

하드웨어 장비가 미리 설치되어 있어야 함

Firewire

User mode로 메모리의 접근을 허용하지 않는 윈도우에서도 덤프 가능

실행 도중 크래쉬 발생 가능

Software

WinDD(Win)

윈도우 버전 및 bit에 상관 없이 덤프 가능

순수한 메모리 덤프 획득 어려움

MDD(Win)

유저 레벨에서 동작

4GB 이상의 램 정보 수집 불가, 64bit 불가

DumpIt(Win)

윈도우 버전 및 bit에 상관 없이 덤프 가능

-

DD(Linux)

간단하게 동작 가능

부가정보를 제공하지 않음

Mac Memory Reader (MAC OS)

유저 레벨에서 동작

전체 메모리가 아닌 에러가 있는 영역만 덤프

Crash

(윈도우 기반)

시스템의 자원이 부족할 때 쓰기 좋음

재부팅 필요

Virtual Machine Imaging

MDD(Win)

세션이 정지된 상태로 덤프

가상화 기반 엔진 필요

Hibernation Files

Hibernation
(
윈도우 기반)

추가적인 프로그램이나 장비 불필요

전체 메모리가 아닌 사용중인 영역만 덤프


분석 대상이 윈도우 OS 환경의 메모리 데이터이므로 윈도우에서 작동하는 도구 중 DumpIt을 사용하여 메모리 덤프를 수행했다

실행 후 ‘y’ 버튼 입력을 통해 쉽게 메모리 덤프를 수행할 수 있다.



3-2) 분석

메모리 덤프를 통해 얻은 이미지를 Volatility, HxD, Memoryze 등의 분석 도구를 통해 분석을 수행한다.



메모리 덤프를 통해 얻은 이미지를 분석하여 정보를 추출하기 위해 먼저 Volatility를 이용하여 대상 시스템에 대한 정보를 파악했다.

Volatility는 이미지 파일을 자동 분석하여 이미지 파일로부터 위과 같이 대상 시스템의 정보를 보여준다

해당 기능을 통해 얻은 정보는 사전에 시나리오를 설정하여 이미지를 획득한 환경과 일치했다.




Volatility는 이미지 파일을 분석하여 실행되고 있던 프로세스 정보를 추출해준다

추출해주는 정보는 해당 프로세스의 이름, PID, PPID 등의 정보를 추출해준다.

사전에 설정한 정보가 IE 브라우저, Chrome, KaKaoTalk, Telegram, NateOn에 한정되어 있으므로 해당 프로세스들에 대해서만 분석을 수행한다.


3-2-1) 메신저 패스워드

먼저 HxD 에디터를 통해 문자열 검색으로 특정 데이터들을 검색하였다

         


Password 입력 형식으로 정의될 수 있는 문자열인 ‘password’를 검색한 결과 다음과 같이 몇몇 패스워드를 찾을 수 있었다

해당 메모리 영역의 전후 문자열들을 통해 카카오톡 로그인 정보임을 확인했다.



‘pass’ 문자를 검색한 화면이다. 해당 메모리 영역의 전 후 문자열 데이터를 분석한 결과와 해당 패스워드 문자를 통해 유추하면 해당 메모리 영역에서 추출한 데이터는 페이스북에 로그인 한 계정 정보임을 알 수 있었다.


3-2-2) 문서 복호화 키



메모리 영역에서 HWP 파일의 암/복호화 키 검색이 가능했으며 따라서 암호화 된 문서 파일을 읽는 도중 메모리 덤프를 수행하면 해당 파일을 암/복호화 할 수 있는 키를 메모리 영역에서 얻을 수 있음을 확인하였다.


3-2-3) 메신저 사용 내역



 

네이트온 메신저에서 남긴 대화 내용의 텍스트 부분을 메모리 영역에서 검색을 한 결과 UTF 인코딩 형식으로 찾아볼 수 있었다.



마찬가지 방법으로 계속해서 탐색을 해본 결과 같은 방법으로 카카오톡 메신저에서 사용된 것으로 추정되는 문자열을 찾을 수 있었다.


3-2-4) 브라우저 사용 내역

 


URL 기반으로 메모리 영역을 탐색하던 도중 다음과 같은 메모리 영역을 찾을 수 있었다. 해당 메모리 영역에선 네이버의 로그인 페이지 URL과 변경 전의 패스워드와 변경 후의 패스워드가 모두 노출되어 쉽게 추출할 수 있었다.



'Study > Digital Forensic' 카테고리의 다른 글

Disk Forensics - MBR Analysis  (0) 2019.02.19

1. MBR (Master Boot Record)?

MBRMaster Boot Record의 약자로 컴퓨터에 전원이 공급되고 부팅이 가능한 운영체제를 찾아 해당 운영체제의 boot code를 실행할 수 있도록 Partition들의 위치 정보를 갖는다.



회색으로 색칠된 부분이 Boot code이고 아래에 여러 색으로 색칠된 부분이 Partition table이다

Boot codePartition Table 중 부팅이 가능한 Partition을 찾아 부팅을 할 수 있게 해주는 코드이며 마지막 2bytes(0xAA55) MBRSignature이다

MBR Partition table 형식이다. Partition Table의 첫 바이트인 Boot Flag는 해당 Partition이 부팅이 가능한지 여부를 나타내는 플래그다

그 다음으로 오는 세 바이트는 CHS(Cylinder-Head-Sector) 방식의 주소를 사용할 경우 해당 Partition 시작 주소를 나타낸다

다음으로 오는 Part Type 필드는 해당 Partition 유형을 나타내며, 값이 가지는 아래 표로 나타낸다.

이후 바이트는 LBA(Logical Block Addressing) 방식의 주소를 사용할 경우의 Partition 시작 주소를 나타낸다

LBA Starting Address 이후에 오는 Number of sectors in partition 필드는 해당 Partition Sector 개수를 나타내며 따라서 LBA 주소 방식을 사용할 경우 전체 파티션 영역은 다음과 같다.

시작 주소 : [Starting LBA address] * [Sector size]

주소  : [Starting LBA address] * [Sector size] * [Num of sectors in Partition]

필드의 의미를 정리하면 다음과 같다.

- Boot flag(1byte)           : 해당 파티션이 부팅이 가능한 파티션인지 여부를 나타냄                          (0x80 : 부팅 가능, 0x00 : 부팅 불가능)

- CHS Start(3bytes)         : 해당 파티션의 시작 주소를 의미함(CHS 방식)

- Part Type(1byte)          : 해당 파티션의 종류를 의미함                                                                 (0x0B : CHS 모드 FAT32, 0x0C : LBA 모드의 FAT32, 0x07 : NTFS 등)

- CHS End(3bytes)          : CHS 방식의 종료 주소

-  LBA Start(4bytes)         : 해당 파티션의 시작 주소(LBA 방식)

-  Size in Sector(4bytes)   : 파티션의 섹터 수

 


Partition Type에 올 수 있는 값들은 위와 같다.

0번지부터 512Bytes까지 MBR이 기록되어 있으며 위 그림과 같이 Partition Table을 확인할 수 있었다.

초록색으로 색칠한 부분이 각 PartitionPartition Type을 나타내며, 네 개의 Partition 모두 해당 값이 0x07이다.

위 그림에서 확인하면 0x07exFAT 또는 NTFS 파일시스템을 나타낸다

4개의 Partition Entry중 색칠한 부분이 각각 [LBA Starting address][Num of sectors in Partition]에 해당한다

따라서 첫 번째 Partition0x3F0x200(512bytes)를 곱한 위치에 존재한다고 볼 수 있다

0x3F0x200을 곱한 값은 0x7E00이므로 해당 위치에 실제 Partition이 저장되어 있는지 확인한다.



앞서 계산한 0x7E00의 위치에 NTFS 파일시스템 형식의 Partition이 있는 것을 확인할 수 있었다

추가적으로 위 그림에서 첫 번째 Partition의 시작 주소인 0x3F와 해당 Partition Sector 수를 나타내는 0x032F8E를 더한 값이 다음 두 번째 Partition의 시작 주소임을 확인할 수 있으며 따라서 첫 번째 Partition이 끝나는 지점의 다음 Sector에 두 번째 Partition이 존재하는 것을 확인할 수 있다

두 번째 Partition의 위치 또한 동일하게 계산해보면 0x032FCD Sector의 크기인 0x200(512bytes)를 곱하면 위치를 구할 수 있다

해당 계산의 결과는 0x65F9A00이며 해당 위치에 실제 Partition이 존재하는지 확인해본다.



위 그림에서 확인할 수 있듯이 0x65F9A00에 해당 Partition이 기록되어 있는 것을 확인할 수 있었다

이후 세 번째 LBA를 계산하면 0x065F9A Sector 크기를 곱한 값은 0x0CBF3400이며 해당 위치에 실제 세 번째 Partition이 존재하는지 확인해본 결과 다음과 같이 실제 Partition이 존재하는 것을 확인할 수 있었다

네 번째 Partition 역시 0x098F67 * 0x2000x131ECE00에 실제 Partition이 위치하는 것을 확인할 수 있었다.

세 번째 파티션


네 번째 파티션


다음으로 CHS(Cylinder-Head-Sector) 방식은 이름 그대로 실제 HDDCylinder, Head, Sector를 이용하여 디스크 영역을 접근하는 방법이다

표현할 수 있는 크기가 크지 않아서 현재는 사용하지 않지만 주소 계산 방법을 살펴보면 다음과 같다.


 색칠된 부분이 CHS Address의 시작 주소를 나타낸다

해당 값을 계산하기 위해선 해당 값으로부터 Cylinder, Head, Sector 부분을 계산해야 하며 계산을 위해선 세 값을 비트로부터 추출해야 한다

Partition Table의 첫 번째 PartitionCHS Address0x000101을 이용하여 세 값을 나타내면 다음과 같다.



해당 값을 리틀엔디안 방식을 고려하지 않고 해석하면 가장 맨 뒤 10비트가 Cylinder를 의미하며 그 앞 6비트는 Sector, 처음 8비트는 Head를 의미한다

따라서 0x000101의 각 세 값은 0, 1, 1이 된다. 해당 값을 이용하여 공식에 따라 LBA 주소로 변환이 가능하며 그 공식은 다음과 같다.



실린더당 헤드의 개수와 트랙당 섹터 개수는 고정적으로 각각 0xFF(255), 0x3F(63)을 사용하며, 해당 값을 대입하여 계산하면 변환된 LBA의 주소는 0x3F(63)이 된다

해당 값은 같은 Partition TableLBA 주소인 0x3F와 일치한다. 두 번째 PartitionCHS 주소인 0x0D0100을 이용하여 LBA 주소 계산을 해보면 마찬가지로 Cylinder의 값은 13, Head의 값은 0, Sector의 값은 1이 되어 LBA 주소 계산 결과 208,845가 도출되며 해당 값을 16진수로 변환하면 0x032FCD가 된다

해당 값은 두 번째 PartitionLBA 값과 일치한다

세 번째 Partition 역시 Cylinder 0x1A(26), Head 0, Sector 1이 되어 계산 결과 417,690이 도출되며 해당 값은 16진수로 0x065F9A로 세 번째 PartitionLBA 주소와 일치한다

네 번째 경우도 0x270100CHS 값은 Cylinder 0x27(39), Sector1, Head0으로 마찬가지로 의 공식에 대입하면 십진수 626,535를 구할 수 있으며 이는 16진수로 0x098F67로 네 번째 PartitionLBA 값과 일치하다.



* 첫 번째 파티션이 63(0x3F)에 있는 이유

과거에 Disk access에 사용된 BIOS APIINT 13hCHS Addressing에 사용되었는데 해당 인테럽트를 사용하여 Disk accessing을 하는 시스템에는 모든 PartitionCylinder Boundary에 위치해야 한다. 따라서 첫 번째 CylinderBoundary에 위치한다. 비록 LBA 주소 계산 방식이 사용되어도 과거 CHS 방식과 호환을 위해 첫 번째 CylinderBoundary에 위치하며 각 Cylinder에는  63개의 섹터가 있어서 0x3F(63) Partition이 위치하게 됐다. 현재는 Logical SectorAdvanced Format으로 인해 한 섹터를 4K로 정렬해야 하며 이러한 시스템에서 0x3F라는 값은 8의 배수가 아니기 때문에 Partition을 위치시키는 과정에서 섹터에 맞지 않는 오배열로 이어질 수 있으며 이는 Performace 문제를 발생시킬 수 있어서 현재 사용되는 OSPartition은 일반적으로 0x800에 위치한다.

 





'Study > Digital Forensic' 카테고리의 다른 글

Memory Forensic  (0) 2019.02.19

+ Recent posts