WeSeong Log in
← Back to posts
Hooking

API Hooking, 패킷 분석

API Hooking, 패킷 분석

Ⅰ. 개요

API Hooking(API 후킹)에 대해 간단히 알아본다. 윈도우 API가 무엇이고 어떤 것들이 있는지 살펴본다. API 후킹과 관련한 플랫폼(?)과 윈도우에서 제공하는 프로세스 모니터링 프로그램, 함수 정보들을 소개한다. API 후킹이 가능한 예제 코드와 함께 Python에서 윈도우 API가 어떻게 동작할지 간단히 살펴본다. Packet(패킷) 구조를 분석해보고자 한다. 패킷 파일을 만들어보게 되면, 기본적으로 Global Header가 있고 Packet Header와 Data가 반복된다. 데이터 링크 계층, 네트워크 계층, 전송 계층 프로토콜의 구조를 분석해서 필요한 정보만 가져오는 방법을 소개한다.



Ⅱ. API Hooking

API Hooking(API 후킹)리버싱의 꽃이라고 불린다. 정보를 가로채거나 실행 흐름을 변경하거나 원래와 다른 기능을 제공하게 하는 것인데, 그게 가능한 이유는 프로세스에 침투하기 때문이다. 윈도우의 경우 API를 통해 윈도우 내의 여러 함수, 기능들을 사용한다. 예를 들어, 블루투스를 키거나 파일 시스템에 접근한다든가 날짜를 바꾸는 기능들을 할 수 있는 것이다. 윈도우 API는 이런 기능(함수)들을 사용할 수 있게 하는 집합체이다. 하드웨어에 접근하려면 커널이 각 장치의 인터페이스와 연결되고 셸이 커널에 명령을 내리는 것처럼 각 셸은 윈도우에서 여러 프로세스들이다. 특정 사용자 프로그램(멜론, 브라우저 등)이 윈도우에서 원활히 작동할 수 있는 이유도 이 윈도우 프로세스들을 이용할 수 있어서인데, 윈도우 API가 이 윈도우 프로세스들과 연결해주기 때문이다.


※ 조금 더 구체적으로 설명하자면, 윈도우에서는 제일 부모 프로세스인 explorer에서 CreateProcessA 함수를 사용해서 유저가 사용하는 프로그램에 대한 프로세스들을 생성한다. 프로세스가 생성되면 메인 스레드가 동작하고 메인 스레드는 각 자식 스레드를 호출한다.

CreateProcessA 함수(processthreadsapi.h) - Win32 apps

API 후킹은 특정 프로세스가 API 호출을 통해 해당 프로세스를 실행할 때 중간에 가로채서 제어권을 가진다. API에 넘어온 파라미터나 리턴 값을 조작하게 되고 호출 자체를 취소시키거나 내가 원하는 프로세스 흐름으로 바꾼다.


API 후킹을 가능하게 해주는 일종의 플랫폼(?)이라고 할 수 있는 것 중 유명한 Frida가 있다.

Modes of Operation

이 Frida는 거의 모든 운영체제에서 사용가능하고 자바스크립트 언어로 동작한다. 자세한 사용법이나 함수들은 위 사이트를 참고하면 좋다.


※ 알아두면 좋은 팁으로 자바스크립트는 웹의 비약적 발전을 이끌었는데, V8엔진이 개발되면서 자바스크립트 언어를 어떤 웹에서든 사용할 수 있게 되었기 때문이다.


※ 프로세스를 추적하는 마이크로소프트(Microsoft)가 제공하는 프로그램을 소개한다. 윈도우의 모든 프로세스를 추적하지만, API 후킹으로 여러 셸에 침투하는 걸 막거나 탐지할 수는 없다. 특히, 파일명을 바꿔서 프로세스명을 정상적인 것으로 표시한 후 동작하는 악성코드도 상당히 많기 때문이다. 그렇다고 도움이 안되는 건 아닌데, 해당 프로세스의 실제 경로와 생성일 등 각종 정보를 추가적으로 살펴보면 알 수 있는 면도 있기 때문이다. 조금 더 자세한 부분은 추가 공부로 포스팅할 예정이다.

Sysinternals - Sysinternals

간단한 예제 코드를 첨부한다.

var hook_createprocess = Module.getExportByName("kernel32.dll", "CreateProcessW");
Interceptor.attach(hook_createprocess, {
    onEnter(args) {
        var lpApplicationName = args[0];
        console.log(Memory.readByteArray(lpApplicationName, 4096));
        var applicationName = Memory.readUtf16String(lpApplicationName);
        console.log(applicationName);
        var lpCommandLine = args[1];
        console.log(Memory.readUtf16String(lpCommandLine));
    },
    onLeave(result) {
        //console.log(result);
    }
});

이 코드는 프로세스를 생성하는 함수에 침투해서 실시간으로 어떤 프로세스들이 생성되는지 알 수 있게 된다.


추가적으로 윈도우 API를 어떻게 활용하는지 Python(파이썬)의 파일 쓰기 함수를 통해 조금 더 알아보자면,

fd = open('경로\\aaaa.txt', 'w')
fd.writelines("12345abcde")
fd.close()

이 코드로 txt파일을 생성할 수 있는 이유는 윈도우의 CreateFileA 함수와 WriteFile 함수를 윈도우 API를 통해 호출시킬 수 있기 때문이다. 각 함수들은 파일 시스템의 MFT에 접근한다. MFT가 해당 파티션에 저장될 임의(빈 곳, 안전한 곳)의 섹터를 지정하게 되고 해당 섹터에 데이터를 저장한다. MFT에 해당 파일의 시간이나 파일명, 파일 위치 등의 각종 정보가 기록(저장)되고 이 일련의 과정을 조작한 함수들을 반환한다. Handle을 반환했다고도 하는데, 여기서 Handle은 단순히 이 함수들을 조작하는 인터페이스처럼 봐도 좋다. 반환하는 이유는 너무 당연하지만, 계속 메모리를 차지하고 있을 수 없기 때문이다.



Ⅲ. 패킷

지금까지 PE, ZIP, DISK(FAT, NTFS)의 구조체를 알아보고 HxD로 구조를 파악해보거나 값을 변경하는 공부를 해왔다. 이제 추가적으로 네트워크의 패킷 구조를 분석해보고자 한다.


※ 알아두면 좋은 TIP으로 IPv6가 개발되었음에도 지금도 IPv4를 사용한다. 그 이유는 NAT로 인해 공인IP와 사설IP로 구분지을 수 있었기 때문이며, 포트 포워딩을 통해 보안에도 좋기 때문이다.



출처 https://boanhack.tistory.com/172

PCAP File Signature

패킷 구조를 본격적으로 살펴보자면, 조금 더 자세한 부분은 위 블로그를 참조하면 좋다. 맨 앞에 24Byte만큼 Global Header가 있다. pcap파일에 기본적으로 붙는 헤더이다. 다음으로 Packet Header가 오게 되는데, Packet Header와 Packet Data가 쌍으로 계속 반복된다. Global Header를 분리했다면, Packet Header와 Data를 구분할 수 있어야 하는데, Packet Header는 16Byte로 구성되고 뒤에 오는 Data의 길이는 12~15Byte가 알려주고 있다. 즉, Header의 길이 16Byte와 Data의 길이를 더한 값이 다음 Packet을 의미한다. 물론, 초기에 Global Header의 값은 더해져 있어야 한다.


이제 본격적으로 데이터 링크, 네트워크, 전송 계층의 패킷 데이터를 구해보고자 한다.

[SK쉴더스 SeSAC 성동1기] 1. 네트워크 유형, OSI 7계층, 프로토콜(데이터 링크, 네트워크, 전송 계층)

[SK쉴더스 SeSAC 성동1기] 2. 네트워크 유형, OSI 7계층, 프로토콜(데이터 링크, 네트워크, 전송 계층)

데이터 링크 계층, 네트워크 계층, 전송 계층의 프로토콜과 헤더 및 데이터 구조에 대해서는 위 게시글을 참조하면 좋다. Packet Data에서 처음으로 찾을 정보는 Ethernet 정보이다. 데이터 링크 계층에 있으며, MAC주소를 알려준다. MAC 주소의 앞에 3Byte는 제조사를 의미하고 뒤 3Byte는 제조업체에서 할당한 고유 식별자(일련번호)이다. 기본적으로 MAC 주소는 6Byte로 되어 있으며, 패킷 데이터에선 Destination Address가 먼저 오고 Source Address가 나중에 오는데, Source Address가 먼저 오게 되면 Destination Address를 찾기 위해(목적지를 찾기 위해) 6Byte를 점프해야 한다. 불필요한 일을 더 하게 되는 셈인 것이다.

[Network] Ethernet/TCP/IP Header 구조

지난 게시글 혹은 이 블로그를 보면 Ethernet의 Header구조를 자세히 설명해주고 있는데, 실제로 덤프된 패킷 파일은 모든 Header 구조를 담는 게 아니므로 Destination Address와 Source Address, Type만을 구하면 된다. 즉, 6Byte, 6Byte, 2Byte만 구하면 된다.


MAC 주소는 IP 주소와 매핑되는데 IP 주소에 대한 MAC 주소를 찾을 때 브로드캐스팅을 한다. 응답 받은 같은 네트워크 대역대 내에서 자신의 IP 주소라면 응답으로 자신의 MAC 주소를 별도의 인증값 필요없이 바로 알려준다. 이게 바로 ARP 프로토콜의 역할로 IP 주소를 통해 MAC 주소를 알아내면 ARP, 반대로 MAC 주소를 통해 IP 주소를 알아내면 RARP라고 한다.


※ 방금 얘기한 ARP와 관련된 ARP Spoofing이란 공격이 있다. 별도의 인증없이 바로 자신의 MAC 주소를 알려주는 점을 이용하는 것인데, 네트워크 통신 과정에서 전달되는 패킷을 스니핑(Sniffing)을 통해 엿본다. 그리고 ARP 요청으로 MAC 주소를 사용자가 요청할 때 자신의 MAC 주소를 알려주고 서버에게도 자신의 MAC 주소를 알려줌으로써 중간에서 데이터를 마음대로 조작할 수 있게 된다. 카페 같은 곳에서 공유기를 통해 같은 네트워크 대역을 공유하는 곳에서 많이 이뤄질 수 있다. 방어하기 위한 방법으로 같은 네트워크 대역의 MAC 주소 중 이상한 점(중복되는 MAC 주소 여부 - arp 명령어를 통해)이 있는지 살펴보거나 IP 주소와 MAC 주소를 동적으로 매핑시켜서 변경시킬 수 있게 하는 것이 아니라 정적으로 매핑해서 변경시킬 수 없게 하면 된다.


덤프된 패킷 파일에서 네트워크 계층의 IP 프로토콜 관련해서 가져올 수 있는 유용한 정보는 Time to Live, Protocol, IP 주소 등이 있다.

pcap 포맷, Header

관련된 더 자세한 정보는 위 블로그를 살펴보면 좋을 것이다. 실제로 'WireShark'로 패킷 파일의 IP 프로토콜 관련된 부분만 계산해보면, Time to Live 앞에 Version, Servies, Total Length, Identification, Flags, Frag Offset의 정보가 있다. 만약 살펴보고 싶다면 따로 계산에 넣지 않고 각각 몇 Byte인지 보면 될텐데 바로 Time to Live 값을 알고 싶으므로 앞의 정보 Byte를 계산해보면, 8Byte 이후에 나온다는 걸 알 수 있다. 그리고 Protocol은 바로 다음인 9Byte부터, IP 주소의 Source Address는 12Byte~15Byte, Destination Address는 16Byte부터 19Byte까지이다.


※ 앞에 Ethernet 프로토콜 바로 뒤에 IP 프로토콜 부분이 오기 때문에, 정확한 계산을 위해 14Byte(이더넷 프로토콜 정보 길이)만큼 더한 뒤 시작해야 한다.


※ IP 주소의 Source AddressDestination Address를 보고 사설 IP인지 공인 IP인지 구별할 수 있는데, 일반적으로 C클래스 사설 대역대를 쓰기 때문에 192.168.~로 시작하는 IP 주소라면 사설 IP이고 그 외라면 공인 IP라고 봐도 된다.


전송 계층은 TCPUDP로 나뉘어진다. 그래서 계산할 때 TCP인지, UDP인지 따질 필요가 있는데, 네트워크 계층의 IP 프로토콜을 추출하면서 위에서 Protocol 정보를 구한 적 있을 것이다. 16진수로 된 값을 10진수로 변환해보면 17과 6이 나오는데 이게 바로 TCP인지, UDP인지 알려주는 값이다. TCP는 6, UDP는 17이기 때문에 이 값으로 구조를 나눠서 분석하면 된다.


TCP를 먼저 살펴보자면 여기서 알아야 할 정보들은 Source/Destination Port와 Sequence/Acknowledgment Number, Flags, Payload(순수 데이터)이다. IP 프로토콜 데이터(총 20Byte)를 얻은 다음부터 각 2Byte씩 Source/Destination Port를 알 수 있고 그 다음에 각 4Byte씩 Sequence/Acknowledgment Number이다. Flags는 조금의 계산이 필요한데, Acknowledgment Number 다음 2Byte 중 뒤에 1Byte만 추출해서 & 연산을 하게 되면 알 수 있다. 0b00100000을 & 연산하면 Urgent Flag 값이 나오고 0이면 없었던 것이고 1이면 요청이 있었던 것이다. 0b00010000은 Acknowledgment 값, 0b00001000은 Push 값, 0b00000100은 Reset 값, 0b00000010은 Syn값, 0b00000001은 Fin값이다. 이렇게 6개의 Flags 값을 구하면 마지막으로 Payload를 구하면 되는데, TCP Header의 20Byte 이후부터 시작되지만 비어있을 수도 있기 때문에 필터링이 필요하고 비어있지 않다면, 다음 패킷의 Header와 Data(즉, 다음 패킷 전까지)가 나오기 전까지 추출하면 된다.


