Presentation is loading. Please wait.

Presentation is loading. Please wait.

HandMade Native API Decomplie

Similar presentations


Presentation on theme: "HandMade Native API Decomplie"— Presentation transcript:

1 HandMade Native API Decomplie
안녕하세요 이번에 native API decompile이라는 주제를 가지고 같이 얘기 해볼 유정빈 이라고합니다 Decompile이라는 게 뭔지 아시는 분들이라면 왜 제가 주제에 ‘서handmade 라는 단어를 덧붙엿는지 아실거에요 간단한 내용설명과 더불어서 같이 한땀한땀 api 를 분석해보려 합니다(사실 ppt 만들기 시작하면서 이 주제로 왜햇는지 후회햇죠;) 주어진 시간이 생각보다 얼마 되지않아 완벽한 api 분석은 할수 없을것 같고, api 에서 제공하는 함수 두개정도를 같이 살펴보고, 이번 강연을 마치도록 하겟습니다

2 유정빈 Symnoisy.tistory.com 3c1c.tistory.com 시스템 해킹 리버싱 현 KSIA 연합원
그러면 일단 간단히 제소개를 하고 시작하도록 하죠. 앞에서도 이야기 했지만, 제 이름은 유정빈입니다 시스템해킹과 리버싱쪽으로 공부하고있고, 현재 3c1c라는 두명밖에 안되는 팀에서 팀장을 맡고있습니다 현 ksia 연합원 이기도 합니다. 발표를 들으시다가 궁금한점은 끝나고 물어봐주시고 그외의 물어볼것들은 제블로그나 페이스북을 통해 물어봐주세요

3 API? Native API? System call mechanism INDEX.
자 그러면 본격적으로 진행해 보도록 하죠. 같이 디컴파일을 하기전에 디컴파일이 뭔지, api는 또뭔지 native api는 뭔지 간단하게 설명을 하고 진행하도록 하겟습니다 제 생각에는 아마 디컴파일을 하기시작하면 반이상이 잠들거라 예상합니다 ㅎㅎ System call mechanism

4 Decompiler & decompile
INDEX. Compiler & compile Decompiler & decompile Native API Decompile

5 1 API? Application programming Interface
. Application programming Interface 운영체제가 어플리케이션과 통신을 위해서 제공하는 함수의 모음 자 그러면 API는 뭘까요? API 코딩을 한번이라도 해보신 분들이라면 한번정도는 들어봣을 법한 그런 단어입니다. API란 application programming interface의 약자입니다. 간단하게 얘기를 해보면, 우리가 OS 라 부르는 운영체제와 프로그램이 소통을 하기위해서, 운영체제가 제공해주는 함수의 모음이라고 생각하면 됩니다. 반대의 개념으로는 콜백 함수라는게 있는데, 이것은 우리가 짜놓은 함수가 반대로 운영체제에게 함수를 제공해주는 것이죠. 간단하죠?

6 1 API? WIn32 interface DLL과 컴포넌트 와의 관계
. API를 얘기하면 당연히 DLL 얘기도 나올수밖에 없어요 왜냐하면 DLL안에 API가 선언이 되어있기 때문이죠 dll 이란 Dynamic Link Library의 약자입니다. 프로그램 자체에는 포함 되지않고,개별적인 파일로 메모리상에 로드가 되게됩니다. 이렇게되면, 특정 메모리영역에 고정적으로 위치해 있기 떄문에, 프로그램에서는 그것을 그냥끌어쓰기만 하면되는 상황이 됩니다. 그렇기 떄문에 프로그램 전체를 패치하지 않더라도, 이 dll 만 건드려주면 모든 부분을 패치해줄수 있는거지요

7 2 Native API? WIn32 interface DLL과 컴포넌트 와의 관계
. 이어서 각 DLL별 특징을 한번 살펴보죠. Kernel32.dll 모듈안에 구현되어있는 kernel api의 경우에는 GUI와 관련되지않은 파일 I/O, 메모리관리, 객체관리 , 프로세스와 스레드 관리등을 제공합니다 . 이부분에 대한 실질적인 처리는 NTDLL에서 처리를 해주고, 커널단으로 넘어가는 식입니다. 그렇기 떄문에, kernel API를 호출해주면, 내부적으로 ntdll 이 호출이되죠. 그런데 kernel API와 ntdll 이후로 처리되는 부분은 커널 모드 부분인데 유저모드에서 그부분을 직접적으로 이동할수 있게 된다면, 보안적인 문제가 생기게 됩니다.쉽게 생각하면 root 부분의 권한인데 유저모드권한에서 침범하게되는거죠 . 그래서 이런 문제를 해결하기 위해서 시스템 콜 메커니즘이라는걸 이용하죠. 이내용은 바로 다음 페이지에서 살펴보도록 하겟습니다 GDI API의 경우에는 비트맵 이미지에 관련된 API고 ,USER API의 경우에는 윈도우창관리 ,메뉴, 대화상자같은 하이레벨 GUI 관련 인터페이스를 제공해줍니다. 내부적으로 USER API는GUI 객체를 그릴때 GDI를호출하게 됩니다.

8 2 System call mechanism 2E!2E!2E!2E!
. 유저모드에서 커널모드를 호출하기 위한 방법(주로 App이 OS API를 호출할때 자주 발생) KiSystemService, KiServiceTable SYSENTER 2E!2E!2E!2E! 그래서 systemcallmechanism이란 위에 써져있듯이 유저모드에서 커널모드를 호출하기 위한방법입니다. 이 메커니즘은 유저 모드와 커널모드 사이에, 특권 모드라는 모드를 추가해주는 겁니다. 그렇게 된다면, 유저모드에서 커널모드로 넘어갈때, 커널모드에게 유저모드가 커널모드를 사용하기를 원한다! 라는 메시지를 전송해주는 것과 같습니다. 윈도우 2000 버전 이하에서는 인터럽트 2e를 사용햇습니다. 이는 IDT를 이용해서 해당 인터럽트 핸들러를 호출하게 됩니다. 참고로, 2e 라는 인터럽트는 인터럽트 디스크립터 테이블안에서 Kisystemservice라는 NTOSKRNL 내부의 커널 서비스 함수를 가리킵니다. 이는 서비스번호와 스택포인터가 유효한지 확인하고나서, 요청된 커널 함수를 호출해 주게됩니다. 실질적인 커널 함수 호출부분은 다양한 커널 서비스 함수 주소들을 담고있는 KiServiceTable안에서 처리해줍니다. KiSystemService의 경우에는 KiServiceTable로 인덱스를 전달해주는 역할을 해줍니다

9 3 Native API? 메모리 관리자,I/O 시스템,객체관리자,프로세스와 스레드에 직접접근할 수 있는 인터페이스 제공
. 메모리 관리자,I/O 시스템,객체관리자,프로세스와 스레드에 직접접근할 수 있는 인터페이스 제공 EX)NtCreateFIle(), ZwCreateFile() Native api는 앞서 설명한 kernel32.dll 아래에있는 실질적인 명령들을 처리해주는 ntdll 아래에 선언되어있습니다. 그렇기 때문에 아까말햇던 .kernel32 dll과 비슷하게 메모리관리, i/o 시스템 객체관리 프로세스와 스레드에 직접접근할 수 있는 인터페이스를 제공해주게 되죠 .또한 실질적인 유저모드에 대한 처리는 이부분에서 하기 때문에 커널과 가장 직접적인 인터페이스라고 할 수 있습니다.가장직접적인 반면, 인텔사는 native api 부분을 문서화 하지않앗습니다 왜냐하면 이전버전들이 너무 느리게 사라져갔기때문이죠. 하위 윈도우 버전들과의 호환성을 위해서 문서화를 하지 않았습니다 그렇기 때문에, 우리가 실질적으로 이부분에 선언되어있는 api를 사용하려면, 인터넷을 뒤지거나 혼자 분석해서 사용해야 하는 상황이 발생합니다 그렇기 때문에 이런 주제를 가지고 한번 얘기해볼 필요가 있는거죠.아래에있는 것들은 네이티브 api의 함수들인데요. 네이티브 API의 경우 api이름앞에 nt 라던가 zw라는 접두사가 붙게 됩니다. Nt의 경우 api를 실질적으로 구현하는 함수이고, Zw의 경우 시스템콜 메커니즘이 실행되는 함수라는 것을 알아두면좋을것 같습니다 NT~~~~~ :API 를 실질적으로 구현하는 함수 ZW~~~~~~: 시스템 콜 메커니즘이 수행되는 함수 시스템콜 메커니즘을 통해서 커널모드 API를 호출하는 이유: 호출되는 API를 커널모드에서 호출한다는 것을 알려주기 위해서임 ~>유저모드에서 커널메모리 주소를 전달함으로써 얻을수 있는 시스템 corruption 방지

