티스토리 뷰
Nt vs Zw - Clearing confusion on the Native API [펌 hongyver.springnote.com]
NineKY 2008. 3. 17. 08:09원문(OSR)의 글을 요약, 이해 정리한 글이다.
아래의 4가지 시나리오를 통해 NtXxx 함수와 ZwXxx함수에 대해 알아보자.
UserMode에서 NtXxx(NTDLL.DLL) 함수 호출하기
UserMode에서 ZwXxx(NTDLL.DLL) 함수 호출하기
KernelMode에서 NtXxx(NTOSKRNL.EXE) 함수 호출하기
KernelMode에서 ZwXxx(NTOSKRNL.EXE) 함수 호출하기
Calling From User Mode
NTDLL.DLL에서 NtXxx의 함수 NtReadFile을 Windbg에서 U 명령어를 통해 disassemble 한 결과는 아래와 같다.
-
0: kd> u ntdll!NtReadFile
ntdll!NtReadFile:
77f761e8 b8b7000000 mov eax,0xb7
77f761ed ba0003fe7f mov edx,0x7ffe0300
77f761f2 ffd2 call edx
77f761f4 c22400 ret 0x24
-
0: kd> u ntdll!ZwReadFile
ntdll!NtReadFile:
77f761e8 b8b7000000 mov eax,0xb7
77f761ed ba0003fe7f mov edx,0x7ffe0300
77f761f2 ffd2 call edx
77f761f4 c22400 ret 0x24
NtReadFile과 ZwReadFile은 0x77f761e8의 동일주소를 가르킨다.
CALL EDX의 0x7ffe0300의 주소를 ln(list nearest symblos) 명령을 통해 심볼을 본다.
- 0: kd> ln 0x7ffe0300(7ffe0300) SharedUserData!SystemCallStub
Exact matches:
SharedUserData!SystemCallStub - 0: kd> u SharedUserData!SystemCallStub
SharedUserData!SystemCallStub:
7ffe0300 8bd4 mov edx,esp
7ffe0302 0f34 sysenter
7ffe0304 c3 ret
SystemCallStub이란 함수에서 SYSENTER(윈도우2000에서는 INT 2Eh)를 호출한다.
SYSENTER(INTEL document)란?
thread into Kernel Mode and executes the routine pointed to by the SYSENTER_EIP_MSR, which is MSR 0x176.
커널로 전환하며 SYSENTER_EIP_MSR 레지스터가 가르키는 포인터 번지를 실행한다.
rdmsr(read msr, msr은 model specific register) 명령어로 SYSENTER_EIP_MSR를 살펴보면
- - 0: kd> rdmsr 176
msr[176] = 00000000:8053a270
- 0: kd> ln 8053a270
(8053a270) nt!KiFastCallEntry | (8053a2fb) nt!KiSystemService
Exact matches:
nt!KiFastCallEntry
결국 SYSENTER KiFastCallEntry를 호출한다는걸 알수있다.
- 053a2f9 eb5c jmp nt!KiSystemService+0x5c (8053a357)
KiFastCallEntry의 마지막(?)을 보면 KiSystemService를 호출한다.
아래는 OSR에서 제공한 windbg용 확장 DLL을 설치(windbg내 winext폴더에 복사)하여 !osrexts.sst를 사용하여 System Service Descriptor Table의 내용을 살펴보고 실제 주소를 disassemble한 예제이다.
- 0: kd> !osrexts.sst
0: 0x805912c2 (nt!NtAcceptConnectPort)
1: 0x805d87b0 (nt!NtAccessCheck)
2: 0x805dc3e4 (nt!NtAccessCheckAndAuditAlarm)
...
b7: 0x8056b2ec (nt!NtReadFile)
...
- 0: kd> u nt!NtReadFile
nt!NtReadFile:
8056b2ec 6a58 push 0x58
8056b2ee 6858044e80 push 0x804e0458
8056b2f3 e8e09ffcff call nt!_SEH_prolog (805352d8)
8056b2f8 33ff xor edi,edi
8056b2fa 897de4 mov [ebp-0x1c],edi
8056b2fd 897de0 mov [ebp-0x20],edi
8056b300 897dd8 mov [ebp-0x28],edi
요약
User mode에서 NtXxx , ZwXxx 호출은 결국 같은 루틴이다.
EAX에 index, EDX에 argument pointer 설정
SystemCallStub 를 호출하여 SYSENTER 호출
SYSENTER 는 인터럽트 방지하고 커널모드로 thread를 변경 SYSENTER_EIP_MSR (XP SP1에서는 KiFastCallEntry)를 호출
KiFastCallEntry 는 새로운 trap frame 형성, 인터럽트 사용가능후 KiSystemService 호출
KiSystemService 는 EDX를 통해 파라미터를 전달받고 EAX의 인덱스에 따라 KiServiceTable[EAX] 함수 호출
Calling From Kernel Mode
NTOSKRNL.EXE에서 NtXxx의 함수 NtReadFile을 Windbg에서 U 명령어를 통해 disassemble 한 결과는 아래와 같다.
(NTOSKRNL.EXE 모듈의 심볼명은 nt 이다. 이는 CPU 에 따라 NTOSKRNL.EXE의 이름이 변경되지만 심볼명은 nt 하나로 통일하기 위한것이 아닐까 라고 추정 - 이재홍님)
- 0: kd> u nt!NtReadFile
nt!NtReadFile:
8056b2ec 6a58 push 0x58
8056b2ee 6858044e80 push 0x804e0458
8056b2f3 e8e09ffcff call nt!_SEH_prolog (805352d8)
8056b2f8 33ff xor edi,edi
NtReadFile은 원래 함수라고 보여진다.
- 0: kd> u nt!ZwReadFile
nt!ZwReadFile:
80504d4c b8b7000000 mov eax,0xb7
80504d51 8d542404 lea edx,[esp+0x4]
80504d55 9c pushfd
80504d56 6a08 push 0x8
80504d58 e89e550300 call nt!KiSystemService (8053a2fb)
80504d5d c22400 ret 0x24
ZwReadFile은 이전에 보았던 코드와 유사하다.
EAX에 index 번호 0xb7을 넣고 EDX에 argument pointer를 설정하고 PUSHFD(PUSH EFLAGS)후 KiSystemService 바로 호출
KernelMode이기 때문에 KernelMode로 전환하기 위한 SYSENTER와 KiFastCallEntry 생략되었다.
아래그림은 EFLAGS
요약
Case A:
Kernel Mode 에서 NtXxx 함수 호출
PreviousMode 변경이 없다.
Case B:
Kernel Mode 에서 ZwXxxx 함수 호출
EAX에 index 번호 0xb7을 넣고 EDX에 argument pointer를 설정하고 PUSHFD(PUSH EFLAGS)후 KiSystemService 바로 호출
PreviousMode KernelMode로 설정
CALL KiServiceTable[EAX]
중요한 차이는 KiSysstemService를 통한 과정은 previous mode가 커널모드로 설정하는것이다.
Nt를 바로 호출하는게 overhead가 없지만 Zw호출은 previousmode를 변경한다.
Previous Mode
Previous mode란 시스템이 시스템 서비스를 호출하는 곳의 모드를 결정하기 위한 indicator로 사용된다.
Previous mode가 User mode로 설정되면 시스템 서비스 처리 루틴은 호출은 User mode에서 호출되는것으로 인식하고 루틴으로 전달된 파라미터를 사용하기전에 유효성 검사를 필요로 한다.
하지만 커널모드는 그러한 세밀한 조사를 하지 않고 유효하다고 가정한다.
Previous Mode를 구하는 API
- KPROCESSOR_MODE ExGetPreviousMode( VOID );
ZwXxx, NtXxx 호출시 실패하는 경우
NtXxx 함수를 직접 호출하면 previous mode를 변경되지 않기때문에 호출된 NtXxx 커널 시스템 서비스 루틴이 previous mode가 user mode로 설정된채 임의 user stack상에서 실행될수 있다. 이러한 호출은
Parameters의 유효성 확인 시도를 알지 못하기 때문에 호출실패가 발생할수 있다.
또다른 문제점은 ProbeForRead, ProbeForWrite를 통해 메모리가 Usermode의 메모리인지를 검사할때 Previous mode가 user mode로 설정된 상태에서 시스템 서비스 NtXxx 함수 호출시 커널모드 버퍼를 인자로 넘겨준다면 STATUS_ACCESS_VIOLATION이 발생할수 있다.
Sample Code
Sample code는 드라이버에서 c: 드라이버에 파일로 로그를 남기는 코드
InitializeObjectAttribute함수를 통해 c: 드라이버에 파일에 대한 핸들생성
- switch (operation)
- {
case IOCTL_OSR_ENABLE_LOGGING:
if (!devExt->LoggingEnabled)
{
RtlInitUnicodeString(&logFileName, OSRNTAPI_LOGFILE);
#ifdef USER_HANDLE
InitializeObjectAttributes(&oa, - &logFileName, OBJ_CASE_INSENSITIVE,
NULL, NULL); - #else
InitializeObjectAttributes(&oa, &logFileName,
OBJ_CASE_INSENSITIVE|OBJ_KERNEL_HANDLE, NULL, NULL);
#endif
code = ZwCreateFile(&devExt->LogFileHandle,
GENERIC_WRITE | SYNCHRONIZE,
&oa, &iosb, …
위의 예제에서 볼수 있듯이 핸들의 2가지가 생성될수 있다.
첫번째는 User mode 상에서 생성된 핸들(같은 프로세스의 context상에서만 접근가능, 다른 프로세스 접근 불가능)과 OBJ_KERNEL_HANDLE의 옵션을 주고 생성한 커널핸들(모든 프로세스, 드라이버에서 접근가능)이 있다.
응용프로그램에서 ReadFile 호출
드라이버 IRP_MJ_READ dispatch 함수, 드라이버에서 log write 시도
- PreviousMode = UserMode
code = NtWriteFile(devExt->LogFileHandle,
NULL,
NULL,
NULL,
&iosb,
(PVOID)"OsrRead: NtWriteFile\r\n",
strlen("OsrRead: NtWriteFile\r\n"),
NULL,
NULL);
#ifdef USER_HANDLE
ASSERT(code == STATUS_ACCESS_VIOLATION);
#else
// OBJ_KERNEL_HANDLE
ASSERT(code == STATUS_INVALID_HANDLE);
#endif
NtWriteFile 호출의 경우
#ifdef USER_HANDLE 정의시 (OBJ_KERNEL_HANDLE를 사용하지 않았을경우)
STATUS_ACCESS_VIOLATION 에러발생
넘겨진 파라미터는 커널버퍼이지만 PreviousMode가 User 이므로 ProbeForRead/Write 를 통해 유효성 검사에서 실패하여 발생
#ifdef USER_HANDLE 미정의시(OBJ_KERNEL_HANDLE를 사용했을경우)
STATUS_INVALID_HANDLE 에러발생
핸들생성시 커널핸들로 생성했지만 PreviousMode가 User 이므로 User모드 핸들에서는 커널핸들을 참조(찾을수 없다)할수없어 invalid 한 핸들에러 발생
- PreviousMode = KernelMode (KiSystemService에서 change)
code = ZwWriteFile(devExt->LogFileHandle,
NULL, NULL, NULL,
&iosb,
(PVOID)"OsrRead: ZwWriteFile\r\n",
strlen("OsrRead: ZwWriteFile\r\n"),
NULL, NULL);
ASSERT(code == STATUS_SUCCESS);
ZwWriteFile 호출의 경우
어떤 경우에서도 성공
Conclusion
User mode에서는 어떤것을 사용하여도 무방하나 kernel mode에서는 ZwXxx를 사용하여 previous mode가 커널로 설정되어 유효성 검사를 거쳐 시스템 서비스 함수들이 호출되도록 하여야 한다.
ETC
MSDN에 따르면 Zw로 시작하는 해당함수를 호출한 프로세스의 대한 접근권한 체크를 하지 않습니다.
'보안' 카테고리의 다른 글
- Total
- Today
- Yesterday
- 지루박멸연구센타
- 열정의 힘을 믿는다
- Le4rN TO Cr4cK
- 디버깅에관한모든것(DebugLab)
- sysinternals
- FoundStone
- hashtab
- 보안-coderant
- 디바이스드라이버 개발자 포럼
- dualpage.muz.ro
- osronline.com - 드라이버 관련 정보 사이트
- NtInternals - NativeAPI Refere…
- pcthreat - spyware 정보 제공
- rootkit.com - 루트킷 관련 정보
- www.ntinternals.net
- WINE CrossRef. - source.winehq…
- tuts4you
- hex-rays
- idapalace
- idefense
- immunityinc
- threatexpert
- hdp.null2root.org
- www.crackstore.com
- crackmes.de
- www.who.is
- www.cracklab.ru
- community.reverse-engineering.…
- video.reverse-engineering.net
- SnD
- 클레이 키위
- reversengineering.wordpress.co…
- www.openrce.org
- www.woodmann.com
- PEID.Plusins.BobSoft
- roxik.com/pictaps/
- regexlib.com
- spyware-browser.com
- www.usboffice.kr
- regulator
- www.txt2re.com
- ietab.mozdev.org
- zesrever.xstone.org
- www.heaventools.com/PE-file-he…
- www.heaventools.com
- www.innomp3.com
- 울지않는벌새
- exetools.com-forum
- exetools.com
- utf8 conv
- robtex - IP trace
- onsamehost - same IP sites
- JpopSuki
- jsunpack.jeek.org
- wepawet.iseclab.org
- www.jswiff.com
- www.hackeroo.com
- winesearcher.co.kr
- khpga.org
- malwareurl.com
- anubis.iseclab.org
- www.crummy.com-eautifulSoup
- malwarebytes.org/forums
- bbs.janmeng.com
- blackip.ustc.edu.cn
- eureka.cyber-ta.org
- exploit-db.com
- 맥쿼리인프라
- 공공인프라
- hai
- O365
- 주식
- CriticalSection
- PIR
- 다올저축은행
- 신한저축은행
- 자동트래이딩
- ElasticSearch
- ubuntu
- 주식트래이딩
- 군함도
- 피봇
- 레고랜드
- 매매가격지수
- ChatGPT
- INVOICE
- 주택구매력지수
- 시스템트래이딩
- ROA
- logrotate
- 미국주식
- 실시간트래이딩
- SBI저축은행
- 전세매매지수
- Pivot
- systemd
- 사회간접자본
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |