항상 x86에서만 놀다가 시대에 뒤쳐졌지만 이제서야 서서히 x64에 발을 넣어보고 있다.
무섭지만..! 하지 않으면 안되므로.. ㅜ ㅜ
인터넷에 찾아보면 x64에 대한 많은 정보들이 있는 것 같지만 공부를 위해 스스로 시스템 콜 추적을 해봐야겠다.
===================================================================
먼저 타겟으로 잡은 OS는 Windows 7 x64 (AMD64) 이고 당연히 드라이버 싸이닝은 disable 시켜놓았다.
( 이번 편에서는 User Area 만 볼 것이므로 드라이버 싸이닝과 관련은 없다. )
추적대상 함수는 CreateFile 함수로 해보겠다.
Windows x64 같은 경우 x86 호환성을 제공하기 때문에 32bit 응용 프로그램들을 수정 없이 바로 사용할 수 있다.
위 그림을 보면 64bit 프로그램들은 프로세스 명 옆에 아무런 표시가 없고 32bit 프로그램들은 옆에 *32 가 붙어있는 것을 알 수 있다. 이처럼 32bit 프로그램이 바로 실행 가능한 이유는 Windows가 32bit 에뮬레이션 레이어(WOW64)를 운영하고 있기 때문이다.
Windows 폴더에 들어가면 32bit OS의 경우에는 System32 폴더만 있지만 64bit OS에선 SysWOW64라는 폴더가 하나 더 생긴 것을 알 수 있다. 처음에 보면 System32에 32bit 프로그램들이 들어가 있고 SysWOW64에 64bit 프로그램들이 들어가 있는 것으로 착각할 수 있는데 그 반대이다. 32bit 프로그램이 전부 SysWOW64 에 들어있다.
하지만 커널에서 작동하는 드라이버는 다르다. 커널 자체는 전부 64bit이고 에뮬레이션 레이어도 없기 때문에 드라이버는 무조건 64bit로 만들어져야한다. 즉.. 64bit OS에선 두 가지 형태의 조합이 나올 수 있다.
32bit user program + 64bit kernel driver
64bit user program + 64bit kernel driver
여기서 시스템 콜 추적할 때에는 위 두가지 경우를 다 살펴보도록 하겠다.
다음 편에서 커널 추적을 할 것이므로 여기선 USER만 살펴보도록 하겠다.
==================================
32bit USER
==================================
테스트를 위해 사용된 코드는 위와 같다. ( x86 & x64 동일 )
디버거는 WinDbg를 사용하였다. pdb가 없다면 EntryPoint에 BP를 걸고 시작해야되지만 여기선 pdb가 있기 때문에 그러한 과정은 생략한다. 또한 x86과 다르게 x64는 PE32+ 라는 구조를 가지며 기존의 IMAGE_OPTIONAL_HEADER 가 아닌 IMAGE_OPTIONAL_HEADER64 구조체를 가지고 자료형들이 조금씩(4Byte->8Byte) 바뀌었다. 이 점을 고려하여 ImageBase 있는 위치를 찾으면된다.
아무튼 CreateFile에 BP를 걸고 멈추고 쭉쭉 들어가 보면 아래와 같은 CALL을 한다.
CreateFile -> CreateFileW -> NtCreateFile
여기까진 32bit OS에서 실행하는 것과 같지만 NtCreateFile 내부는 32bit OS와는 다르다.
NtCreateFile 내부는 다음과 같이 생겼다.
ntdll32!NtCreateFile:
77300094 b852000000 mov eax,52h
77300099 33c9 xor ecx,ecx
7730009b 8d542404 lea edx,[esp+4]
7730009f 64ff15c0000000 call dword ptr fs:[0C0h]
773000a6 83c404 add esp,4
773000a9 c22c00 ret 2Ch
32bit OS에서 32bit 프로그램은 KiFastSystemCall 함수를 호출하지만
64bit OS에서 32bit 프로그램에선 fs:[0C0h] 를 호출하고 있다.
일단 위 코드에서 fs:[0C0h] 는 WOW32Reserved 를 나타내고 있다.
ntdll!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
0:000> dd fs:[0C0h]
0053:000000c0 748f2320 00000412 00000000 00000000
0053:000000d0 00000000 00000000 00000000 00000000
0053:000000e0 00000000 00000000 00000000 00000000
0053:000000f0 00000000 00000000 00000000 00000000
0053:00000100 00000000 00000000 00000000 00000000
0053:00000110 00000000 00000000 00000000 00000000
0053:00000120 00000000 00000000 00000000 00000000
0053:00000130 00000000 00000000 00000000 00000000
0:000> !address 748f2320
Usage: Image
Base Address: 748f1000
End Address: 748f4000
Region Size: 00003000
State: 00001000 MEM_COMMIT
Protect: 00000020 PAGE_EXECUTE_READ
Type: 01000000 MEM_IMAGE
Allocation Base: 748f0000
Allocation Protect: 00000080 PAGE_EXECUTE_WRITECOPY
Image Path: C:\Windows\SYSTEM32\wow64cpu.dll
Module Name: wow64cpu
Loaded Image Name:
Mapped Image Name:
More info: lmv m wow64cpu
More info: !lmi wow64cpu
More info: ln 0x748f2320
More info: !dh 0x748f0000
0:000:x86> lm t n
start end module name
01310000 01322000 Console_Study2 Console_Study2.exe Wed Nov 07 11:07:35 2012 (5099C267)
748f0000 748f8000 wow64cpu wow64cpu.dll Tue Aug 21 03:48:10 2012 (5032866A)
74900000 7495c000 wow64win wow64win.dll Tue Aug 21 03:48:12 2012 (5032866C)
74960000 7499f000 wow64 wow64.dll Tue Aug 21 03:48:09 2012 (50328669)
75010000 75120000 kernel32 kernel32.dll Tue Aug 21 02:40:01 2012 (50327671)
76770000 767b7000 KERNELBASE KERNELBASE.dll Tue Aug 21 02:40:02 2012 (50327672)
77100000 772a9000 ntdll ntdll.dll Thu Nov 17 15:32:46 2011 (4EC4AA8E)
772e0000 77460000 ntdll32 ntdll32.dll Thu Nov 17 14:28:47 2011 (4EC49B8F)
[출처] PE32와 PE32+의 차이점|작성자 좐심
[출처] PE32와 +의 차이PE32점|작성자 좐심
call r12 {wow64!whNtCreateFile (00000000`7497bee8)}
↓
call qword ptr [wow64!_imp_NtCreateFile (00000000`74961a48)]
Wow64SystemServiceEx 내부적으로 wow64.dll 에 있는 whNtCreateFile 함수를 호출하고 또 들어가 wow64.dll에 있는 NtCreateFile 함수를 호출하게 된다. 위는 32bit 모듈인 ntdll32.dll에 있는 NtCreateFile 이었고 이번 NtCreateFile은 64bit 모듈인 wow64.dll 에서 import 하고 있는 ntdll!NtCreateFile 이다. 모든 여정은 끝이났다. ntdll!NtCreateFile 내부를 보자.
ntdll!NtCreateFile:
00000000`77151860 4c8bd1 mov r10,rcx
00000000`77151863 b852000000 mov eax,52h
00000000`77151868 0f05 syscall
00000000`7715186a c3 ret
00000000`7715186b 0f1f440000 nop dword ptr [rax+rax]
syscall 이 발견되었다. 32bit OS에선 sysenter 였는데 왜 여긴 syscall 일까.. 이 부분은 아래서 설명하도록 하고
지금까지 CreateFile을 따라왔던 경로를 쭉 나열해보자.
(32bit)
kernel32 ! CreateFile ->
KERNELBASE ! CreateFileW ->
ntdll32 ! NtCreateFile ->
(64bit)
wow64cpu ! X86SwitchTo64BitMode ->
wow64cpu ! CpupReturnFromSimulatedCode ->
wow64cpu ! ServiceNoTurbo ->
wow64 ! Wow64SystemServiceEx ->
wow64 ! whNtCreateFile ->
ntdll ! NtCreateFile ( 최종도착 )
64bit 시스템에서 32bit 응용프로그램은 시스템콜 한번 하기가 참 힘든 것 같다.
또한 IA32 구조에선 모든 인자가 스택에 들어가 있지만(fastcall 제외) AMD64에선
RCX, RDX, R8, R9, 스택 순으로 들어가기 때문에 그에 맞춰서 인자도 위치도 전부 변경되었다.
그리고 위에서 syscall 에 대해 설명한다고 했는데 syscall에 대한 설명을 Intel 문서에서 찾아보았다.
SYSCALL은 기존의 RIP를 RCX에 저장하고 새로운 RIP를 로드하는데 그 위치는 IA32_LSTAR 라고 나와있다.
IA32_LSTAR 값은 Intel 문서에서 MSR 정보를 찾아보면 알 수 있다.
0xc0000082 값으로 나와있다.
kd> rdmsr C0000082H
msr[c0000082] = fffff800`02c7bec0
kd> ln fffff800`02c7bec0
(fffff800`02c7bec0) nt!KiSystemCall64 | (fffff800`02c7bfde) nt!KiSystemServiceStart
Exact matches:
nt!KiSystemCall64 (<no parameter info>)
그 위치는 nt 모듈에 있는 KiSystemCall64 라는 것을 알 수 있다.
이로서 커널 추적을 시작하면 된다.
이번엔 64bit 프로그램일 때 CreateFile 을 추적해 보자.
==================================
64bit USER
==================================
64bit 응용 프로그램은 번거로운 과정없이 kernel32.dll -> KERNELBASE.dll -> ntdll.dll 로 들어오면
ntdll!NtCreateFile 함수가 호출되는데 내부를 보면 끝이다.
ntdll!NtCreateFile:
00000000`77151860 4c8bd1 mov r10,rcx
00000000`77151863 b852000000 mov eax,52h
00000000`77151868 0f05 syscall
00000000`7715186a c3 ret
00000000`7715186b 0f1f440000 nop dword ptr [rax+rax]
바로 syscall이 보이며 앞 주소를 보면 32bit 응용 프로그램의 목적지와 같은 것을 알 수 있다.
이로서 이번 포스팅은 마치지만 뭔가 syscall 이 호출되는 과정으로 들어오는 시기에 이상한 부분이 있다.
ntdll32.dll은 존재하지 않은 모듈이다. ntdll.dll이 ntdll + ntdll32 인걸까?....
뭔가 혼동이온다. ㅡ ㅡ;
PS. x86 시스템에서 32bit 응용 프로그램 내부의 API 함수들의 가장 밑단을 후킹하려면 예전에는 KiFastSystemCall 함수를 인라인후킹했어야하는데 x64 시스템에서 32bit 응용 프로그램 내부의 API 함수의 가장 밑단을 후킹하려면 GS:[C0h] 에 있는 WOW32Reserved 의 값을 바꿔버리면 될 것같다.
'My Study > Programming&Theory' 카테고리의 다른 글
NTFS : MFT 요소 값 구하기 2가지 방법 (15) | 2012.11.20 |
---|---|
Windows x64 시스템 콜 Kernel Area (0) | 2012.11.09 |
Context 는 매우 중요하다!! (0) | 2012.11.06 |
x86 자가보호 DLL (1) | 2012.11.06 |
Themida의 JMP Trick 따라해보기 (1) | 2012.10.16 |