10 4 Compiler & Compile 목적코드 C (기계어코드) Compiler
. 목적코드 (기계어코드) Compiler C 그러면 잠깐 쉬어갈겸 간단한 내용들을 살펴보겠습니다 우선은 컴파일러 부분인데요. 저도 아직 컴파일러 부분을 깊숙히 공부해보지는 않았지만,

11 +) Disassem . 어셈블리 언어 Disassem. 목적코드 실행 바이너리 –> 어셈블러

12 5 Decompiler & Decompile . Decompile. 고급언어 Ex)C 실행 바이너리

13 문서화 되지 않은(native) API를 찾아보자`
전제 :) 리얼 극한의 상황, IDA 안깔려있고, 인터넷에서 문서 또한 찾을수 없다 가정. 우리에게 주어진건 해당 API에서 함수 일부를 끌어와서 사용하는 프로그램 달랑 하나 이번 발표의 main: 문서화 되지 않은(native) API를 찾아보자` 리얼 극한의 상황 아이다도 안깔려있고,인터넷에서 문서또한 찾을수없다 가정하며, 우리에게 주어진건 해당 api에서 사용하는

14 6 Native API decompile Ntdll!Generic Table RTL 함수(RunTime Library)
. Ntdll!Generic Table RTL 함수(RunTime Library) OS와 상호작용 없이 단순히 문자열이나 데이터 처리와 같은 일상적으로 요구되는 기능을 제공하는 함수

15 6 Native API decompile .

16 6 Native API decompile API를 끄집어 낸 다음 API의 이름들을 보고 대충 기능들을 유추해본다
. API를 끄집어 낸 다음 API의 이름들을 보고 대충 기능들을 유추해본다 기능들을 유추해보고 맨땅에 헤딩할때 분석해봐야할 가장 좋은 출발점은 ‘데이터 구조체’부분

17 그렇다면, 가장 처음 접근해야 할 API는 무엇인가?
그렇다면 이제부터 분석 ㄱㄱ

18 6 Native API decompile RtlInitializeGenericTable .

19 6 Native API decompile RtlInitializeGenericTable .

20 6 Native API decompile Offset18(decimal 24, 7th)=2nd param value
EAX= 1st parameter ECX =2nd parameter EDX=0 6 Native API decompile . UnknownStruct->Memb1=0; UnknownStruct->Memb3=&UnknownStruct->Memb2; UnknownStruct->Memb2=&UnknownStruct->Memb2; UnknownStruct->Memb4=&UnknownStruct->Memb2; Offset18(decimal 24, 7th)=2nd param value Offset1C(decimal 28 8th)=3rd param value Offset20(decimal 32, 9th)=4th param value Offset14(decimal 20,4th)=0 Offset10(decimal 16,3rd)=0 Offset24(decimal 36, 8th)=5th param value UnknownStruct->Memb7= Param2; UnknownStruct->Memb8= Param3; UnknownStruct->Memb9= Param4; UnknownStruct->Memb8= Param5; 스택이 올라가는 방법(높은주소에서 낮은주소, 아니면 낮은주소에서 높은주소 스택은 높은주소에서 낮은주소, 힙은 낮은주소에서 높은주소) , calling convention, intel 문법은 오른쪽에서 왼쪽 또는 왼쪽에서 오른쪽인지 짚어주기 1)Parameter 개수를 찾고 ,지역변수가 뭐가있나 찾아봄 LEA 명령- > 메모리 접근이 아닌 주소를 계산하기 위해서 사용 두번째 포인터로 세개의 포인터 모두 초기화됨 = 각 멤버는 세 포인터 그룹에 대한 포인터

21 6 Native API decompile Struct TABLE { UNKNOWN Member1;
. Struct TABLE { UNKNOWN Member1; UNKNOWN_PTR Member2; UNKNOWN_PTR Member3; UNKNOWN_PTR Member4; UNKNOWN Member5; UNKNOWN Member6; UNKNOWN Member7; UNKNOWN Member8; UNKNOWN Member9; UNKNOWN Member10; }

22 6 Native API decompile RtlInitializeGenericTable .

23 6 Native API decompile RtlGetElementGenericTable
. [summary] 루트 데이터 구조체 안에는 offset+4부터 시작해서 세개의 포인터 그룹이 존재한다 이 포인터들은 다른 세 포인터 그룹을 가리키고 처음에는 자신들을 가리키게 초기화되어 있다.(가정)이는 테이블에 엔트리가 차면 변경 RtlGetElementGenerticTable 함수는 이 포인터중 4번째 파라미터값에 12를 더해서 반환. 12는 세포인터의 전체 크기와 동일 테이블 데이터 구조체에 대한 포인터를 사용하고, 테이블에 대한 index 번호를 사용한다 도출해 낼수 있는 결론: Index 번호와 테이블 구조체에 대한 포인터를 사용한다. 테이블의 엔트리를 반환 값으로 반환한다(가정) 12를 더함으로써 헤더 부분이 아닌 데이터 부분을 반환한다. Offset + 0x10(5th Parameter) =>세번째 포인터인 Offset+C가 가리키는 인덱스 ( 7C 주소의 비교문) Offset +C(4th Parameter) => 메모리를 가리키는 포인터 (7C 주소의 ADD EAX(4th Parameter에 0C를 더해줌) ECX =1st parameter EDX=6th parameter EAX=4th parameter ESI=5th parameter EDI= 2nd parameter if(Param2 == 0xffffffff) return 0; AdjustedIndexNumber(EBX) =IndexNumber(EDI)+1 if( IndexNumber== 0xffffffff||(AdjustedIndexNumber >6th parameter) return 0; Interleaved코드 : 이부분은 상호 의존적인 명령쌍을 효과적으로 처리하기 위해서 코드위치를 변경한 것이다. 즉 CPU가 먼저 어떤 명령을 실행하기전에 먼저 실행해야 하는명령이 있을때 그 명령을 자연스럽게 먼저 실행하도록 명령위치를 배치한것이다. 간단하게 얘기하면 고수준의 병렬실행이 가능하게해줌 LEA EBX,[EDI+1] 로인해, 2번째 파라미터는 인덱스 번호라는 것을 유추 (Jmp above 명령과 함꼐) AdjustedIndexNumber라는 별칭을 또 따로준 이유는 EBX레지스터를 계속 사용하기 떄문임

24 6 Native API decompile RtlGetElementGenericTable . 헬 게이트 Open .. 애도요

25 (세번째 포인터(memb4,EAX)가 가리키는인덱스
6 Native API decompile RtlGetElementGenericTable(로직 이해) ECX =1st parameter EDX=6th parameter EAX=4th parameter (메모리를 가리키는 포인터) ESI=5th parameter (세번째 포인터(memb4,EAX)가 가리키는인덱스 EDI= 2nd parameter EBX = Table Index 값 LOOP1 :) 각 엔트리의 offset +4값을 취하면서 낮은 인덱스 방향 4th parameter: loop 돌면서 찾아낸 엔트리 포인터 5th parameter:해당 엔트리에 대한 인덱스 . 오잉? 일단 보류 빼기연산=비교연산 ESI-EBX=0 or ESI =EBX ESI-EBX= 검색 전체 개수 LOOP ESI !=EBX Case: ESI>EBX Jg , JL = 부호있는값 JA, JB = 부호없는값

26 6 Native API decompile RtlGetElementGenericTable(로직 이해)
ECX =1st parameter EDX=6th parameter EAX=4th parameter (result 가리키는 포인터) ESI=5th parameter (result 엔트리 가리키는인덱스 EDI= 2nd parameter EBX = Table Index 값 . EDX= EDX-(EBX)Table Index+1 EDI=EDI(=EBX)TableIndex-ESI EDX(6th Parameter)=리스트의 끝 ~찾고자하는 엔트리 인덱스 까지의 거리 EDI(2nd Parameter)=찾고자하는 엔트리 인덱스 ~마지막으로 찾은 엔트리 인덱스 까지의 거리 (=마지막인덱스) ESI< EBX (==인경우는 앞에서 윗줄에서 이미처리)

27 6 Native API decompile RtlGetElementGenericTable(로직 이해)
ECX =1st parameter EDX=6th parameter (처음~끝 인덱스 거리) EAX=4th parameter (메모리를 가리키는 포인터) ESI=5th parameter (세번째 포인터(memb4)가 가리키는인덱스 EDI= 2nd parameter (마지막Index) EBX = Table Index 값 . EDX(6th Parameter)=처음부터 끝까지 엔트리 인덱스의 거리 EDI(2nd Parameter)=찾고자하는 엔트리 인덱스 ~마지막으로 찾은 엔트리 인덱스 까지의 거리 (=마지막인덱스)

28 (세번째 포인터(memb4)가 가리키는인덱스
6 ECX =1st parameter EDX=6th parameter EAX=4th parameter (메모리를 가리키는 포인터) ESI=5th parameter (세번째 포인터(memb4)가 가리키는인덱스 EDI= 2nd parameter EBX = Table Index 값 Native API decompile RtlGetElementGenericTable (로직 이해) .

29 6 Native API decompile RtlGetElementGenericTable Struct TABLE
. Struct TABLE { PVOID UnKnown1; LIST_ENTRY *LLHead; LIST_ENTRY *SomeEntry; LIST_ENTRY *LastElementFound; ULONG LastElementIndex; ULONG NumberOfElements; ULONG Unknown1; ULONG Unknown2; ULONG Unknown3; ULONG Unknown4; }

30 6 Native API decompile RtlGetElementGenericTable
. PVOID stdcall RtlGetElementGenericTable(TABLE *Table,ULONG ElementToGet) { ULONG TotalElementCount=Table->NumberOfElements; LIST_ENTRY *ElementFound=Table->LastElementFound; ULONG LastIndexFound=Table->LastElementIndex; ULONG AdjustedElementToGet=ElementToGet+1; if(ElementToGet==-1 || AdjustedElementToGet>ToTalElementCount) if(AdjustedElementToGet != LastIndexFound) if(LastIndexFound >AdjustedElementToGet) ULONG HalfWayFromLastFound = LastIndexFound/2; if(AdjustedElementToGet >HalfWayFromLastFound) ULONG ElementToGo=LastIndexFound –AdjustedElementToGet; while(ElementsToGo--) ElementFound=ElementFound ->Blink; }

31 6 Native API decompile RtlGetElementGenericTable else {
. else { ULONG ElementsToGo = AdjustedElementToGet; ElementFound=(LIST_ENTRY*) &Table->LLHead; while(ElementsToGo--) ElementFound = ElementFound ->Flink; } ULONG ElementsToLastFound=AdjustedElementToGet – LastIndexFound; ULONG ElementsToEnd = TotalElementCount – AdjustedElementToGet +1; if (ElementsToLastFound <= ElementsToEnd) while(ElementsToLastFound -- ) ElementFound = ElementFound->Flink;

32 6 Native API decompile RtlGetElementGenericTable else {
. else { ElementFound = (LIST_ENTRY *)&Table->LLHead; while(ElementsToEnd--) ElementFound = ElementFound->Blink; } Table->LastElementFound= ElementFound; Talbe->LastElementIndex = AdjustedElementToGet; return (PVOID) ((PULONG)ElementFound +3);

33 결론: 디컴파일은 장인 정신을 요구한다 (하지만 꼭 한번은 해봐야 한다) 디컴파일러를 감사히 여기며 쓰자`

34

35 결론: 디컴파일은 장인 정신을 요구한다 (하지만 꼭 한번은 해봐야 한다) (이거로 발표는 하지말자 ;) 디컴파일러를 감사히 여기며 쓰자` (하지만 뭉개질때는 내가 분석해야한다)

36 THANK YOU! Q&A symnoisy.tistory.com facebook.com/jungbin.yu


Download ppt "HandMade Native API Decomplie"

Similar presentations


Ads by Google