※ TCP 상태 정보를 알려주는 Flags의 값들은 3-Way-Handshake 과정 동안 우리에게 유용한 정보를 제공한다. Urgent긴급한 요청을 의미하고 Ack완료를 의미, Push수신한 데이터를 즉시 애플리케이션 계층으로 전달을 의미한다. Reset강제로 연결 종료 후 요청 받은 나머지 데이터를 무시한다. Syn연결 요청을 의미하고 마지막으로 Fin은 정상적인 연결 종료를 의미한다. 이 Syn 요청에 대한 패킷을 모니터링 후 차단하게 된다면, 보안을 강화할 수 있다.


연결 과정을 Syn과 Ack, Seq 번호로 살펴보자면, 맨 처음에 클라이언트가 특정 서버로 Syn 요청을 할 때 랜덤 Seq 번호를 보낸다. 서버는 받은 Seq 번호에 1을 더한 값과 Ack 번호를 다시 보낸다. 클라이언트는 다시 받은 Ack 번호에 1을 더한 값을 Seq 번호로 보내고 새로운 랜덤 Ack 번호를 보냄으로써 최종적으로 연결된다. Syn, Ack의 개념을 알게 되면 공격 대상에 포트 스캐닝을 할 수 있게 된다. 공격하기 전에 TCP 로그에 남기지 않기 위해 서버가 열려있는지부터 확인하는 작업이다. 처음에 Syn 요청을 하고 Ack 요청을 응답 받으면 그 즉시 연결을 종료하고 최종 연결 완료 Ack 요청을 보내지 않는 방식으로 이루어진다. 이 과정은 엄밀히 말하면 TCP Half-Open Scan(스텔스 스캔)이라고 불린다. 이 TCP 정보들이 중요한 또 하나의 이유는 이런 스텔스 스캔을 방화벽을 이용해서 포트나 Flags의 요청들을 차단해 사전에 막을 수 있기 때문이다.


※ TCP의 Payload 데이터는 웹 통신 패킷(HTTP의 Get, Post Method의 내용 부분) 즉, 실제 전달 데이터들이 담긴다.


UDP로 이어가자면, UDP에서는 Source/Destination Port와 UDP Payload만 추출하면 된다. TCP에 비해 확실히 간단해진다. 마찬가지로 네트워크 계층의 IP 프로토콜 다음부터 계산하면 되는데, Source/Destination Port는 각각 2Byte씩 계산하면 된다. 그 뒤에 UDP Header와 Data(Payload)를 포함한 전체 길이를 알려주는 Length가 오는데 UDP Payload는 이 길이값에서 -8을 해주면 된다. 그 이유는 UDP Payload 전에 두 개의 Port번호와 길이(Length), Checksum 정보가 모두 2Byte씩 총 8Byte로 오기 때문이다. 즉, Source/Destination Port는 처음부터해서 각 2Byte씩, UDP Payload는 Length의 값에서 8Byte를 뺀 곳까지 추출하면 원하는 정보를 가져올 수 있을 것이다.


※ UDP에서 Query를 보낼 때 사용하는 Port는 53이다. 보통 DNS(Domain Name Server)에 가고자 하는 곳의 IP 주소를 알기 위해 UDP를 보내게 되는데, 정방향과 역방향이 있다. 정방향은 www.google.com과 같이 호스트 이름을 가지고 해당하는 IP 주소를 찾는 것이고 역방향은 IP 주소(ex. 192.168.0.10)만을 알고서 호스트 이름을 찾는 것이다.



후기

이번 주의 마지막 금요일에 있던 수업을 정리하면서 파이썬으로 패킷 파일을 분석해주는 코딩을 진행하였습니다. 확실히 구조들을 파이썬으로 코딩하면서 어떻게 추출할 수 있는지 계산하며 계속 들여다보니까 이해가 조금 더 용이해지는 것 같습니다. 다음 주부터 아마 모바일도 들어가게 될텐데 확실히 이번 주에 배운 여러 구조체들을 이해하고 가야할 것 같습니다.