<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet href="http://rss.egloos.com/style/blog.xsl" type="text/xsl" media="screen"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
	<title>6F637465742E65676C6F6F732E636F6D</title>
	<link>http://octet.egloos.com</link>
	<description>octopus's Blog</description>
	<language>ko</language>
	<pubDate>Mon, 21 Jan 2008 06:19:56 GMT</pubDate>
	<generator>Egloos</generator>
	<image>
		<title>6F637465742E65676C6F6F732E636F6D</title>
		<url>http://pds7.egloos.com/logo/200711/28/31/e0081331.jpg</url>
		<link>http://octet.egloos.com</link>
		<width>80</width>
		<height>107</height>
		<description>octopus's Blog</description>
	</image>
  	<item>
		<title><![CDATA[ Defeating Kernel Native API Hookers by Direct KiServiceTable Restoration ]]> </title>
		<link>http://octet.egloos.com/1296572</link>
		<guid>http://octet.egloos.com/1296572</guid>
		<description>
			<![CDATA[ 
  <pre><a class="link1" href="http://blog.naver.com/lsc3762/90014446734" target="_top">http://blog.naver.com/lsc3762/90014446734</a><br />
<br />
Defeating Kernel Native API Hookers by Direct KiServiceTable Restoration<br />
<br />
유저모드 API 호출<br />
- 유저모드 Win32 어플리케이션은 여러 dll이 익스포트하는 api를 호출해서 시스템 서비스를 요청한다.<br />
- 예를들어 오픈된 파일이나 파이프, 디바이스에 쓸려면 kernel32.dll이 익스포트하는 WriteFile api를 이용한다.<br />
- WriteFile api는 다시 ntdll.dll이 익스포트하는 NtWriteFile/ZwWriteFile native api를 호출한다.<br />
- ntdll.dll에서 NtWriteFile과 ZwWriteFile은 같은 코드를 포인트한다.<br />
- 실제 작업은 커널모드에서 이뤄지므로 NtWriteFile/ZwWriteFile은 INT 0x2E 소프트웨어 인터럽트를 이용하여 커널코드로 이동하는 <br />
  최소한의 코드만 갖는다.<br />
- win2k에서 NtWriteFile/ZwWriteFile의 디스어셈블은 다음과 같다.<br />
	MOV EAX, 0EDh<br />
	LEA EDX, DWORD PTR SS:[ESP+4]<br />
	INT 2E<br />
	RETN 24<br />
- 0xED는 커널 함수의 위치를 갖는 KiServiceTable의 인덱스에 이용되는 시스템 서비스 넘버이다.<br />
- WinXP에서 디스어셈블은 다음과 같다.<br />
	MOV EAX, 112h<br />
	MOV ED, 7FFE0300h<br />
	CALL DWORD PTR DS:[EDX]<br />
	RETN 24<br />
- 7FFE0300h은 다음 함수에 대한 포인터이다.<br />
	MOV EDX, ESP<br />
	SYSENTER<br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="display: none;"><br />
[ System Service Dispatcher ]<br />
- INT 2E에 대한 ISR(Interrupt Service Routine)은 KiSystemService이다.<br />
- KiSystemService는 파라미터를 검사한후 SSDT(System Service Dispatch Table, KiServiceTable)의 엔트리를 확인해서 해당 시스템 <br />
  서비스를 호출한다.<br />
--------------------------------<br />
0x00 NtAcceptConnectPort<br />
--------------------------------<br />
0x01 NtAccessCheck<br />
--------------------------------<br />
0x02 NtAccessCheckAndAuditAlarm<br />
--------------------------------<br />
.............................<br />
--------------------------------<br />
0xED NtWriteFile<br />
--------------------------------<br />
...........................<br />
--------------------------------<br />
<br />
[ KeServiceDescriptorTable ]<br />
- KiServiceTable은 커널에서 익스포트되있지 않다.<br />
- 그러나 주소는 KeServiceDescriptorTable로 알수 있다.(KeServiceDescriptorTable.ServiceDescriptor[0].KiServiceTable)<br />
	typedef struct ServiceDescriptorTable {<br />
		SDE ServiceDescriptor[4];<br />
	} SDT;<br />
<br />
	typedef struct ServiceDescriptorEntry {<br />
		PDWORD KiServiceTable;<br />
		PDWORD CounterTableBase;<br />
		DWORD ServiceLimit;	// KiServiceTable의 엔트리 갯수<br />
		PBYTE ArgumentTable;<br />
	} SDE;<br />
- ServiceDescriptor[1], [2], [3]은 사용되지 않는다.<br />
<br />
[ Kernel Native API Hookers ]<br />
- 커널에 로드되는 디바이스 드라이버는 KiServiceTable의 엔트리를 수정할수 있다.<br />
- 자신의 디바이스 드라이버내의 후킹함수를 포인트하도록 수정한다.<br />
- 보통, 원래 native api를 호출할수 있도록 원래 엔트리를 기억해둔다.<br />
<br />
NTSYSAPI NTSTATUS NTAPI NtXXXHook(...)<br />
{<br />
	ManipulateInputParameters(...);<br />
	NtXXXOriginal(...);<br />
	ManipulateReturnBuffers(...);<br />
	return;<br />
}<br />
<br />
- KiServiceTable 엔트리 수정은 보통 DriverEntry()에서 한다.<br />
<br />
#define SYSTEMSERVICE(_api) KeServiceDescriptorTable.ServceDescriptor[0].ServiceTable[*(DWORD *)((unsigned char *)_api+1)]<br />
                                                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^<br />
											system service number<br />
// 원래 함수 포인터를 기억<br />
NtWriteFileOrig=(NTWRITEFILE)(SYSTEMSERVICE(ZwWriteFile));<br />
<br />
// 후킹함수인 NtWriteFileHook()을 포인트하도록 KiServiceTable 엔트리를 수정<br />
(NTWRITEFILE(SYSTEMSERVICE(ZwWriteFile))=NtWriteFileHook;<br />
<br />
[ 프로세스 숨기기 ]<br />
- 유저모드 프로그램은 프로세스 리스트를 얻기위해 ToolHelp.dll이 익스포트하는 api를 이용한다.<br />
- 이 api는 ntdll.dll에서 익스포트하는 Nt/ZwQuerySystemInformation()을 호출한다.<br />
- 유저모드 프로그램에서 첫번째 파라미터를 SystemProcessAndThreadsInformation로 줘서 직접 Nt/ZwQuerySystemInformation()을 호출<br />
  할수도 있다.<br />
- 커널 루트킷은 NtQuerySystemInformation()을 후킹해서 첫번째 파라미터가 SystemProcessAndThreadInformation인지 검사한다.<br />
- 그런다음 리턴된 버퍼에서 프로세스를 숨긴후 콜러에게 리턴해준다.<br />
NTSYSAPI NTSTATUS NTAPI NtQuerySystemInformationHook(IN ULONG SystemInformationClass,<br />
	IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL)<br />
{<br />
	nts=NtQuerySystemInformationOrig(...);<br />
	if((nts==STATUS_SUCCESS) &amp;&amp; (SystemInformationClass==SystemProcesAndThreadsInformation)) {<br />
		ManipulateReturnBuffers(SystemInformation, ...);<br />
	}<br />
	return nts;<br />
}<br />
<br />
- 리턴된 버퍼는 다음과 같은 구조체 배열이다.<br />
typedef struct _SYSTEM_PROCESS_INFORMATION {<br />
	ULONG NextEntryDelta;	// 다음 엔트리 오프셋<br />
	ULONG ThreadCount;	// 스레드 갯수<br />
	ULONG Reserved[6];<br />
	LARGE_INTEGER CreateTime;	// 프로세스 생성시간<br />
	LARGE_INTEGER UserTime;		// 유저모드에서 쓰인 시간<br />
	LARGE_INTEGER KernelTime;	// 커널모드에서 쓰인 시간<br />
	UNICODE_STRING ProcessName;	// 프로세스 이름<br />
	KPRIORITY BasePriority;		// base process priority<br />
	ULONG ProcessId;	// pid<br />
	ULONG InheritedFromProcessId;	// ppid<br />
	.........<br />
	SYSTEM_THREAD_INFORMATION Threads[1];<br />
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;<br />
<br />
[ 로드된 드라이버 숨기기 ]<br />
- 보통 자기 자신을 숨기는데 쓰인다.<br />
- 유저모드 프로그램은 첫번째 파라미터로 SystemModuleInformation을 줘서 Nt/ZwQuerySystemInformation()을 호출해서 로드된 드라이버<br />
  목록을 구할수 있다.<br />
typedef NTSYSAPI NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(IN ULONG SystemInformationClass, IN OUT PVOID SystemInformation,<br />
	IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL);<br />
- 두번째 파라미터에 아웃풋 버퍼를 줘서 받는다.<br />
- 이 버퍼의 첫번째 DWORD값은 리턴된 배열 엔트리 갯수이다. 그다음 나머지 바이트는 다음과 같은 구조체 배열이다.<br />
typedef struct _SYSTEM_MODULE_INFORMATION {<br />
	ULONG Reserved[2];<br />
	PVOID ImageBase;<br />
	ULONG ImageSize;<br />
	ULONG Flags;<br />
	USHORT Index;<br />
	USHORT Unknown;<br />
	USHORT LoadCount;<br />
	USHORT ModuleNameOffset;<br />
	CHAR ImageName[256];<br />
} SYSTEM_MODULE_INFORMATION;<br />
<br />
[ 파일 숨기기 ]<br />
- 루트킷은 NtQueryDirectoryFile()을 후킹한다.<br />
<br />
- 어떻게 시스템 서비스 후킹을 탐지할수 있을까?<br />
SSDT는 커널 이미지 바깥쪽을 포인트하는 엔트리를 갖는다.<br />
Nt/ZwQuerySystemInformation()으로 드라이버 목록과 베이스 주소를 알수 있다.<br />
80400000 001A2340 - \WINNT\System32\ntoskrnl.exe<br />
80062000 00010460 - \WINNT\System32\hal.dll<br />
ED410000 00003000 - \WINNT\System32\BOOTVID.DLL<br />
BFFD8000 00028000 - ACPI.sys<br />
ED5C8000 00001000 - \WINNT\System32\DRIVERS\WMILIB.SYS<br />
<br />
kd&gt; d KeServiceDescriptorTable<br />
8046dfa0 b8 42 47 80 00 00 00 00-f8 00 00 00 9c 46 47 80 .BG..........FG.	ServiceDescriptor[0]<br />
8046dfb0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................	ServiceDescriptor[1]<br />
8046dfc0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................	ServiceDescriptor[2]<br />
8046dfd0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................	ServiceDescriptor[3]<br />
<br />
kd&gt; d 804742b8<br />
804742b8 52 dd 49 80 c1 f6 4a 80-3a 04 4b 80 b8 d5 50 80 R.I...J.:.K...P.<br />
804742c8 70 04 4b 80 a2 ce 45 80-be f7 50 80 fe f7 50 80 p.K...E...P...P.<br />
804742d8 38 4a 49 80 f2 a9 50 80-d8 de 4a 80 2d d8 4f 80 8JI...P...J.-.O.<br />
804742e8 49 a6 4a 80 df 4d 49 80-ca b8 44 80 3d 7e 4c 80 I.J..MI...D.=~L.<br />
804742f8 74 ee 5b f7 a9 94 4b 80-e0 db 4f 80 1a 19 40 80 t.[...K...O...@.<br />
80474308 1c 04 4d 80 76 95 41 80-86 6d 4f 80 8e 35 49 80 ..M.v.A..mO..5I.<br />
80474318 a8 f9 44 80 57 07 4b 80-d5 e2 49 80 75 dc 49 80 ..D.W.K...I.u.I.<br />
80474328 a0 92 46 80 84 4b 4f 80-d2 35 49 80 40 92 4c 80 ..F..KO..5I.@.L.<br />
<br />
KiserviceTable내의 주소값으로 시스템 서비스를 후킹하는 드라이버를 알수 있다.<br />
80400000 - \WINNT\System32\ntoskrnl.exe<br />
80062000 - \WINNT\System32\hal.dll<br />
....<br />
BF8B3000 - \SystemRoot\System32\Drivers\Cdfs.SYS<br />
BF6F4000 - \SystemRoot\System32\Drivers\Fastfat.SYS<br />
BF74F000 - \SystemRoot\System32\DRIVERS\ipsec.sys<br />
F75BA000 - \SystemRoot\System32\DRIVERS\KProcCheck.sys<br />
F75BE000 * \SystemRoot\System32\DRIVERS\gotr.sys<br />
<br />
[ KProcCheck POC ]<br />
C:\&gt;kproccheck -t<br />
KProcCheck Version 0.1 Proof-of-Concept by SIG^2 (www.security.org.sg)<br />
<br />
Checks SDT for Hooked Native APIs<br />
<br />
ZwAllocateVirtualMemory 	10 \SystemRoot\System32\DRIVERS\gotr.sys [F75BEE74]<br />
ZwCreateFile 			20 \SystemRoot\System32\DRIVERS\gotr.sys [F75BEA85]<br />
ZwCreateKey 			23 \SystemRoot\System32\DRIVERS\gotr.sys [F75BEC5E]<br />
ZwCreateProcess 		29 \SystemRoot\System32\DRIVERS\gotr.sys [F75BEDB7]<br />
ZwDeleteFile 			34 \SystemRoot\System32\DRIVERS\gotr.sys [F75BE80C]<br />
ZwGetTickCount 			4C \SystemRoot\System32\DRIVERS\gotr.sys [F75BEE27]<br />
ZwLoadDriver 			55 \SystemRoot\System32\DRIVERS\gotr.sys [F75BEBF2]<br />
ZwQueryDirectoryFile 		7D \SystemRoot\System32\DRIVERS\gotr.sys [F75BE6E8]<br />
ZwQuerySystemInformation 	97 \SystemRoot\System32\DRIVERS\gotr.sys [F75BE623]<br />
ZwSetInformationFile 		C2 \SystemRoot\System32\DRIVERS\gotr.sys [F75BE8A8]<br />
<br />
Number of Service Table entries hooked = 10<br />
<br />
- NtQuerySystemInformation(SystemModuleInformation, ...)이 후킹됐을수 있으므로 다른 방법으로 로드된 드라이버를 구한다.<br />
- 직접 PsLoadedModuleList를 트래버스해서 구한다.<br />
- Win2k 커널에서 PsLoadedModuleList는 다음과 같은 doubly linked-list의 시작이다.<br />
struct {<br />
	LIST_ENTRY link;	// Flink, Blink<br />
	BYTE unknown1[16];<br />
	DWORD ImageBase;<br />
	DWORD EntryPoint;<br />
	DWORD ImageSize;<br />
	UNICODE_STRING DrvPath;<br />
	UNICODE_STRING DrvName;<br />
	....<br />
}<br />
<br />
그러나 PsLoadedModuleList는 익스포트되 있지 않다. 그러므로 익스포트된 함수 MmGetSystemRoutineAddress()부터 다음과 같은 <br />
인스트럭션이 나올때 까지 스캐닝해서 주소를 찾는다.<br />
8b35b8e14680 mov esi,[nt!PsLoadedModuleList (8046e1b8)]<br />
81feb8e14680 cmp esi,0x8046e1b8<br />
<br />
Nt/ZwquerySystemInformation(SystemModuleInformatin, ...)과 위에서 찾은 값을 비교해서 숨겨진 드라이버를 찾을수 있다.<br />
C:\&gt;kproccheck -d<br />
KProcCheck Version 0.1 Proof-of-Concept by SIG^2 (www.security.org.sg)<br />
<br />
80400000 - \WINNT\System32\ntoskrnl.exe<br />
80062000 - \WINNT\System32\hal.dll<br />
F7410000 - \WINNT\System32\BOOTVID.DLL<br />
F7000000 - pci.sys<br />
F7010000 - isapnp.sys<br />
F7500000 - intelide.sys<br />
F7280000 - \WINNT\System32\DRIVERS\PCIIDEX.SYS<br />
F7288000 - MountMgr.sys<br />
BFFE3000 - ftdisk.sys<br />
...<br />
BF6F4000 - \SystemRoot\System32\Drivers\Fastfat.SYS<br />
BF74F000 - \SystemRoot\System32\DRIVERS\ipsec.sys<br />
F75BA000 - \SystemRoot\System32\DRIVERS\KProcCheck.sys<br />
F75BE000 * \SystemRoot\System32\DRIVERS\gotr.sys --[Hidden]--<br />
<br />
Total number of drivers = 73<br />
<br />
[ 후킹된 엔트리 복원 ]<br />
- 어떻게 원래 값을 알수 있을까?<br />
- KiServiceTable의 복사본은 ntoskrnl.exe에서 찾을수 있다.<br />
.data:004742B8 off_0_4742B8 dd offset sub_0_49DD52 ; DATA XREF: sub_0_55A996<br />
.data:004742BC dd offset sub_0_4AF6C1<br />
.data:004742C0 dd offset sub_0_4B043A<br />
.data:004742C4 dd offset sub_0_50D5B8<br />
.data:004742C8 dd offset sub_0_4B0470<br />
.data:004742CC dd offset sub_0_45CEA2<br />
.data:004742D0 dd offset sub_0_50F7BE<br />
.data:004742D4 dd offset sub_0_50F7FE<br />
.data:004742D8 dd offset NtAddAtom<br />
.data:004742DC dd offset sub_0_50A9F2<br />
.data:004742E0 dd offset NtAdjustPrivilegesToken<br />
.data:004742E4 dd offset sub_0_4FD82D<br />
.data:004742E8 dd offset sub_0_4AA649<br />
.data:004742EC dd offset NtAllocateLocallyUniqueId<br />
<br />
- 드라이버를 로드하거나 유저모드에서 직접 \device\physicalmemory에 써서 후킹을 복원할수 있다.<br />
- \device\physicalmemory는 커널 메모리를 포함한 피지컬 메모리에 유저모드 프로그램이 읽거나 쓸수 있게 해준다.<br />
<br />
[ SDTrestore POC ]<br />
- physical 메모리를 보기위해 \device\physicalmemory를 이용하는건 Mark Russinovich가 Physmem 툴에서 처음 사용되었다.<br />
90210 - \device\physicalmemory에 써서 GDT call gate를 인스톨한다. ActiveprocessLinks에서 자신의 EPROCESS 구조체를 언링킹해서 <br />
	프로세스를 숨긴다.<br />
crazylord - "playing with windows /dev/(k)mem", \device\physicalmemory에 써서 GDT call gate를 인스톨한후 ActiveProcessLinks를 <br />
	트래버싱해서 프로세스 목록을 구한다.<br />
다음은 어떻게 어드민 권한을 가진 유저모드 프로그램이 피지컬 메모리에 읽고 쓸수 있는 권한을 갖을수 있는지 설명한다.<br />
1. ntdll.dll에서 익스포트하는 NtOpenSection() native api에 SECTION_MAP_READ | SECTION_MAP_WRITE access flag를 줘서 <br />
   \device\physicalmemory 핸들을 구한다. 이건 보통 어드미니스트레이터가 \device\physicalmemory에 SECTION_MAP_WRITE 억세스 <br />
   권한이 없으므로 실패한다.<br />
2. NtOpenSection()에 READ_CONTROL | WRITE_DAC access flag로 피지컬메모리 핸들을 구한다. 이건 피지컬메모리 오브젝트에 새 DACL을 <br />
   추가해준다.<br />
3. 어드민이 SECTION_MAP_WRITE access권한을 가질수 있도록 피지컬메모리에 DACL을 추가한다.<br />
4. 다시 피지컬메모리 핸들을 구한다.<br />
<br />
피지컬메모리에 쓸려면 프로그램은 피지컬메모리 페이지를 자신의 버츄얼 어드레스 공간에 맵핑해야한다.<br />
ntStatus=_NtMapViewOfSection(<br />
	hPhyMem,	// \device\physicalmemory handle<br />
	(HANDLE)-1,	// OUT- physical memory가 맵핑될 virtual memory(paging file)<br />
	0,<br />
	*length,<br />
	&amp;viewBase,	// IN/OUT- 맵핑될 physical memory address(page aligned)<br />
	length, 	// IN/OUT- 맵핑된 physical memory size<br />
	ViewShare,<br />
	0,<br />
	PAGE_READWRITE<br />
);<br />
<br />
KiServiceTable을 복원할려면 ntoskrnl.exe 커널 파일 이미지에서 KiServiceTable의 위치와 physical memory 주소를 알아야 한다.<br />
win2k, winxp, 서비스팩에 따라 다르므로 이 주소들을 하드코딩할수 없다.<br />
NtmapViewOfSection()에서 페이지를 맵핑하는데 virtual address가 아니라 physical memory address를 이용한걸 기억하라.<br />
win2k에서 커널 베이스 주소는 가상메모리의 0x80400000이다.<br />
win2k 커널의 physical memory 주소는 0x400000이다.<br />
<br />
- 디스크 이미지에서 KiServiceTable의 주소 구하기<br />
ntoskrnl.exe를 dll로 로드한후 익스포트 테이블에서 KeServiceDescriptorTable의 오프셋을 구한다.<br />
ServiceDescriptorTable을 갖고있는 physical page로 맵핑한다.<br />
PhyaddrserviceDescriptorTable=kernelPhysicalBaseAddr+offset_of_KeServiceDescriptroTable<br />
맵핑된 페이지에서 KiServiceTable 주소를 구한다.<br />
이것은 커널영역에서 KiServiceTable의 virtual address(0x804742b8)일 것이다.<br />
kd&gt; d KeServiceDescriptorTable<br />
8046dfa0 b8 42 47 80 00 00 00 00-f8 00 00 00 9c 46 47 80 .BG..........FG.<br />
8046dfb0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br />
8046dfc0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br />
8046dfd0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br />
8046dfe0 20 f1 df ff 00 00 00 00-00 00 00 00 00 00 00 00 ...............<br />
8046dff0 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br />
8046e000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................<br />
<br />
커널 파일 이미지의 오프셋으로 변경한다.<br />
serviceTableOffset=serviceTableVirtualAddr-kernelVirtualBaseAddr(0x80400000)<br />
이렇게해서 KiServiceTable의 복사본의 오프셋을 알수 있다.<br />
그러나 이 방법은 실행중인 커널의 KiServiceTable의 오프셋이 디스크 이미지 내의 오프셋과 같아야 한다.<br />
루트킷이나 다른 보안프로그램이 KiServiceTable을 재배치한다면 이 방법은 안된다.<br />
rootkit.com의 90210이 디스크 이미지에서 KiServiceTable 위치를 찾는 보다 나은 방법을 제시했다.<br />
KiServiceTable은 KeServiceDescriptorTable의 복사본 실행이 설정됐을때 KiInitSystem에서 참조된다.<br />
INIT:0055AA65 	mov eax, offset dword_0_482258<br />
INIT:0055AA6A 	mov ds:KeServiceDescriptorTable, offset KiServiceTable<br />
INIT:0055AA74 	mov ds:dword_0_48225C, eax<br />
<br />
그런데 KiInitSystem은 익스포트되있지 않으므로 다음과 같은 인스트럭션을 ntoskrnl.exe에서 스캔해서 찾아야 한다.<br />
mov KeServiceDescriptorTable, imm32<br />
<br />
relocation table이 참조하는 위치만 스캔하므로 이 방법이 보다 효과적이고 믿을만 하다.<br />
이 테이블에서 각 엔트리에 대해 KeServiceDescriptorTable을 참조하는지 검사한다.<br />
참조한다면 인스트럭션이 mov KeServiceDescriptorTable, imm32 인지 검사한다.<br />
<br />
- KiServiceTable 패치<br />
디스크상의 KiServiceTable의 복사본의 위치를 찾은후에 실행중인 커널의 KiServiceTable을 갖고있는 피지컬 페이지로 맵핑하고 다른 <br />
엔트리를 패치한다.<br />
패치하기 전에 먼저 값을 커널의 virtual address로 바꿔줘야한다.<br />
for(DWORD i=0; i&lt;sdtCount; i++) {<br />
	if((kernelServiceTable[i]-kernelVirtualBase+peXH2.imageBase)!=fileServiceTable[i]) {<br />
		kernelServiceTable[i]=fileServiceTable[i]-peXH2imageBase+kernelVirtualBase;<br />
		printf("[+] Patched SDT entry %.2X to %.8X\n", i, kernelServiceTable[i]);<br />
	}<br />
}<br />
<br />
Native API Hookers 예제<br />
커널 루트킷- NT Rootkit, HE4Hook<br />
보안 툴- Sebek Win32, DiamondCS Process Guard, Kerio Personal Firewall 4<br />
<br />
HE4Hook은 다음과 같이 파일을 숨긴다.<br />
1. 다음과 같은 시스템 서비스를 후킹한다.<br />
ZwCreateFile 			20 --[hooked by unknown at 81222476]--<br />
ZwOpenFile 				64 --[hooked by unknown at 812224A8]--<br />
ZwQueryDirectoryFile 7D --[hooked by unknown at 812224D2]--<br />
2. 또는 파일 시스템 드라이버의 콜백 테이블을 후킹한다.<br />
1번 방법은 KiServiceTable 엔트리를 복원해서 쉽게 제거할수 있다.<br />
<br />
Sebek은 하니팟에서 주로 사용하는 콘솔 로거이다.<br />
cmd.exe의 인풋, 아웃풋을 캡쳐하고 로깅서버에 udp 패킷으로 보낸다.<br />
cmd.exe 세션을 암호화해서 로깅할수 있다.<br />
다음과 같은 시스템 서비스를 후킹한다.<br />
ZwClose 							18 SEBEK.sys [F729A092]<br />
ZwCreateFile 					20 SEBEK.sys [F729A98C]<br />
ZwCreateKey 					23 SEBEK.sys [F729AD10]<br />
ZwEnumerateKey 				3C SEBEK.sys [F729AE02]<br />
ZwEnumerateValueKey 			3D SEBEK.sys [F729AA50]<br />
ZwOpenFile 						64 SEBEK.sys [F729A8E6]<br />
ZwOpenKey 						67 SEBEK.sys [F729AD88]<br />
ZwQueryDirectoryFile 		7D SEBEK.sys [F729A4CC]<br />
ZwQuerySystemInformation 	97 SEBEK.sys [F729A5F0]<br />
ZwReadFile 						A1 SEBEK.sys [F7299CF0]<br />
ZwRequestWaitReplyPort 		B0 SEBEK.sys [F7299F14]<br />
ZwSecureConnectPort 			B8 SEBEK.sys [F7299FE6]<br />
ZwWriteFile 					ED SEBEK.sys [F7299D48]<br />
sebek.sys를 숨기기 위해(anti-detection), sebek.sys를 로드하는 레지스트리키를 숨기기 위해(anti-detection), cmd.exe를 로그하기 위해 <br />
ZwReadFile/ZwWriteFile을 후킹한다.<br />
역쉬 쉽게 무력화할수 있다.<br />
<br />
DiamondCS Process Guard<br />
프로세스 종료, 서스펜드, 악의적인 커널 드라이버가 로딩하는걸 보호하는 보안툴이다.<br />
ZwCreateFile 				20 \??\C:\WINNT\System32\drivers\procguard.sys 	[F7392D8A]<br />
ZwCreateKey 				23 \??\C:\WINNT\System32\drivers\procguard.sys	[F7391F98]<br />
ZwCreateThread 			2E \??\C:\WINNT\System32\drivers\procguard.sys	[F73924FC]<br />
ZwOpenFile 					64 \??\C:\WINNT\System32\drivers\procguard.sys	[F7392C62]<br />
ZwOpenKey 					67 \??\C:\WINNT\System32\drivers\procguard.sys	[F7391F64]<br />
ZwOpenProcess 				6A \??\C:\WINNT\System32\drivers\procguard.sys	[F739289E]<br />
ZwOpenThread 				6F \??\C:\WINNT\System32\drivers\procguard.sys	[F73926F8]<br />
ZwRequestWaitReplyPort 	B0 \??\C:\WINNT\System32\drivers\procguard.sys	[F7390AE6]<br />
ZwSetValueKey 				D7 \??\C:\WINNT\System32\drivers\procguard.sys	[F739224E]<br />
ZwWriteVirtualMemory		F0 \??\C:\WINNT\System32\drivers\procguard.sys	[F7392A40]<br />
<br />
Kerio Personal Firewall 4<br />
알려지지않은 또는 새로운 또는 수정된 프로그램이 실행될때마다 유저에게 프로세스를 실행할 것인지 묻는다.<br />
ZwCreateFile 		20 \SystemRoot\system32\drivers\fwdrv.sys [BFBD3830]<br />
ZwCreateProcess	29 \SystemRoot\system32\drivers\fwdrv.sys [BFBD3380]<br />
ZwCreateThread 	2E \SystemRoot\system32\drivers\fwdrv.sys [BFBD35E0]<br />
ZwResumeThread 	B5 \SystemRoot\system32\drivers\fwdrv.sys [BFBD3630]<br />
<br />
Native API 후킹 보안툴<br />
KiServiceTable을 복원해서 무력화시킬수 있다.<br />
이걸 방지하기 위해 추가적으로 다음과 같은게 필요하다.<br />
- NtLoadDriver를 후킹해서 드라이버가 로딩되는걸 막는다.<br />
- SystemInformationClass=SystemLoadAndCallImage[13]으로 NtSetSystemInformation을 후킹해서 드라이버 로딩을 막는다.<br />
- ZwOpenSection을 후킹해서 \device\physicalmemory로 write access를 막는다.<br />
- 심볼릭 링크를 통해 \device\physicalmemory로 write access를 막는다.<br />
<br />
후킹을 하지않는 루트킷<br />
FU 루트킷<br />
- PsLoadedModuleList에서 언링킹을 해서 드라이버를 숨긴다.<br />
- ActiveprocessLinks에서 언링킹을 해서 프로세스를 숨긴다.<br />
<br />
Process Hide- phide<br />
- ActiveProcessLinks에서 언링킹을 해서 프로세스를 숨긴다. 드라이버를 인스톨할 필요없이 GDT call gate를 통해 한다.<br />
<br />
이런류의 루트킷은 KiServiceTable을 복원하는 방법으로 무력화시킬수 없다.<br />
<br />
결론<br />
Sysem Service 후킹에 기반한 커널 루트킷은 KiServiceTable을 복원해서 무력화시킬수 있다.<br />
요즘 루트킷들은 후킹을 사용하지않고 탐지하고 무력화하기가 힘들다.<br />
보안툴은 시스템 서비스 후킹에 의존해선 안된다.</div></pre><br/><br/>tag : <a href="/tag/HOOKING" rel="tag">HOOKING</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>HOOKING</category>

		<comments>http://octet.egloos.com/1296572#comments</comments>
		<pubDate>Wed, 16 Jan 2008 03:00:03 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ < Fast System Call에 의한 커널코드 진입방식 > ]]> </title>
		<link>http://octet.egloos.com/1267855</link>
		<guid>http://octet.egloos.com/1267855</guid>
		<description>
			<![CDATA[ 
  <br />
<br />
&lt; Fast System Call에 의한 커널코드 진입방식 &gt;<br />
<br />
 <br />
<br />
1. 인텔 펜티엄 2 이상에서 사용함.<br />
<br />
2. SYSENTER, SYSEXIT 2개의 명령어 사용함.<br />
<br />
3. SYSENTER는 Ring 3에서 사용되는 명령어로, 마이크로프로세서에 있는 MSRs(MODEL-<br />
<br />
   SPECIFIC REGISTERS)로부터 진입하고자하는 커널의 CS, EIP의 레지스터 정보와 커널 레벨 진입<br />
<br />
   후, 사용하게 될 스택 정보 SS, ESP를 가져와서 세팅한 후 커널레벨로 진입이 이루어진다.<br />
<br />
http://blog.naver.com/gunner98?Redirect=Log&logNo=110026271729<br />
<br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="DISPLAY: none"> <br />
<br />
4. SS(Stack Segment)의 경우 SYSENTER_CS_MSR + 8로 지시되고 있으며, 이는 운영체제를 시작<br />
<br />
   할 때, Fast System Call을 지원하게 하기 위해서는 GDT(Global Descriptor Table)에서 SS(Stack<br />
<br />
   Segment)의 위치를 CS(Code Segment) 다음에 위치하도록 만들어야 한다는 이야기이다.<br />
<br />
5. SYSEXIT 명령는 다시 복귀하게 될 Application의 CS와 SS를 SYSENTER_CS_MSR + 16과<br />
<br />
   SYSENTER_CS_MSR + 24로 계산하여 찾으므로 GDT에서 커널레벨의 SS 다음에 배치해야 한다.<br />
<br />
6. SYSENTER에 의해 SYSENTER_EIP_MSR에 저징되어 있는 주소로 커널레벨 전환이 이루어진 후<br />
<br />
   커널에서는 자신이 수행하고자 하는 코드를 수행한 후 다시 Application으로 돌아오고자 하는 할 때<br />
<br />
   사용하는 명령어가 SYSEXIT이다.<br />
<br />
7. SYSEXIT는 IRET 과는 달리 우리가 Application 레벨로 복귀하게 될 EIP, ESP 값을 EDX, ECX 레<br />
<br />
   지스터에 먼제 세팅하여 준 후 호출해 주어야 하며, 이렇게 호출되어진 SYSENTER는<br />
<br />
   SYSENTER_CS_MSR + 16과 SYSENTER_CS_MSR + 24를 사용하여 복귀하게 된 Application의<br />
<br />
   CS와 SS를 세팅하고 SYSEXIT를 호출하기 전에 세팅한 EDX와 ECX 레지스터 값을 통하여 EIP,<br />
<br />
   ESP를 세팅한 후 복귀하게 된다.<br />
<br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds6.egloos.com/pds/200801/10/31/e0081331_478580936a643.jpg" width="488" height="498" onclick="Control.Modal.openDialog(this, event, 'http://pds6.egloos.com/pds/200801/10/31/e0081331_478580936a643.jpg');" /></div><br />
<br />
</div><br/><br/>tag : <a href="/tag/SYSENTER" rel="tag">SYSENTER</a>,&nbsp;<a href="/tag/SYSEXIT" rel="tag">SYSEXIT</a>,&nbsp;<a href="/tag/FastSystemCall" rel="tag">FastSystemCall</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>SYSENTER</category>
		<category>SYSEXIT</category>
		<category>FastSystemCall</category>

		<comments>http://octet.egloos.com/1267855#comments</comments>
		<pubDate>Thu, 10 Jan 2008 02:19:55 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[  Windows NT 시스템 콜은 어떻게 동작하는가? ]]> </title>
		<link>http://octet.egloos.com/1264493</link>
		<guid>http://octet.egloos.com/1264493</guid>
		<description>
			<![CDATA[ 
  <p class="just"><span class="title">  Windows NT 시스템                         콜은 어떻게 동작하는가?<br />
</span><br />
<br />
<!-- COPY AND PASTE CODE BELOW TO MAKE A NEW PARAGRAPH --><span class="subtitle"><span style="font-size: 12pt; font-family: 'Times New Roman';">                                                                                                                             Windows NT 시스템 콜을 설명하는 대부분의 문서들은                         중요한 부분들을 숨겨놓고 있다. 이로 인해 유저 모드 어플리케이션이                         커널 모드로 진입하는 것을 이해하려고 할 때 혼돈이 생긴다.                         다음 글은 시스템 서비스를&nbsp;수행하기 위해 커널 모드로 전환하는                         데에 Windows NT가 사용하는 정확한 메커니즘&nbsp;밝힐 것이다. 이 설명은                         보호 모드에서 동작하는 x86 호환 CPU에 대한 것이다.                         Windows NT가 지원하는 다른 플랫폼에서도 유사한 메커니즘이 사용될                         것이다.</span></span></p><p class="just"><br />
<span class="subtitle"><span style="font-size: 12pt; font-family: 'Times New Roman';"></span></span></p>      <br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="display: none;"><br />
<br />
                  <p class="just"><span>By John Gulbrandsen 8/19/2004<br />
<a href="mailto:John.Gulbrandsen@SummitSoftConsulting.com">John.Gulbrandsen@SummitSoftConsulting.com</a></span></p>                        <p class="just"><span>Translated into Korean by Snoya                         10/01/2004<br />
Homepage:&nbsp;<a href="http://www.system-inside.com/">system-inside.com</a><br />
MSN:                         <a href="mailto:neo3k@hanmail.net">neo3k@hanmail.net</a><br />
E-mail:                         <a href="mailto:neo3k@hanmail.net">neo3k@hanmail.net</a></span></p>                        <h2><font size="4">커널 모드란 무엇인가?</font></h2>                        <p class="MsoNormal">                                                                                                                                  대부분의 개발자(심지어 커널                         모드 개발자도)들이 생각하는 것과 다르게, x86 CPU에는                         "커널 모드"라고 불리는 모드가 없다.&nbsp;모토롤라 68000과 같은 CPU는                         CPU에 내장된&nbsp;두 가지의 프로세서 모드를 가진다.&nbsp;즉, 현재 user-mode 에서                         실행되는지 supervisor-mode 에서 실행되는지를 나타내는 status 레지스터를 가진다. 인텔 x86                         CPU는 그러한 플래그를 가지고 있지 않다. 대신에 수행 중인 프로그램의 권한을                         결정하기 위한 현재 실행중인 코드 세그먼트의 특권 레벨이 있다. x86                         CPU의 보호 모드에서 동작하는 어플리케이션의 각각의 코드 세그먼트는 세그먼트 디스크립터라고 하는 8바이트 크기의                         데이터 구조에 의해 서술된다. 세그먼트 디스크립터는&nbsp;세그먼트의 시작 주소와 세그먼트의 크기, 그리고 특권 레벨                         등과 같은 정보를 가지고 있다. 특권 레벨 3을                         가지는 코드 세그먼트에서 실행되는 코드는 유저 모드에서 실행된다고                         말하고 특권 레벨 0을 가지는 코드 세그먼트에서 실행되는 코드는 커널 모드에서                         실행된다고 말한다. 즉, 커널 모드(특권 레벨 0)과 유저 모드(특권 레벨                         3)은 CPU가 아닌 코드의 속성이다. 인텔은 특권 레벨 0을 "링 0",                         특권 레벨 3을 "링 3"이라고 부른다. x86 CPU에는 Windows NT는 사용하지 않는 두                         개의 특권 레벨이 더 있다(링 1, 링&nbsp;2). 특권 레벨 1,2가 사용되지                         않는 이유는 Windows NT는 여러 다른 하드웨어 플랫폼에서 동작하도록 설계되어                         있고 다른 CPU는 인텔 x86 CPU와 같이 4개의 특권 레벨을                         가지고 있을지 없을지 모르기 때문이다. <o:p></o:p></p>                        <p class="MsoNormal">                                                                                                                                    x86 CPU는 낮은 특권 레벨(숫자는                         더 높음)에서 실행되는 코드가 높은 특권 레벨(숫자는 더                         낮음)에서 실행되는 코드를 호출하는 것음 금지한다. 만약 이것이 시도되면,                         일반 보호(General Protection, GP) 예외가 CPU에 의해 자동으로 발생한다. 운영체제의                         일반 보호 예외 핸들러가 호출되고 적절한 처리가 이루어질                         것이다(사용자에게 경고, 어플리케이션 종료&nbsp;등). 특권 레벨을                         포함한 위에서 논의한 모든 메모리 보호 기능은 x86 CPU의 기능이지 Windows                         NT의 것이 아니라는 점에 유의하라. CPU의 지원없이는 Windows NT는 위와                         같은 메모리 보호를 수행할 수 없다.                         <o:p></o:p></p>                        <h2><font size="4">  세그먼트 디스크립터는 어디에                         있는가? <o:p></o:p></font></h2>                        <p class="MsoNormal">                                                                                                                                                                                                                                                시스템에 존재하는 각각의 코드 세그먼트는                         세그먼트 디스크립터에 의해 서술되기 때문에, 그리고 시스템에는&nbsp;많은 코드 세그먼트들이 존재할                         것이기 때문에, 세그먼트 디스크립터는 어딘가에 저장되어 있어야 할                         것이고&nbsp;프로그램이 어느 세그먼트의 코드를 실행하기를 원할 때 그것을                         허용하거나 금지하기 위해 CPU는 그것을 읽을 수 있어야 한다.                         인텔은 CPU 칩 자체에 그 모든 정보를 저장하는 것이 아닌 메인                         메모리에 저장하기로 결정하였다. 메인 메모리에는 두 개의 테이블이 있다; 전역                         디스크립터 테이블(Global Descriptor Table, GDT) 그리고 지역                         디스크립터 테이블(Local Descriptor Table, LDT). 이들 디스크립터 테이블의                         주소와 크기를 저장하기 위한 두 개의 레지스터가                         CPU 안에 있다. 이 레지스터는 전역 디스크립터 테이블 레지스터(Global                         Descriptor Table Register, GDTR)과 지역 디스크립터 테이블 레지스터(Local Descriptor Table                         Register, LDTR)이다. 이들 디스크립터 테이블을 설치하고 GDTR, LDTR 레지스터에 GDT, LDT                         의 주소를 적재하는 것은 운영체제의 책임이다. 이것은 부트 과정에서 매우                         이른 시기에, 심지어 CPU가 보호 모드로 전환되기                         전에 이루어져야 한다. 왜냐하면 디스크립터 테이블 없이는 보호 모드에서                         메모리 세그먼트가 액세스될 수 없기 때문이다. 아래                         그림 1은 GDTR, LDTR, GDT, LDT 사이의 관계를 나타낸다.</p>                        <p class="MsoNormal"><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c16299ce.jpg" width="500" height="322.49070632" onclick="Control.Modal.openDialog(this, event, 'http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c16299ce.jpg');" /></div></p>                        <p class="MsoNormal">                                                                   두 개의 세그먼트 디스크립터 테이블이                         있기 때문에 세그먼트 디스크립터를 선택하기위해 인덱스만을 사용하는 것은 충분하지                         않다. 둘 중 어느 테이블을 선택할 지를 결정하는                         1 비트가 필요하다. 테이블 지시가 비트와 결합된 인덱스를 세그먼트                         셀렉터라고 한다. 세그먼트 셀렉터의 형식은 아래와 같다.</p>                        <p class="MsoNormal"><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds6.egloos.com/pds/200801/09/31/e0081331_47848c1b8bf27.jpg" width="500" height="229.523809524" onclick="Control.Modal.openDialog(this, event, 'http://pds6.egloos.com/pds/200801/09/31/e0081331_47848c1b8bf27.jpg');" /></div></p>                        <p class="MsoNormal">                                                                                                                                                                                                                                                                                                                         위                         그림 2와 같이, 세그먼트 셀렉터는 RPL(Requestor Privilege Level)이라고 하는 2                         비트 필드를 포함한는데 그것은&nbsp;이 셀렉터가 가리키는 코드 세그먼트 디스크립터에 접근할                         수 있는지를 결정하는데 사용된다. 예를 들어, 특권 레벨 3(유저 모드) 코드가 셀렉터가                         가리키는 코드 세그먼트 디스크립터에 의해 서술되는 코드 세그먼트의 코드를 호출하려고                         할 때, 셀렉터의 RPL 특권 레벨 0에서 실행되는                         코드만을 읽을 수 있다고 지시한다면 일반 보호                         예외가 발생한다(?).&nbsp;이렇게 x86 CPU는 링 3(유저 모드)                         코드가 링 0(커널 모드) 코드에 접근하는 것을 막는다. 사실은 이것보다 약간 더                         복잡하다. 보다 자세한 것을 알기 원하면 더 읽을거리 목록에 있는&nbsp;"Protected Mode                         Software Architecture"의 RPL 부분을 참고하라. 우리의 목적을 위해서는 이정도만 알고                         있으면 충분하다. <o:p></o:p></p>                        <h2><font size="4">인터럽트 게이트</font> <o:p></o:p></h2>                        <p class="MsoNormal">유저 모드에서 실행되는                         어플리케이션 코드가 커널 모드 코드를 호출할 수 없다면 어떻게 Windows NT의 시스템 콜이                         동작하는 것일까? 다시 해답은 CPU의 기능을 이용하는 것이다. 서로 다른 특권 레벨 사이에서의 제어                         이행을 위해서                         Windows NT는 인터럽트 게이트라고 하는 x86 CPU의 기능을 이용한다.                         인터럽트 게이트를 이해하기 위해 우리는 먼저&nbsp;x86 CPU&nbsp;보호 모드에서 인터럽트가                         어떻게 사용되는지 알아야 한다. <o:p></o:p></p>                        <p class="MsoNormal">대부분의 다른 CPU처럼,                         x86 CPU 또한 각각의 인터럽트를 어떻게 처리할 것인지에 대한 정보를                         포함하는 인터럽트 벡터 테이블을 가지고 있다. 리얼 모드에서                         x86 CPU의 인터럽트 벡터 테이블은 단순히                         인터럽트 서비스 루틴(Interrupt Service Routine, ISR)의 주소(4바이트 크기)를                         가진다. 그러나 보호 모드에서 인터럽트 벡터 테이블은 8바이트 크기의 인터럽트 게이트                         디스크립터를 가진다. 인터럽트 게이브 디스크립터는 어느 코드 세그먼트에                         ISR이 존재하는지 그리고 그 코드 세그먼트에서 ISR은 어디에서 시작하는지에 대한 정보를                         가지고 있다. 인터럽트 벡터 테이블이 단순한 포인터가                         아닌 인터럽트 게이트 디스크립터를 가지는 이유는, 유저                         모드 코드는 커널 모드 코드를 직접 호출할 수 없다는                         법칙 때문이다. 인터럽트 게이트 디스크립터의 특권 레벨을 검사함으로써 CPU는                         호출하는 어플리케이션이 명확한 위치에 있는 보호된 코드를 호출하는 것이                         허용된다는 것을 확인한다(이것이 바로 "인터럽트 게이트"라는 이름이 붙은 이유이다. 즉, 그것은                         유저 모드 코드가 커널 모드 코드로 제어를 이행할 수                         있도록 하는 명확히 정의된 문이다).&nbsp; <o:p></o:p></p>                        <p class="MsoNormal">                                                                                                                                                               인터럽트 게이트 디스크립터는 인터럽트 서비스 루틴을 가지고 있는                         코드 세그먼트를 서술하는 코드 세그먼트 디스크립터를 가리키는                         세그먼트 셀렉터를 포함한다. 우리의 Windows NT 시스템 콜의 경우,                         세그먼트 셀렉터는 전역 디스크립터 테이블에 있는 코드 세그먼트                         디스크립터를 가리킨다. 전역 디스크립터 테이블은 모든 "전역" 세그먼트 디스크립터를                         가지고 있다. 즉, 시스템에서 실행중인 어느 특정 프로세서에                         국한되는 것이 아니다(GDT는 운영체제의 코드와 데이터 세그먼트를 서술하는                         세그먼트 디스크립터를 가진다). 그림 3은 'int 2e'                         명령으로 인한 인터럽트 디스크립터 테이블과 전역 디스크립터 테이블                         엔트리 그리고 목적지 코드 세그먼트에 있는 인터럽트 서비스 루틴                         사이의 관계를 나타낸다.</p>                        <p class="MsoNormal"><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c235d9e7.jpg" width="500" height="347.517730496" onclick="Control.Modal.openDialog(this, event, 'http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c235d9e7.jpg');" /></div></p>                        <h2><font size="4">  NT 시스템 콜을 알아보자</font></h2>                        <p class="MsoNormal">                                                                                                                                                                                                                                           어느                         정도 배경 지식을 쌓았으므로 이제 Windows NT 시스템                         콜이 어떻게 유저 모드에서 커널 모드로 전환하는지 정확하게 알아볼                         준비가 되었다. Windows NT 에서의 시스템 콜은 "int                         2e" 명령을 실행함으로써 시작된다. 'int' 명령어는 CPU로 하여금 소프트웨어 인터럽트를                         발생하도록 한다. 즉, 인터럽트 디스크립터 테이블로 가서 인덱스 2e에 있는                         인터럽트 게이트 디스크립터를 읽는다. 인터럽트 게이트 디스크립터는 ISR을 가지고 있는                         코드 세그먼트를 가리키는 세그먼트 셀렉터를 포함한다. 또한 그 목표                         코드 세그먼트 내의 ISR이 위치하는 오프셋도 포함한다. CPU는 GDT                         또는 LDT 에 있는 엔트리를 결정하기 위해 세그먼트                         셀렉터를 사용할 것이다. 일단 CPU가 목표 세그먼트 디스크립터에 있는 정보를                         알게되면 그 정보를 CPU로 로드한다. 또한 인터럽트 게이트 디스크립터에                         있는 오프셋 값을 EIP 레지스터에 로드한다. 이 시점에서 CPU는 커널 모드                         코드 세그먼트에 있는 ISR을 실행할 준비가 거의 되었다. </p>                        <h2><font size="4">  CPU는 자동으로 커널 모드                         스택으로 전환한다</font></h2>                        <p class="MsoNormal">                                                                                                                                                                                                         CPU가 커널 모드 코드                         세그먼트에 있는 ISR을 실행하기 전에, 커널 모드 스택으로 전환되어야                         한다. 그 이유는, 커널 모드 코드는 커널                         모드에서 실행되기 위한 충분한 공간이 필요하기 때문에 유저 모드                         스택을 신용할 수가 없기 때문이다. 예를 들어, 멍청한                         유저 모드 코드가 스택 포인터를 유효하지 않은 메모리                         주소를 가리키도록 했을 때 'int 2e' 명령을 수행하면                         커널 모드 함수가 그 스택 포인터를 사용할                         때 시스템 충돌을 일으킬 것이다. 따라서 x86 보호 모드                         환경에서 각각의 특권 레벨은 자신의 스택을 가진다. 인터럽트 게이트 디스크립터                         등을 통해 더 높은 특권 레벨로의 함수                         호출이 이루어질 때, CPU는 자동으로 유저 모드 프로그램의 SS,                         ESP, EFLAGS, CS, EIP 레지스터를 커널 모드 스택에 저장한다.                         Windows NT 시스템 서비스 디스패쳐 함수(KiSystemService)의 경우 유저 모드 코드가 'int                         2e'를 호출하기 전에 스택에 넣은 인자에 접근할 필요가                         있다. 규칙에 의해, 유저 모드 코드는 'int                         2e'를 호출하기 전에 유저 모드 스택의 인자들을 가리키는 포인터를 EBX                         레지스터에 적재해야 한다. 그러면 KiSystemService는 호출되는 시스템 함수가                         필요로하는 만큼의 인자를 유저 모드 스택에 커널                         모드 스택으로 복사한다. 그림 4는 이에 대하여 설명하고 있다.</p>                        <p class="MsoNormal"><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c291fb02.jpg" width="500" height="345.108695652" onclick="Control.Modal.openDialog(this, event, 'http://pds7.egloos.com/pds/200801/09/31/e0081331_47848c291fb02.jpg');" /></div></p>                        <h2><font size="4"> 어떤 시스템 호출을 우리는                         호출하는가?</font></h2>                        <p class="MsoNormal">모든 Windows NT 시스템                         콜은 커널 모드로 전환하기 위해 'int 2e' 소프트웨어 인터럽트를 사용한다. 그러면 유저 모드                         코드는 어떻게 커널 모드 코드에게 어떤 시스템 함수가 실행되어야 하는지를 알릴까? 대답은 'int                         2e' 가 호출되기 전에 EAX 레지스터에 인덱스가 적재되어야 한다는 것이다. 커널 모드 ISR은                         EAX 레지스터를 보고 유저 모드에서 넘어온 모든 인자가 올바르다면 해당 함수를 호출한다. ISR에                         의해 커널 모드 함수로 인자가 전달된다.</p>                        <h2><font size="4">  시스템 콜에서                         돌아와서</font></h2>                        <p class="MsoNormal">일단 시스템 콜이 완료되면                         IRET 명령에 의해 CPU는 자동으로 프로그램의 원래 레지스터 값을 복원한다. 커널 모드 스택에                         저장된 레지스터 값들을 꺼내고 CPU는 'int 2e' 다음의 유저 모드                         코드를 계속 실행한다.</p>                        <h2><font size="4">실험</font></h2>                        <p class="MsoNormal">                                                                  인터럽트 디스크립터 테이블 2e 번째 엔트리에                         있는 인터럽트 게이트 디스크립터를 조사함으로써 우리는 CPU가 Windows                         NT 시스템 서비스 디스패쳐 루틴을 이 글에서 설명한 것처럼                         발견하는 것을 확인할 수 있다. 이 문서의                         샘플 파일에는 GDT, LDT, IDT 내의 디스크립터를 표시하는 WindDbg 확장                         DLL을 포함한다.</p>                        <p class="MsoNormal"> 예제 코드 다운로드: <a href="http://snoya.ye.ro/driver/ntsystemcalls/pds/ProtMode.zip">ProtMode.zip</a></p>                        <p class="MsoNormal">                                                               이 확장 DLL은&nbsp;'protmode.dll' (Protected Mode) 파일이다. kdextx86.dll                         이 있는 디렉토리에 이 DLL을 복사한 후 WinDbg에서                         ".load protmode.dll" 명령을 내리면 이 확장 DLL이 로드된다(역자                         주: 보통, 경로는 다음과 같을 것이다. C:\Program Files\Debugging                         Tools for Windows\w2kfre(또는 w2kchk, winxp)). 타겟 플랫폼에 연결되었다면 브레이크를                         걸어라. 'int 2e'에 대한 IDT 디스크립터를 표시하는 명령은 "!descriptor                         IDT 2e" 이다. 이 명령은 다음과 같은 정보를                         표시한다. <o:p></o:p></p>                        <p class="MsoNormal" style=""><span style="background: white none repeat scroll 0% 50%; font-size: 9pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: Courier;">kd&gt;!descriptor                         IDT 2e<br />
------------------- Interrupt Gate Descriptor                         -------------------- <br />
IDT base = 0x80036400, Index =                         0x2e, Descriptor @ 0x80036570 <br />
80036570 c0 62 08 00                         00 ee 46 80 <br />
Segment is present, DPL = 3, System                         segment, 32-bit descriptor <br />
Target code segment selector =                             0x0008 (GDT Index = 1, RPL = 0) <br />
Target code segment offset =                             0x804662c0 <br />
-------------------                         Code Segment Descriptor -------------------- <br />
GDT base = 0x80036000, Index =                               0x01, Descriptor @ 0x80036008                         <br />
80036008 ff ff 00 00 00 9b cf 00 <br />
Segment size is                         in 4KB pages, 32-bit default operand and data size                         <br />
Segment is present, DPL =         0, Not system segment,                         Code segment <br />
Segment is not conforming, Segment is                         readable, Segment is accessed <br />
Target code segment base address =                              0x00000000 <br />
Target code segment size =                         0x000fffff&nbsp;</span><span style="background: white none repeat scroll 0% 50%; font-size: 9pt; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; color: black; font-family: Courier;">                         <o:p></o:p></span></p>                        <p class="MsoNormal"><o:p></o:p></p>                        <p class="MsoNormal">'descriptor' 명령은 다음과 같은 정보를                         파해쳐낸다: </p>                        <p class="MsoNormal"><o:p></o:p></p>                        <ul style="margin-top: 0in;" type="disc"><li class="MsoNormal" style="">                             IDT 인덱스 2e 에 있는 디스크립터의 주소는 0x80036570                           이다.                           <br />
</li><li class="MsoNormal" style="">그 주소에                           있는 디스크립터의 실제 값은 C0<span style="">&nbsp; </span>62 08 00 00 EE 46 80                           이다.                           <br />
</li><li class="MsoNormal" style="">그 값은 다음을                           의미한다:                           <ul style="margin-top: 0in;" type="circle"><li class="MsoNormal" style="">                                 인터럽트 게이트 디스크립터의                             세그먼트 셀렉터에 의해 서술되는 코드 세그먼트 디스크립터를                             포함하는 세그먼트가 존재한다.                             <br />
</li><li class="MsoNormal" style="">최소한                             특권 레벨 3에서 실행되는 코드도 이 인터럽트 게이트를 이용할                             수 있다.                             <br />
</li><li class="MsoNormal" style="">                                   우리의 시스템                             콜(2e)를 위한 인터럽트 핸들러를 가지고 있는 세그먼트는 GDT의                             인덱스 1 에 위치한 세그먼트 디스크립터에 의해 서술된다.                             <br />
</li><li class="MsoNormal" style="">                              KiSystemService는 타겟 세그먼트 내의 오프셋                             0x804662c0 에서 시작한다.<br />
</li></ul><br />
</li></ul>                        <p class="MsoNormal"><o:p></o:p></p>                        <p class="MsoNormal">                                  "!descriptor IDT 2e" 명령은 또한 GDT                         인덱스 1 에 있는 타겟 코드 세그먼트 디스크립터를 덤프한다. 아래는 이                         GDT 디스크립터에 대한 설명이다:</p>                        <p class="MsoNormal"><o:p></o:p></p>                        <ul style="margin-top: 0in;" type="disc"><li class="MsoNormal" style="">                               GDT 인덱스 1 에 있는 코드 디스크립터의                           주소는&nbsp;0x80036008 이다.                           <br />
</li><li class="MsoNormal" style="">그주소에                           있는 디스크립터의 실제 값은&nbsp;FF FF 00 00 00 9B CF 00 이다.                           <br />
</li><li class="MsoNormal" style="">그 값은                           다음을 의미한다:                           <ul style="margin-top: 0in;" type="circle"><li class="MsoNormal" style="">                                  단위는&nbsp;4KB 페이지이다. 이것이 의미하는 것은,&nbsp;크기 필드                             값(0x000fffff)은 실제 크기를 얻기 위해 가상 메모리                             페이지&nbsp;크기(4096 바이트)로 곱해져야 한다는 것이다. 그 결과 4GB 크기가 된다.&nbsp;이것이                             바로 커널 모드 코드가 메모리의 커널 모드 영역뿐만                             아니라 유저 모드 영역에도 접근할 수 있는 이유이다(역자 주:                             GDT 인덱스 3에 있는 유저 모드 세그먼트도 4GB의 크기를 가진다.                             그러나 상위 2G의 커널 영역에는 접근할 수 없는데                             그러한 보호는 세그멘테이션이 아닌 페이징 단계에서 보호하는                             것으로 알고 있다. 따라서 크기가 4GB이기 때문에 전체                             메모리 영역에 접근할 수 있다는 말은 문제가 있는 것                             같다).                             <br />
</li><li class="MsoNormal" style="">                                커널 모드 세그먼트이다(DPL=0).                             <br />
</li><li class="MsoNormal" style="">                                   이                             세그먼트는&nbsp;conforming 이 아니다. 더 읽을거리&nbsp;목록 중&nbsp;"Protected Mode                             Software Architecture" 를 참고하라.                             <br />
</li><li class="MsoNormal" style="">                                                                                                       이 세그먼트는 읽기 가능하다. 더                             읽을거리 목록 중 "Protected Mode Software Architecture" 를                             참고하라.                             <br />
</li><li class="MsoNormal" style="">                                 이 세그먼트는 accessed                             되었다. 더 읽을거리 목록 중 "Protected Mode                             Software Architecture" 를 참고하라.<br />
</li></ul><br />
</li></ul>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">ProtMode.dll WinDbg 확장 DLL을 빌드하기                         위해서는,&nbsp;Visual Studio 6.0 으로 프로젝트를                         연 후 빌드를 클릭하라. ProtMode.dll 과 같은 확장                         DLL을 작성하는 방법은 "Debugging Tools for Windows"&nbsp;에 들어있는&nbsp;SDK을 참고하라(역자 주:                         WinDbg을 설치하면 도움말 파일이 설치되는 데 확장 DLL을                         작성하는 데에도 도움이 될 뿐만 아니라 전체적으로 훌륭한 문서라고                         생각된다. 기회가 된다면 한글로 번역하고픈 마음이다).&nbsp;마이크로소프트에서 무료로 다운받을 수                         있다.</p>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <h2><st1:place w:st="on"><font size="4">더                         읽을거리</font></st1:place> </h2>                                                <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">                              인텔 x86 CPU 보호 모드에                         대한 다음 두 훌륭한 자료:</p>                        <p class="MsoNormal" style="margin-left: 0.75in; text-indent: -0.25in;"><span style="">1)<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                         </span></span>"Intel Architecture Software Developers                         Manual, Volume 3 - System Programming Guide". Available                         from Intel's web site in PDF format.</p>                        <p class="MsoNormal" style="margin-left: 0.75in; text-indent: -0.25in;"><span style="">2)<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                         </span></span>"Protected Mode Software Architecture" by                         Tom Shanley. Available from Amazon.com (published by                         Addison Wesley).</p>                        <p class="MsoNormal" style="text-indent: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="text-indent: 0.25in;">                          반드시 읽어보야 할 x86 CPU 프로그래밍에 대한                         자료:</p>                        <p class="MsoNormal" style="text-indent: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 66.75pt; text-indent: -30.75pt;"><span style="">1)<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                         </span></span>Intel Architecture Software Developers                         Manual, Volume 1 - Basic Architecture.</p>                        <p class="MsoNormal" style="margin-left: 66.75pt; text-indent: -30.75pt;"><span style="">2)<span style="font-family: 'Times New Roman'; font-style: normal; font-variant: normal; font-weight: normal; font-size: 7pt; line-height: normal; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;                         </span></span>Intel Architecture Software Developers                         Manual, Volume 2 - Instruction Set Reference Manual.</p>                        <p class="MsoNormal"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in; text-indent: -0.25in;"><span style="">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 이들                         책들은 인텔 웹 사이트에서 PDF 포맷으로 얻을 수 있다                         </span>                                      (당신은 또한 볼륨 3을 제외한 두 권의                         책을 하드카피로 무료로 얻을 수 있다. 그러나 볼륨 3은 PDF                         포맷으로만 유효하다). </p>                        <h2><font size="4"> 저자에 대하여</font></h2>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">                                                                                             John Gulbrandsen                         는&nbsp;Summit Soft Consulting 의 설립자이며 회장이다. John 은 임베디드와                         Windows 시스템 개발뿐만 아니라&nbsp;Microprocessor-, digital-, analog- electronics 설계에도 정규                         지식을 가지고 있다. John 은 1992년(Windows 3.0)부터                         Windows를 프로그램해왔다.&nbsp;그는 C++, C#, VB를 이용하여 Windows 어플리게이션과 웹 시스템을 작성하고 또한 SoftIce를                         이용하여 Windows 커널 모드 디바이스 드라이버를 개발하고                         디버깅한다.&nbsp;&nbsp;</p>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">To                         contact John drop him an email: <a href="mailto:John.Gulbrandsen@SummitSoftConsulting.com">John.Gulbrandsen@SummitSoftConsulting.com</a></p>                        <h2><font size="4"> <st1:city w:st="on"><st1:place w:st="on">                          Summit</st1:place>                          </st1:city>Soft Consulting에                         대하여</font></h2>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">                          Summit Soft Consulting 는 사우스                         캘리포니아에 위치한 마이크로소프트 운영체제와 그 핵심                         기술을 전문으로 하는 컨설팅 회사이다. 우리는 커널 모드와                         NT internals 프로그래밍을 포함한 Windows 시스템 개발을                         주로 다른다.</p>                        <p class="MsoNormal" style="margin-left: 0.25in;"><o:p></o:p></p>                        <p class="MsoNormal" style="margin-left: 0.25in;">                            Summit Soft Consulting 방문하기: <a href="http://www.summitsoftconsulting.com/">http://www.summitsoftconsulting.com/</a></p></div><br/><br/>tag : <a href="/tag/SystemCall" rel="tag">SystemCall</a>,&nbsp;<a href="/tag/NativeAPI" rel="tag">NativeAPI</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>SystemCall</category>
		<category>NativeAPI</category>

		<comments>http://octet.egloos.com/1264493#comments</comments>
		<pubDate>Wed, 09 Jan 2008 08:57:22 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ SSDT에 대한 고찰 ]]> </title>
		<link>http://octet.egloos.com/1250585</link>
		<guid>http://octet.egloos.com/1250585</guid>
		<description>
			<![CDATA[ 
  http://nerd.egloos.com/2723343<br />
<br />
며칠전 부터 SSDT에 대해서 유심히 살펴보았고, 변경된 SSDT를 Restore해주는<br />
녀석의 코드를 살펴본 결과를 정리해보고자 한다.<br />
<br />
sunwangme라는 사람이 만든 sdtreset 이라는 녀석은 메모리에 있는 SSDT를 <br />
디스크내에 ntoskrnl.exe 안의 export된 SSDT와 비교해서 바뀐 부분을 복구시키는<br />
간단한 POC이다.<br />
<br />
그런데 한가지 문제는 내가 사용하는 PC에는 현재 KAV(카스퍼스키 백신)이 깔려<br />
있기에 이녀석이 SSDT를 Relocation 한다는 사실이다. <br />
일반적으로 KeServiceDescriptorTable의 Base 주소가 바뀌지 않았을 경우에는 <br />
ntoskrnl.exe에서 적절한 값을 찾아내어 복구 할 수 있었다. 하지만 KAV는 이 Base<br />
주소를 ntoskrnl.exe의 바이너리 외부로 빠져버렸기 때문에 아무리 디스크 이미지<br />
내에서 찾으려 해도 불가능하다는 것이다. 대략 난감. ㅠㅠ;<br />
<br />
하지만 솟아날 구멍은 항상 존재하는 것~ ROOTKIT에 올라온 기사 가운데<br />
<a title="" href="http://somma.egloos.com/2598389"><span style="color: rgb(174, 111, 30);">A more stable way to locate real KiServiceTable</span></a> 이라는 것에서 힌트를 얻게 되었다.<br />
KAV와 같이 KeServiceDescriptorTable을 Relocation하는 녀석들의 경우는 더 Stable<br />
한 방법으로 KiServiceTable의 주소를 알아내는 방법이다.<br />
<span style="font-family: 굴림체;"></span>아래는 <span class="edgeatext">  KeServiceDescriptorTable이 </span><span class="edgeatext">KiInitSystem()에서 초기화 되는 것을 코드로 표현한<br />
것이다.<br />
</span><span class="edgeatext"><code><br />
&nbsp;&nbsp;&nbsp;&nbsp;KeServiceDescriptorTable[0].Base = &amp;KiServiceTable[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;KeServiceDescriptorTable[0].Count = NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;KeServiceDescriptorTable[0].Limit = KiServiceLimit;<br />
&nbsp;&nbsp;&nbsp;&nbsp;KeServiceDescriptorTable[0].Number = &amp;KiArgumentTable[0];<br />
&nbsp;&nbsp;&nbsp;&nbsp;for (Index = 1; Index &lt; NUMBER_SERVICE_TABLES; Index += 1) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;KeServiceDescriptorTable[Index].Limit = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;}<br />
<br />
위와 같은 코드라면 분명 리버싱해보면 다음과 같은 assembly code를 찾을 수 있을 것이다.<br />
<br />
</code></span><span style="font-family: 굴림체;">C7 05 ..8 bytes..&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable</span><br />
<span style="font-family: 굴림체;"></span><br />
위와 같은 인스트럭션은&nbsp; DLL이라면 재배치가 필요하다. 기준 재배치를 위해서 reloc 섹션을 <br />
참고해야만 할 것이다. 그럼 우선 ntoskrnl을 dll로 매핑한 다음에 export 되어 있는 <br />
KeServiceDescriptorTable의 주소를 GetProcAddress()로 알아낸다. 그리고 reloc 섹션을<br />
탐색하여 위의 인스트럭션이 존재하는 RVA값을 알아낸다. 인스트럭션이 시작하는 오프셋에서<br />
6을 더하면 그곳이 KiServiceTable의 주소가 될 것이다. 그러면 실제 복구를 할 때 디스크에서 <br />
이 주소를 기본으로 하여 각각의 서비스 인덱스에 대한 주소값을 가져온 후에 KAV가 바꿔버린 <br />
서비스 디스패처 루틴의 주소를 복구해줄 수 있다는 결론이다.<br/><br/>tag : <a href="/tag/SSDT" rel="tag">SSDT</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>SSDT</category>

		<comments>http://octet.egloos.com/1250585#comments</comments>
		<pubDate>Mon, 07 Jan 2008 01:38:37 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ PDA 중국집 사이트 pxdxa.com ]]> </title>
		<link>http://octet.egloos.com/1236129</link>
		<guid>http://octet.egloos.com/1236129</guid>
		<description>
			<![CDATA[ 
  <p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">PDA 유저라면 꼭 알아야 할 사이트를 소개하고자 합니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">최근 중국의 인터넷 보급 확대로, 중국의 수많은 네티즌들이 여러 방면에서 두각을 나타내고 있는데요, 대표적으로 느끼는 부분이 미국드라마 자막입니다.</span></p><p><br><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">(MissFlash) 미국에서 현지에서 방송후 몇 시간이 되지 않아 중국 네티즌이 작성한 영문자막이 올라오는 것을 보면, 얼마나 대단한지 실감할 수 있습니다. 이렇게 올라온 영문자막을 참고로 한국어 자막도 만들어집니다. ^^; 참고로, 자막을 만드는데는 굉장히 많은 시간과 노력이 들어갑니다. 스크립트를 만들 수 있는 청취능력(Hearing) 뿐만 아니라 자막제작과 함께 싱크도 맞춰야 하니까요...</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">중국 네티즌들의 도움을 받을 수 있는 또 하나는 바로, PDA분야가 아닐까 생각합니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">조금만 관심있는 유저라면 알고 계시리라 생각하는 바로 일명 "중국집"으로 통하는 </span><a class="con_link" href="http://www.pxdxa.com/" target="_blank"><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">http://www.pxdxa.com</span></a><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">사이트 입니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">이 사이트를 통해 PDA 관련 핫 이슈부터, 최신 소프트웨어 정보... 거기다가 일명 "과자" 로 알려진, 크랙도 쉽게 구할수 있습니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">하지만, 사이트내의 대부분의 언어가 중국어로 표기되어 있어, 사용에는 불편한 점이 있습니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">그래서, </span><a class="con_link" href="http://www.pxdxa.com/" target="_blank"><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">http://www.pxdxa.com</span></a><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">의 사용방법을 간단히 설명하도록 하겠습니다. ^^</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">1. 사이트에 접속하신 후, 원활한 사이트 이용을 위해 회원가입을 위해 Register 버튼을 누릅니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">2. 가입약관에 동의하냐? 는 질문 같습니다. Agree 버튼을 누릅니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">3. 정보를 입력합니다. 위쪽에 있는 필수입력사항만 입력하면 되므로, 간편하게 끝낼 수 있습니다.<br>순서대로 아이디(Check username을 통해 중복여부를 확인합니다.)와, 비밀번호, 비밀번호 확인, 숫자코드 입력 및 이메일 주소를 입력하시면 됩니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">4. 가입완료 후, Login 버튼을 누르면 아래의 화면을 보실 수 있습니다. (혹은 자동으로 로그인 될 수도 있습니다. 그럴 경우 바로 5번으로 이동하시면 됩니다.)<br>순서대로 아이디와 비밀번호를 입력하신 후, 아래 쪽의 빨간 박스를 클릭하면 로그인을 완료합니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">5. 달라진 시작화면을 보실 수 있습니다. Search 버튼을 눌러 원하는 프로그램을 찾을 수 있습니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">6. 위 쪽의 빨간 박스에 해당하는 부분에 검색을 원하는 이름을 입력하고, 아래 쪽의 버튼을 클릭하면 검색결과를 확인할 수 있습니다. 첨부파일은 게시물의 아래쪽에 "Attachment："라고 표시되어 있습니다. :)</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림"></span>&nbsp;</p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">* 이제 어둠의 경로를 이용하기 위한 준비를 마쳤습니다.</span></p><p><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">원본: </span><a class="con_link" href="http://kaist.tistory.com/102" target="_blank"><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">http://kaist.tistory.com/102</span></a><br><span style="FONT-SIZE: 100%; FONT-FAMILY: 굴림">출처 : MissFlash.com, http ://www.missflash.com/<br><a href="http://cafe.naver.com/m420.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=89">http://cafe.naver.com/m420.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=89</a></span></p><br/><br/>tag : <a href="/tag/PDA" rel="tag">PDA</a>,&nbsp;<a href="/tag/스마트폰" rel="tag">스마트폰</a>,&nbsp;<a href="/tag/중국집" rel="tag">중국집</a>			 ]]> 
		</description>
		<category>Etcetra</category>
		<category>PDA</category>
		<category>스마트폰</category>
		<category>중국집</category>

		<comments>http://octet.egloos.com/1236129#comments</comments>
		<pubDate>Fri, 04 Jan 2008 04:10:41 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ 64비트 윈도우를 프로그래밍 하기 위해서 알아야 할 모든 것들 ]]> </title>
		<link>http://octet.egloos.com/1229938</link>
		<guid>http://octet.egloos.com/1229938</guid>
		<description>
			<![CDATA[ 
  http://www.microsoft.com/korea/msdn/msdnmag/issues/06/05/x64/default.aspx#S5<br/><br/>tag : <a href="/tag/64비트" rel="tag">64비트</a>			 ]]> 
		</description>
		<category>└Inclusion</category>
		<category>64비트</category>

		<comments>http://octet.egloos.com/1229938#comments</comments>
		<pubDate>Thu, 03 Jan 2008 02:53:33 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ PUSHFD, POPFD 명령 ]]> </title>
		<link>http://octet.egloos.com/1224623</link>
		<guid>http://octet.egloos.com/1224623</guid>
		<description>
			<![CDATA[ 
  PUSHFD 명령은 32비트 EFLAGS 레지스터 값을 스택에 푸시하고 POPFD는 스택에서 EFLAGS로 팝한다.<br />
<br />
PUSHFD<br />
POPFD<br />
<br />
실제 주소 모드 프로그램은 16비트 FLAGS 레지스터를 스택에 푸시하기 위해 PUSHF 명령을 사용하고 POPF는 스택에서 FLAGS로 팝한다.<br />
<br />
플래그 값의 백업을 만들고 후에 다시 복구하는 것이 유용할 때가 많다. 이렇게 하는 한 가지 방법은 PUSHFD와 POPFD로 코드의 어떤 블록을 감싸는 것이다.<br />
pushfd<br />
;<br />
; code<br />
;<br />
popfd<br />
<br />
이 러한 푸시와 팝을 사용할 때 프로그램의 실행 경로가 POPFD를 지나치지 않게 매우 조심해야만 한다. 프로그램 내의 모든 푸시와 팝이 있는 곳을 기억하기 까다로울 수 있다. 보다 오류가 적은 방법은 플래그를 변수에 저장하는 것이다.<br />
.data<br />
saveFlags DWORD     ?<br />
.code<br />
pushfd<br />
pop       saveFlags<br />
<br />
다음 문은 그 변수로부터 플래그를 복원한다.<br />
push      saveFlags<br />
popfd<br />
<br/><br/>tag : <a href="/tag/PUSHFD" rel="tag">PUSHFD</a>,&nbsp;<a href="/tag/POPFD" rel="tag">POPFD</a>,&nbsp;<a href="/tag/ASEMBLY" rel="tag">ASEMBLY</a>,&nbsp;<a href="/tag/어셈블리" rel="tag">어셈블리</a>			 ]]> 
		</description>
		<category>└Language</category>
		<category>PUSHFD</category>
		<category>POPFD</category>
		<category>ASEMBLY</category>
		<category>어셈블리</category>

		<comments>http://octet.egloos.com/1224623#comments</comments>
		<pubDate>Wed, 02 Jan 2008 02:32:10 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ WinDbg 간단 사용법 ]]> </title>
		<link>http://octet.egloos.com/1201648</link>
		<guid>http://octet.egloos.com/1201648</guid>
		<description>
			<![CDATA[ 
  <br />
<h2>웹 심볼(Web Symbol) 얻기</h2><div><p><strong><span style="color: rgb(255, 0, 0);">&nbsp;SRV*D:\Symbol\WebSymbol*http://msdl.microsoft.com/download/symbols</span></strong> 를&nbsp;&nbsp;Symbol Path에 넣으면 웹 심볼을 사용할 수 있다.</p></div><p>&nbsp;</p><h2>로드된 모듈 리스트 보기</h2><p><strong>&nbsp;lm</strong> 명령을 이용하면 된다.</p><div><ul><li>lm&nbsp;k : Kernel Mode&nbsp;모듈 표시</li><li>lm u&nbsp;: User Mode&nbsp;모듈 표시</li><li>lm m : 패턴을 검사하여 해당하는 것만 보여줌 &lt;lm&nbsp;m&nbsp;my*&gt;</li></ul></div><p>&nbsp;</p><br />
<br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="DISPLAY: none"><br />
<br />
<div><p>lkd&gt; <strong>lm</strong><br />
start&nbsp;&nbsp;&nbsp; end&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; module name<br />
00c80000 00c90000&nbsp;&nbsp; NateOnHook40u&nbsp;&nbsp; (export symbols)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C:\Program Files\NATEON\BIN\NateOnHook40u.dll<br />
00cb0000 00cb9000&nbsp;&nbsp; MgHookDll C (export symbols)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C:\Program Files\LG Software\On Screen Display\MgHookDll.dll<br />
01000000 0106a000&nbsp;&nbsp; windbg&nbsp;&nbsp;&nbsp;&nbsp; (pdb symbols)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;D:\Symbol\WebSymbol\windbg.pdb\D6EF677AA54441279479F0307F05A8941\windbg.pdb<br />
016a0000 01784000&nbsp;&nbsp; ext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (export symbols)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; C:\Program Files\Debugging Tools for Windows\winext\ext.dll<br />
01790000 017c1000&nbsp;&nbsp; kext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (pdb symbols)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; D:\Symbol\WebSymbol\kext.pdb\6B643FC4E9F94FF4ABA4CEF1FD6F89D61\kext.pdb</p></div><p>&nbsp;</p><h2>모듈의 심볼(Symbol) 검사</h2><p>&nbsp;<strong>x&nbsp;모듈!패턴&nbsp;</strong>을 입력하면 된다.</p><div><p>lkd&gt; x <strong>nt!Ke*<br />
</strong>804f8c02 nt!KeQuerySystemTime = &lt;no type information&gt;<br />
804f8c9e nt!KeEnableInterrupts = &lt;no type information&gt;<br />
80500e38 nt!KeSwitchKernelStack = &lt;no type information&gt;<br />
804fad32 nt!KeReadStateProcess = &lt;no type information&gt;<br />
804f9188 nt!KeReleaseInterruptSpinLock = &lt;no type information&gt;</p></div><p>&nbsp;</p><h2>데이터 타입(Date Type) 표시</h2><p>&nbsp;<strong>dt 데이터&nbsp;타입</strong>&nbsp;을 입력하면 된다.</p><div><p>lkd&gt; <strong>dt _EPROCESS</strong><br />
&nbsp;&nbsp; +0x000 Pcb&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _KPROCESS<br />
&nbsp;&nbsp; +0x06c ProcessLock&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _EX_PUSH_LOCK<br />
&nbsp;&nbsp; +0x070 CreateTime&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _LARGE_INTEGER<br />
&nbsp;&nbsp; +0x078 ExitTime&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : _LARGE_INTEGER<br />
&nbsp;&nbsp; +0x080 RundownProtect&nbsp;&nbsp; : _EX_RUNDOWN_REF<br />
&nbsp;&nbsp; +0x084 UniqueProcessId&nbsp; : Ptr32 Void<br />
&nbsp;&nbsp; +0x088 ActiveProcessLinks : _LIST_ENTRY<br />
&nbsp;&nbsp; +0x090 QuotaUsage&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : [3] Uint4B<br />
&nbsp;&nbsp; +0x09c QuotaPeak&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : [3] Uint4B<br />
&nbsp;&nbsp; +0x0a8 CommitCharge&nbsp;&nbsp;&nbsp;&nbsp; : Uint4B</p></div><p>&nbsp;</p><p>&nbsp;</p><h2>메모리 덤프(Memory Dump)</h2><p>&nbsp;<strong>d*</strong>&nbsp;명령들을 이용하면 된다.</p><ul><li>db : Byte&nbsp;형식 +&nbsp;Ascii 로 표시</li><li>dd : 데이터를&nbsp;4Byte 형식으로 표시</li></ul><p>&nbsp;</p><div><p>lkd&gt; <strong>db 8053db18</strong><br />
8053db18&nbsp; 8b ff 55 8b ec 8b 45 08-8b 4d 0c 8b 55 14 89 48&nbsp; ..U...E..M..U..H<br />
8053db28&nbsp; 0c 8b 4d 10 89 48 10 03-ca 89 48 14 8b 4d 18 83&nbsp; ..M..H....H..M..<br />
8053db38&nbsp; c1 fe 89 48 18 8b 4d 1c-89 48 20 66 8b 4d 20 66&nbsp; ...H..M..H f.M f</p></div><p>&nbsp;</p><p>&nbsp;</p><h2>디스어셈블리(Disassembly)</h2><p><strong>&nbsp;u&nbsp;주소</strong> 를 이용하면&nbsp;된다. 특정 함수를 디스어셈블리 하고 싶으면 <strong>uf 주소</strong> 를 하면 된다.</p><div><ul><li>u 주소&nbsp;:&nbsp;주소에서 일부분만 디스어셈블리</li><li>u 주소1 주소2&nbsp;: 주소1에서 주소 2까지&nbsp;디스어셈블리</li></ul></div><p>&nbsp;</p><div><p>lkd&gt; <strong>u 8053db18 or uf nt!NtOpenProcess<br />
</strong>nt!KeInitializeProfile:<br />
8053db18 8bff&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; edi,edi<br />
8053db1a 55&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; push&nbsp;&nbsp;&nbsp; ebp<br />
8053db1b 8bec&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ebp,esp<br />
8053db1d 8b4508&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; eax,[ebp+0x8]<br />
8053db20 8b4d0c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov&nbsp;&nbsp;&nbsp;&nbsp; ecx,[ebp+0xc]</p></div><p>&nbsp;</p><h2>메모리 영역 속성 보기(VA&nbsp;Dump)</h2><p><strong>&nbsp;!vadump</strong> 명령을 사용하면 된다. 만약 특정 메모리의 속성을 보고 싶다면 <strong>!vprot 주소</strong> 명령을 사용하면 된다.</p><div><p>0:000&gt;&nbsp;!vadump<br />
BaseAddress:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00000000<br />
RegionSize:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00010000<br />
State:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00010000&nbsp;&nbsp;MEM_FREE<br />
Protect:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00000001&nbsp;&nbsp;PAGE_NOACCESS<br />
<br />
BaseAddress:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00010000<br />
RegionSize:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00001000<br />
State:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00001000&nbsp;&nbsp;MEM_COMMIT<br />
Protect:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00000004&nbsp;&nbsp;PAGE_READWRITE<br />
Type:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;00020000&nbsp;&nbsp;MEM_PRIVATE</p></div><p>&nbsp;</p><div><p>0:000&gt;&nbsp;!vprot&nbsp;30c191c<br />
BaseAddress:&nbsp;030c1000<br />
AllocationBase:&nbsp;030c0000<br />
AllocationProtect:&nbsp;00000080&nbsp;PAGE_EXECUTE_WRITECOPY<br />
RegionSize:&nbsp;00011000<br />
State:&nbsp;00001000&nbsp;MEM_COMMIT<br />
Protect:&nbsp;00000010&nbsp;PAGE_EXECUTE<br />
Type:&nbsp;01000000&nbsp;MEM_IMAGE</p></div><p>&nbsp;</p><h2>프로세스 관련</h2><p>&nbsp;모든 프로세스를 보기위해서는 <strong>!process 0 0</strong> 를 입력하면 된다. 디버거를 특정 프로세스에 붙이고 싶으면 <strong>.process /i [pid]&nbsp;</strong>를 입력하면 된다.</p><p>&nbsp;</p><p>lkd&gt; <strong>!process 0 0<br />
</strong>**** NT ACTIVE PROCESS DUMP ****<br />
PROCESS 8a3a3490&nbsp; SessionId: none&nbsp; Cid: 0004&nbsp;&nbsp;&nbsp; Peb: 00000000&nbsp; ParentCid: 0000<br />
&nbsp;&nbsp;&nbsp; DirBase: 00780000&nbsp; ObjectTable: e1001c70&nbsp; HandleCount: 521.<br />
&nbsp;&nbsp;&nbsp; Image: System</p><p>PROCESS 8a184158&nbsp; SessionId: none&nbsp; Cid: 03f0&nbsp;&nbsp;&nbsp; Peb: 7ffdd000&nbsp; ParentCid: 0004<br />
&nbsp;&nbsp;&nbsp; DirBase: 17a40020&nbsp; ObjectTable: e163dd70&nbsp; HandleCount:&nbsp; 20.<br />
&nbsp;&nbsp;&nbsp; Image: smss.exe</p>PROCESS 89df4da0&nbsp; SessionId: 0&nbsp; Cid: 0440&nbsp;&nbsp;&nbsp; Peb: 7ffd5000&nbsp; ParentCid: 03f0<br />
&nbsp;&nbsp;&nbsp; DirBase: 17a40040&nbsp; ObjectTable: e1c6cb18&nbsp; HandleCount: 626.<br />
&nbsp;&nbsp;&nbsp; Image: csrss.exe<br />
<br />
<div><ul><li><strong>이 글은&nbsp;kkamagui에 의해 작성된 글입니다.</strong></li><li><strong>마음껏 인용하시거나 사용하셔도 됩니다. 단 출처(<a href="http://kkamagui.tistory.com/"><span style="color: rgb(128, 0, 128);">http://kkamagui.tistory.com</span></a>, <a href="http://kkamagui.springnote.com/pages/404250#"><span style="color: rgb(128, 0, 128);">http://kkamagui.springnote.com</span></a>)는 밝혀 주십시오.</strong></li><li><strong>기타 사항은&nbsp;kkakkunghehe at daum.net 이나</strong> <a href="http://kkamagui.tistory.com/"><strong><span style="color: rgb(128, 0, 128);">http://kkamagui.tistory.com</span></strong></a><strong>으로 보내주시면 반영하겠습니다.</strong></li></ul></div></div><br />
<br/><br/>tag : <a href="/tag/WinDbg" rel="tag">WinDbg</a>,&nbsp;<a href="/tag/디버거" rel="tag">디버거</a>			 ]]> 
		</description>
		<category>Etcetra</category>
		<category>WinDbg</category>
		<category>디버거</category>

		<comments>http://octet.egloos.com/1201648#comments</comments>
		<pubDate>Fri, 28 Dec 2007 06:30:33 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ 루트킷 탐지 및 제거 모듈 (Anti RootKit Module) ]]> </title>
		<link>http://octet.egloos.com/1192610</link>
		<guid>http://octet.egloos.com/1192610</guid>
		<description>
			<![CDATA[ 
  <table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td class="td_bodyt"><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="head_title">루트킷 탐지 및 제거 모듈 (Anti RootKit Module)<br />
</td>                  	</tr>                  </tbody></table>                    </td>                  </tr>                  <tr>                    <td class="gray8" align="right">                    <br />
</td>                  </tr>                  <tr>                    <td class="td_body"> <span style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="background-color: rgb(250, 255, 169);"><font size="3"><span style="font-weight: bold; background-color: rgb(201, 237, 255);">세인트 시큐리티의 5번째 솔루션 - Anti-RootKit Module</span></font><br />
<br />
<br />
루트킷 탐지 및 제거 모듈 (루트킷 무력화 모듈)</span><br />
<br />
<font style="font-weight: bold; text-decoration: underline;" size="2">1. 루트킷이란?</font></span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">- 루트킷 소개</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">루트킷이란 원래 Linux 에서 별다른 설정이나 공격 없이 바로 Root 권한의 계정을 획득하기 위한 악성 프로그램으로만들어지다가 점차 발전한 형태의 가장 지능적인 해킹 프로그램이라고 할 수 있겠다. 현대에 들어서는 Linux 운영체제뿐만 아니라Windows 운영체제에서도 사용자에게 피해를 주기 위해서 여러 형태로 발전 및 개발 되고 있다. </span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">루트킷은 가장 기본적인 공격 코드를 포함함과 동시에 루트킷 프로그램 자신을 보호하기 위해서 여러 가지 고급 기법들이 동원된다. 그중에 대표적인 예로 작동되는 프로세스를 숨기거나 자신의 파일을 노출 되지 않도록 한다거나 특정 레지스터 값을 숨기는 등의 작동을하여 자신을 보호 한다. 따라서 탐지를 한다거나 탐지를 하더라도 치료를 하기가 더욱 까다로워 진다.</span><br />
<br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="DISPLAY: none"><br />
<br />
<br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">- 루트킷 기법을 이용한 악성코드</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">최근에는 위와 같은 루트킷 기법들이 대중화 되고 일반화 되면서 각종 스파이웨어 프로그램이나 악성 코드 프로그램과의 혼합을 통해더욱 사용자들을 힘들게 하고 있다. 그냥 작동하고 그냥 피해만 주던 악성 코드 프로그램들이 이제는 고급 기술인 루트킷 기술을흡수하여 악성코드 자신이 작동하는 프로그램 메커니즘을 보호하고 자신이 관장하는 특정 레지스터 값을 사용자가 변경하지 못하게 막는등의 행위를 하게 되면서 사용자는 최악의 상황에는 치료도 하지 못하고 그냥 시스템을 재 설치 해야 하는 상황에 놓이게 되었다.더욱이 각종 마케팅 목적으로 쓰이는 프로그램 중에서도 일부가 해당 회사의 마케팅 효과를 위해서 사용자는 프로그램을 끄지도지우지도 못하게 만들어 놓아 불편을 가중 시키는 경우도 발생하게 되는 것이다.</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><div style="padding: 10px; background-color: rgb(250, 255, 169); font-family: tahoma,arial,helvetica,sans-serif;">이러한 상황에서 루트킷은 그 기술이 공개된 포럼을 통해서 개발자들 사이에 공공연히 공유가 되고 있는 실정이고 조금만 실력이 있는개발자라면 쉽게 그 내용을 프로그램으로 옮길 수 있게 되는 것이다. 또한 그 루트킷 기술 자체는 장, 단점을 가지고 있어서악용하는 경우도 있는가 반면에 다른 특정 분야에서 잘 이용되는 경우도 있어서 이를 구분 하여 탐지하는 것은 더욱 어려워 지는것이 현실이다. 시중에 나와 있는 백신이나 악성코드 기술을 능가하는 기술을 가진 루트킷을 어떻게 탐지하고 제거 할 것인가?</div><br />
<br />
<br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><font style="font-weight: bold; text-decoration: underline; font-family: tahoma,arial,helvetica,sans-serif;" size="2">2. 기반 기술 분석</font><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">- 루트킷 작동 기법</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">루트킷의 작동 기법은 아주 다양하지만 가장 기반이 되는 기술은 Hooking 기술이다. Hooking 기술이란 특정 함수나 특정명령어가 실행 됨에 있어서 그 중간에 해당 명령 내용을 가로채서 특정 작업을 한 이후에 원래 실행 함수로 넘겨주게 되어 처리를하도록 만드는 기술이다. (루트킷의 기반이 되는 이 기술은 여러 영역에 걸쳐서 작동이 가능하며 모든 운영체제, 모든 환경에서가능하도록 되어 있어서 그 변형 기술이 나올 가능성도 아주 높다.)</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">이러한 Hooking 기술은 User Mode, Kernel Mode로 크게 나누어 볼 수 있다. User Mode 에서는 APIHooking, Message Hooking 기법 등을 이용하고 있으며 Kernel Mode 에서는 SSDT Hooking,IDT Hooking, IRP Hooking 기법 등을 이용한다. (각 기법에 대한 상세한 설명은 본 문서의 목적이 아니므로생략할 수 있도록 하겠다.)</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">과거의 루트킷과 같은 경우는 이러한 Hooking 된 모듈을 찾아서 Hooking 코드만을 복구 해주면 자연스럽게 루트킷을 탐지및 제거가 가능하게 되었지만 현대에 들어서는 루트킷이 사용하는 Hooking 코드가 복구 되면 시스템을 망가지게 해서 탐지를못하게 하는 경우도 있어 기존의 방법으로는 복구하기가 힘들어 진 것이 사실이다. 따라서 세인트 시큐리티는 아래와 같은 기법을개발하였다.</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><font style="font-weight: bold; text-decoration: underline; font-family: tahoma,arial,helvetica,sans-serif;" size="2">3. 루트킷 탐지 및 제거 기법 (루트킷 무력화 기법)</font><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">- 탐지 기법</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">루트킷은 일단 시스템에 존재 여부를 탐지를 하는 것이 가장 중요하다. 탐지를 위해서는 기존에 Hooking 되어 있는 코드를사용하지 않고 직접 해당 함수를 구현을 해서 탐지를 하는 기법이 있을 수 있겠고 Hooking 된 코드를 복원하여 원래 함수를호출하는 방법이 있을 수 있다. Hooking 된 코드를 복원하는 방법에는 아래에 언급된 각종 Hooking 기법 탐지에 많이사용된다.</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">A. &nbsp; &nbsp;Import Address Table (IAT)</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">B. &nbsp; &nbsp;System Service Dispatch Table (SSDT) - KeServiceDescriptorTable</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">C. &nbsp; &nbsp;Interrupt Descriptor Table (IDT) - Per CPU</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">D. &nbsp; &nbsp;Drivers’ I/O Request Packet (IRP) handler </span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">하지만 위의 Hooking 기법들을 바로 복원을 해서 루트킷을 탐지하는 방법에는 여러 위험성이 따르게 된다. 시스템의 상황에 따라범용으로 제작하기가 힘들어지고 실제 제작을 한다고 하더라도 모든 운영체제에 쉽게 적용을 시키기가 쉽지 않다는 것이다. </span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">따라서 세인트 시큐리티는 새로운 방법을 제안한다. 제안 하는 방법은 바로 가상화 방법이다. <span style="text-decoration: underline; color: rgb(255, 0, 0); font-weight: bold;">시스템 커널 드라이버를 하나 만들고 그 드라이버에서 Windows System 에서 사용하는 실제 함수들을 가상 메모리에Mapping 을 하고 시스템 Entry 에 연결을 시켜주어 실제 Hooking 함수들을 작동하지 않도록 하는 것</span>이다. 따라서 각종 Hooking 기법을 통해서 프로세스를 숨기거나 파일을 숨기거나 레지스터 값을 숨기거나 기타 내용을 차단을한다고 하더라도 시스템에 원래 사용하게 되는 함수나 명령어를 사용하게 되고 전혀 Hooking 되지 않은 원래 함수를 호출하여올바른 결과를 가지고 올 수 있게 되는 것이다.</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">실제 테스트 해본 결과 CPU 타입이나 환경에 크게 영향을 받지 않고 사용하는 운영체제 별로 Mapping 되는 함수에 대해서만미리 정의를 해주게 되면 반 영구적으로 사용할 수 있는 기법이 되는 것이다. 또한 새로운 Hooking 기법이 나온다고 하더라도해당 Hooking 에 사용하는 함수나 종류만 판별하게 되면 가상 공간에 해당 내용을 Mapping 해주고 SystemEntry 만 연결해주면 되는 방법이니 신종 기법에 대한 대응도 상당히 빠르게 진행 할 수 있다.</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">- 제거 기법</span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">위의 방법을 통해 루트킷 탐지가 가능해지면 제거 또한 쉽게 가능해진다. 일단 해당 루트킷 프로그램이 작동을 하고 있으면 프로세스작동을 중단시킨다. 프로세스를 중단을 시키는 것은 위의 탐지 과정을 통해서 우리가 사용하는 모듈은 Hooking 되지 않은 실제함수를 호출을 해주는 것이기 때문에 프로그램 작동 중단 방지 등의 Hooking 코드로 막아놓은 것을 우회하여 프로그램을 중단시킬 수 있다. 그와 동시에 해당 프로세스가 작동하면서 보호 되고 있었던 루트킷 파일을 찾아내고 지울 수 있도록 원래 함수를호출하여 주면 쉽게 제거가 가능해진다. </span><br style="font-family: tahoma,arial,helvetica,sans-serif;"><br style="font-family: tahoma,arial,helvetica,sans-serif;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">혹 루트킷이 다른 시스템 모듈이나 파일에 자신의 프로세스를 걸고 있어도 프로세스가 작동이 중단이 되면 걸어 놓은 링크들이 다 풀리기 때문에 마찬가지로 제거가 가능해진다.<br />
<br />
<br />
<div class="imageblock center" style="text-align: center; clear: both;"><br />
</div><br />
<div class="imageblock center" style="text-align: center; clear: both;"><div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds6.egloos.com/pds/200712/26/31/e0081331_477214a765c30.png" width="500" height="279.411764706" onclick="Control.Modal.openDialog(this, event, 'http://pds6.egloos.com/pds/200712/26/31/e0081331_477214a765c30.png');" /></div><br />
<div style="text-align:center"><img class="image_mid" border="0" onmouseover="this.style.cursor='pointer'" alt="" src="http://pds7.egloos.com/pds/200712/26/31/e0081331_477214ae7aa28.png" width="500" height="290.196078431" onclick="Control.Modal.openDialog(this, event, 'http://pds7.egloos.com/pds/200712/26/31/e0081331_477214ae7aa28.png');" /></div>http://www.stsc.co.kr/<br />
</div></span></td></tr></tbody></table></div><br/><br/>tag : <a href="/tag/루트킷" rel="tag">루트킷</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>루트킷</category>

		<comments>http://octet.egloos.com/1192610#comments</comments>
		<pubDate>Wed, 26 Dec 2007 08:47:39 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
	<item>
		<title><![CDATA[ 프로그램 내에서 로그온한 유저 확인하기 ]]> </title>
		<link>http://octet.egloos.com/1163423</link>
		<guid>http://octet.egloos.com/1163423</guid>
		<description>
			<![CDATA[ 
  <div id="post-view" class="post-view pcol2"><div style="font-size: 9pt; font-family: 돋움;" class="view"><strong><span style="color: rgb(255, 0, 0);">## 이하 내용 및 첨부파일(이미지 포함)은 모두 스크랩된 것임을 밝힙니다 ##<br />
<br />
</span></strong><font color="#3a32c3">프로그램을 개발하다 보면 username과 userpassword를 확인해야 하는 경우가 있습니다</font>. 이러한 경우에 <font color="#ff0000"><strong>LogonUser</strong> 라는 API를 사용하면 됩니다. 하지만 이 API 사용할 때는 윈도우 NT4와 윈도우 2000에서는 시스템 권한으로 호출을 해야 합니다</font>. (자세한 내용은 MSDN 참조) 이 방법을 우회하기 위해서 서비스 에서 <strong>LogonUser</strong>&nbsp; API를 실행하도록 프로그램을 개발합니다.&nbsp;그런데<font color="#0075c8"> </font><font color="#3a32c3">windows xp, windows2003, windows vista에서는 시스템 권한 없이 호출해도 됩니다</font>.&nbsp;이와 같이 운영체제 마다 API사용 권한이 다르기 때문에 프로그램 개발이 복잡도가 올라갑니다. 그래서 아래와 같은 새로운 방법을 사용할것을 권장합니다.<br />
<br />
<br />
<a class="more" onfocus="blur()" onclick="this.innerHTML=(this.nextSibling.style.display=='none')?'[닫기]': '[열기]';this.nextSibling.style.display=(this.nextSibling.style.display== 'none')?'block':'none';" href="javascript:void(0);">[열기]</a><div style="display: none;"><br />
<br />
<br />
<p>&nbsp;</p><p>&nbsp;새로운 방법은 Security Support Provider Interface (SSPI)를 이용하는 것입니다. 이 방법은 설명 보다는 아래 코드를 그대로 사용하면 됩니다.</p><p>&nbsp;</p><p>///////////////////////////////////////////////////////////////////////////////<br />
//<br />
//&nbsp; SSPI Authentication Sample<br />
//<br />
//&nbsp; This program demonstrates how to use SSPI to authenticate user credentials.<br />
//<br />
//&nbsp; THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF<br />
//&nbsp; ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED<br />
//&nbsp; TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A<br />
//&nbsp; PARTICULAR PURPOSE.<br />
//<br />
//&nbsp; Copyright (C) 2007.&nbsp; Microsoft Corporation.&nbsp; All rights reserved.<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
#define SECURITY_WIN32<br />
#include &lt;windows.h&gt;<br />
#include &lt;tchar.h&gt;<br />
#include &lt;stdio.h&gt;<br />
#include &lt;conio.h&gt;<br />
#include &lt;sspi.h&gt;<br />
#include &lt;lm.h&gt;<br />
#include &lt;lmcons.h&gt;<br />
<br />
// Older versions of WinError.h do not have SEC_I_COMPLETE_NEEDED #define.<br />
// So, in such an SDK environment setup, we will include issperr.h which has the<br />
// definition for SEC_I_COMPLETE_NEEDED. Include issperr.h only if<br />
// SEC_I_COMPLETE_NEEDED is not defined.<br />
#ifndef SEC_I_COMPLETE_NEEDED<br />
#include &lt;issperr.h&gt;<br />
#endif<br />
<br />
typedef struct _AUTH_SEQ {<br />
&nbsp;&nbsp; BOOL fInitialized;<br />
&nbsp;&nbsp; BOOL fHaveCredHandle;<br />
&nbsp;&nbsp; BOOL fHaveCtxtHandle;<br />
&nbsp;&nbsp; CredHandle hcred;<br />
&nbsp;&nbsp; struct _SecHandle hctxt;<br />
} AUTH_SEQ, *PAUTH_SEQ;<br />
<br />
<br />
// Function pointers<br />
ACCEPT_SECURITY_CONTEXT_FN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _AcceptSecurityContext&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
ACQUIRE_CREDENTIALS_HANDLE_FN&nbsp;&nbsp;&nbsp; _AcquireCredentialsHandle&nbsp; = NULL;<br />
COMPLETE_AUTH_TOKEN_FN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _CompleteAuthToken&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
DELETE_SECURITY_CONTEXT_FN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _DeleteSecurityContext&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
FREE_CONTEXT_BUFFER_FN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeContextBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
FREE_CREDENTIALS_HANDLE_FN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeCredentialsHandle&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
INITIALIZE_SECURITY_CONTEXT_FN&nbsp;&nbsp; _InitializeSecurityContext = NULL;<br />
QUERY_SECURITY_PACKAGE_INFO_FN&nbsp;&nbsp; _QuerySecurityPackageInfo&nbsp; = NULL;<br />
QUERY_SECURITY_CONTEXT_TOKEN_FN&nbsp; _QuerySecurityContextToken = NULL;<br />
<br />
<br />
#define CheckAndLocalFree(ptr) \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ptr != NULL) \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; { \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LocalFree(ptr); \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ptr = NULL; \<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
#pragma comment(lib, "netapi32.lib")<br />
<br />
LPVOID RetrieveTokenInformationClass(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE hToken,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TOKEN_INFORMATION_CLASS InfoClass,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LPDWORD lpdwSize)<br />
{<br />
&nbsp;&nbsp; LPVOID pInfo = NULL;<br />
&nbsp;&nbsp; BOOL fSuccess = FALSE;<br />
<br />
&nbsp;&nbsp; __try<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *lpdwSize = 0;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Determine the size of the buffer needed<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetTokenInformation(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hToken,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InfoClass,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NULL,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *lpdwSize, lpdwSize);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _tprintf(_T("GetTokenInformation failed with %d\n"), GetLastError());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Allocate a buffer for getting token information<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pInfo = LocalAlloc(LPTR, *lpdwSize);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (pInfo == NULL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _tprintf(_T("LocalAlloc failed with %d\n"), GetLastError());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!GetTokenInformation(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hToken,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InfoClass,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pInfo,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *lpdwSize, lpdwSize))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _tprintf(_T("GetTokenInformation failed with %d\n"), GetLastError());<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fSuccess = TRUE;<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; __finally<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Free pDomainAndUserName only if failed<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Otherwise, the caller has to free after use<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (fSuccess == FALSE)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CheckAndLocalFree(pInfo);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; return pInfo;<br />
}<br />
<br />
PSID GetUserSidFromWellKnownRid(DWORD Rid)<br />
{<br />
&nbsp;&nbsp;&nbsp; PUSER_MODALS_INFO_2 umi2;<br />
&nbsp;&nbsp;&nbsp; NET_API_STATUS nas;<br />
<br />
&nbsp;&nbsp;&nbsp; UCHAR SubAuthorityCount;<br />
&nbsp;&nbsp;&nbsp; PSID pSid = NULL;<br />
<br />
&nbsp;&nbsp;&nbsp; BOOL bSuccess = FALSE; // assume failure<br />
<br />
&nbsp;&nbsp;&nbsp; nas = NetUserModalsGet(NULL, 2, (LPBYTE *)&amp;umi2);<br />
<br />
&nbsp;&nbsp;&nbsp; if (nas != NERR_Success)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("NetUserModalsGet failed with error code : [%d]\n", nas);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SetLastError(nas);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; SubAuthorityCount = *GetSidSubAuthorityCount<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (umi2-&gt;usrmod2_domain_id);<br />
<br />
&nbsp;&nbsp;&nbsp; // <br />
&nbsp;&nbsp;&nbsp; // Allocate storage for new Sid. account domain Sid + account Rid<br />
&nbsp;&nbsp;&nbsp; // <br />
<br />
&nbsp;&nbsp;&nbsp; pSid = (PSID)LocalAlloc(LPTR,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetSidLengthRequired((UCHAR)(SubAuthorityCount + 1)));<br />
<br />
&nbsp;&nbsp;&nbsp; if (pSid != NULL)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (InitializeSid(<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pSid,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetSidIdentifierAuthority(umi2-&gt;usrmod2_domain_id),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; (BYTE)(SubAuthorityCount+1)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD SubAuthIndex = 0;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Copy existing subauthorities from account domain Sid into<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // new Sid<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (; SubAuthIndex &lt; SubAuthorityCount ; SubAuthIndex++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *GetSidSubAuthority(pSid, SubAuthIndex) =<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *GetSidSubAuthority(umi2-&gt;usrmod2_domain_id,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SubAuthIndex);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Append Rid to new Sid<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *GetSidSubAuthority(pSid, SubAuthorityCount) = Rid;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; NetApiBufferFree(umi2);<br />
<br />
&nbsp;&nbsp;&nbsp; return pSid;<br />
}<br />
<br />
BOOL IsGuest(HANDLE hToken)<br />
{<br />
&nbsp;&nbsp;&nbsp; BOOL fGuest = FALSE;<br />
&nbsp;&nbsp;&nbsp; PSID pGuestSid = NULL;<br />
&nbsp;&nbsp;&nbsp; PSID pUserSid = NULL;<br />
&nbsp;&nbsp;&nbsp; TOKEN_USER *pUserInfo = NULL;<br />
&nbsp;&nbsp;&nbsp; DWORD dwSize = 0;<br />
<br />
&nbsp;&nbsp;&nbsp; pGuestSid = GetUserSidFromWellKnownRid(DOMAIN_USER_RID_GUEST);<br />
&nbsp;&nbsp;&nbsp; if (pGuestSid == NULL)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fGuest;<br />
<br />
&nbsp;&nbsp;&nbsp; //<br />
&nbsp;&nbsp;&nbsp; // Get user information<br />
&nbsp;&nbsp;&nbsp; //<br />
<br />
&nbsp;&nbsp;&nbsp; pUserInfo = (TOKEN_USER *)RetrieveTokenInformationClass(hToken, TokenUser, &amp;dwSize);<br />
&nbsp;&nbsp;&nbsp; if (pUserInfo != NULL)<br />
&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (EqualSid(pGuestSid, pUserInfo-&gt;User.Sid))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fGuest = TRUE;<br />
&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp; CheckAndLocalFree(pUserInfo);<br />
&nbsp;&nbsp;&nbsp; CheckAndLocalFree(pGuestSid);<br />
<br />
&nbsp;&nbsp;&nbsp; return fGuest;<br />
}<br />
<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
void UnloadSecurityDll(HMODULE hModule) {<br />
<br />
&nbsp;&nbsp; if (hModule)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; FreeLibrary(hModule);<br />
<br />
&nbsp;&nbsp; _AcceptSecurityContext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _AcquireCredentialsHandle&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _CompleteAuthToken&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _DeleteSecurityContext&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _FreeContextBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _FreeCredentialsHandle&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _InitializeSecurityContext&nbsp; = NULL;<br />
&nbsp;&nbsp; _QuerySecurityPackageInfo&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; _QuerySecurityContextToken&nbsp; = NULL;<br />
}<br />
<br />
<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
HMODULE LoadSecurityDll() {<br />
<br />
&nbsp;&nbsp; HMODULE hModule;<br />
&nbsp;&nbsp; BOOL&nbsp;&nbsp;&nbsp; fAllFunctionsLoaded = FALSE;<br />
&nbsp;&nbsp; TCHAR&nbsp;&nbsp; lpszDLL[MAX_PATH];<br />
&nbsp;&nbsp; OSVERSIONINFO VerInfo;<br />
<br />
&nbsp;&nbsp; //<br />
&nbsp;&nbsp; //&nbsp; Find out which security DLL to use, depending on<br />
&nbsp;&nbsp; //&nbsp; whether we are on Windows NT or Windows 95, Windows 2000, Windows XP, or Windows Server 2003<br />
&nbsp;&nbsp; //&nbsp; We have to use security.dll on Windows NT 4.0.<br />
&nbsp;&nbsp; //&nbsp; All other operating systems, we have to use Secur32.dll<br />
&nbsp;&nbsp; //<br />
&nbsp;&nbsp; VerInfo.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);<br />
&nbsp;&nbsp; if (!GetVersionEx (&amp;VerInfo))&nbsp;&nbsp; // If this fails, something has gone wrong<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; if (VerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VerInfo.dwMajorVersion == 4 &amp;&amp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; VerInfo.dwMinorVersion == 0)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lstrcpy (lpszDLL, _T("security.dll"));<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; else<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lstrcpy (lpszDLL, _T("secur32.dll"));<br />
&nbsp;&nbsp; }<br />
<br />
<br />
&nbsp;&nbsp; hModule = LoadLibrary(lpszDLL);<br />
&nbsp;&nbsp; if (!hModule)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return NULL;<br />
<br />
&nbsp;&nbsp; __try {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _AcceptSecurityContext = (ACCEPT_SECURITY_CONTEXT_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "AcceptSecurityContext");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_AcceptSecurityContext)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
#ifdef UNICODE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "AcquireCredentialsHandleW");<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _AcquireCredentialsHandle = (ACQUIRE_CREDENTIALS_HANDLE_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "AcquireCredentialsHandleA");<br />
#endif<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_AcquireCredentialsHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // CompleteAuthToken is not present on Windows 9x Secur32.dll<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Do not check for the availablity of the function if it is NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _CompleteAuthToken = (COMPLETE_AUTH_TOKEN_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "CompleteAuthToken");<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _DeleteSecurityContext = (DELETE_SECURITY_CONTEXT_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "DeleteSecurityContext");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_DeleteSecurityContext)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeContextBuffer = (FREE_CONTEXT_BUFFER_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "FreeContextBuffer");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_FreeContextBuffer)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeCredentialsHandle = (FREE_CREDENTIALS_HANDLE_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "FreeCredentialsHandle");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_FreeCredentialsHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
#ifdef UNICODE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "InitializeSecurityContextW");<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _InitializeSecurityContext = (INITIALIZE_SECURITY_CONTEXT_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "InitializeSecurityContextA");<br />
#endif<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_InitializeSecurityContext)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
#ifdef UNICODE<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "QuerySecurityPackageInfoW");<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _QuerySecurityPackageInfo = (QUERY_SECURITY_PACKAGE_INFO_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "QuerySecurityPackageInfoA");<br />
#endif<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_QuerySecurityPackageInfo)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GetProcAddress(hModule, "QuerySecurityContextToken");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!_QuerySecurityContextToken)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAllFunctionsLoaded = TRUE;<br />
<br />
&nbsp;&nbsp; } __finally {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!fAllFunctionsLoaded) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UnloadSecurityDll(hModule);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hModule = NULL;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; return hModule;<br />
}<br />
<br />
<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
BOOL GenClientContext(PAUTH_SEQ pAS, PSEC_WINNT_AUTH_IDENTITY pAuthIdentity,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PVOID pIn, DWORD cbIn, PVOID pOut, PDWORD pcbOut, PBOOL pfDone) {<br />
<br />
/*++<br />
<br />
 Routine Description:<br />
<br />
&nbsp;&nbsp; Optionally takes an input buffer coming from the server and returns<br />
&nbsp;&nbsp; a buffer of information to send back to the server.&nbsp; Also returns<br />
&nbsp;&nbsp; an indication of whether or not the context is complete.<br />
<br />
 Return Value:<br />
<br />
&nbsp;&nbsp; Returns TRUE if successful; otherwise FALSE.<br />
<br />
--*/<br />
<br />
&nbsp;&nbsp; SECURITY_STATUS ss;<br />
&nbsp;&nbsp; TimeStamp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tsExpiry;<br />
&nbsp;&nbsp; SecBufferDesc&nbsp;&nbsp; sbdOut;<br />
&nbsp;&nbsp; SecBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbOut;<br />
&nbsp;&nbsp; SecBufferDesc&nbsp;&nbsp; sbdIn;<br />
&nbsp;&nbsp; SecBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbIn;<br />
&nbsp;&nbsp; ULONG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fContextAttr;<br />
<br />
&nbsp;&nbsp; if (!pAS-&gt;fInitialized) {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ss = _AcquireCredentialsHandle(NULL, _T("NTLM"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SECPKG_CRED_OUTBOUND, NULL, pAuthIdentity, NULL, NULL,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;pAS-&gt;hcred, &amp;tsExpiry);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ss &lt; 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fHaveCredHandle = TRUE;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; // Prepare output buffer<br />
&nbsp;&nbsp; sbdOut.ulVersion = 0;<br />
&nbsp;&nbsp; sbdOut.cBuffers = 1;<br />
&nbsp;&nbsp; sbdOut.pBuffers = &amp;sbOut;<br />
<br />
&nbsp;&nbsp; sbOut.cbBuffer = *pcbOut;<br />
&nbsp;&nbsp; sbOut.BufferType = SECBUFFER_TOKEN;<br />
&nbsp;&nbsp; sbOut.pvBuffer = pOut;<br />
<br />
&nbsp;&nbsp; // Prepare input buffer<br />
&nbsp;&nbsp; if (pAS-&gt;fInitialized)&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbdIn.ulVersion = 0;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbdIn.cBuffers = 1;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbdIn.pBuffers = &amp;sbIn;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbIn.cbBuffer = cbIn;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbIn.BufferType = SECBUFFER_TOKEN;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbIn.pvBuffer = pIn;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; ss = _InitializeSecurityContext(&amp;pAS-&gt;hcred,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fInitialized ? &amp;pAS-&gt;hctxt : NULL, NULL, 0, 0,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SECURITY_NATIVE_DREP, pAS-&gt;fInitialized ? &amp;sbdIn : NULL,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0, &amp;pAS-&gt;hctxt, &amp;sbdOut, &amp;fContextAttr, &amp;tsExpiry);<br />
&nbsp;&nbsp; if (ss &lt; 0)&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // &lt;winerror.h&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "InitializeSecurityContext failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; pAS-&gt;fHaveCtxtHandle = TRUE;<br />
<br />
&nbsp;&nbsp; // If necessary, complete token<br />
&nbsp;&nbsp; if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_CompleteAuthToken) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ss = _CompleteAuthToken(&amp;pAS-&gt;hctxt, &amp;sbdOut);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ss &lt; 0)&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "CompleteAuthToken failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf (stderr, "CompleteAuthToken not supported.\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; *pcbOut = sbOut.cbBuffer;<br />
<br />
&nbsp;&nbsp; if (!pAS-&gt;fInitialized)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fInitialized = TRUE;<br />
<br />
&nbsp;&nbsp; *pfDone = !(ss == SEC_I_CONTINUE_NEEDED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || ss == SEC_I_COMPLETE_AND_CONTINUE );<br />
<br />
&nbsp;&nbsp; return TRUE;<br />
}<br />
<br />
<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
BOOL GenServerContext(PAUTH_SEQ pAS, PVOID pIn, DWORD cbIn, PVOID pOut,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PDWORD pcbOut, PBOOL pfDone) {<br />
<br />
/*++<br />
<br />
 Routine Description:<br />
<br />
&nbsp;&nbsp;&nbsp; Takes an input buffer coming from the client and returns a buffer<br />
&nbsp;&nbsp;&nbsp; to be sent to the client.&nbsp; Also returns an indication of whether or<br />
&nbsp;&nbsp;&nbsp; not the context is complete.<br />
<br />
 Return Value:<br />
<br />
&nbsp;&nbsp;&nbsp; Returns TRUE if successful; otherwise FALSE.<br />
<br />
--*/<br />
<br />
&nbsp;&nbsp; SECURITY_STATUS ss;<br />
&nbsp;&nbsp; TimeStamp&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tsExpiry;<br />
&nbsp;&nbsp; SecBufferDesc&nbsp;&nbsp; sbdOut;<br />
&nbsp;&nbsp; SecBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbOut;<br />
&nbsp;&nbsp; SecBufferDesc&nbsp;&nbsp; sbdIn;<br />
&nbsp;&nbsp; SecBuffer&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sbIn;<br />
&nbsp;&nbsp; ULONG&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fContextAttr;<br />
<br />
&nbsp;&nbsp; if (!pAS-&gt;fInitialized)&nbsp; {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ss = _AcquireCredentialsHandle(NULL, _T("NTLM"),<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SECPKG_CRED_INBOUND, NULL, NULL, NULL, NULL, &amp;pAS-&gt;hcred,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;tsExpiry);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ss &lt; 0) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "AcquireCredentialsHandle failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fHaveCredHandle = TRUE;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; // Prepare output buffer<br />
&nbsp;&nbsp; sbdOut.ulVersion = 0;<br />
&nbsp;&nbsp; sbdOut.cBuffers = 1;<br />
&nbsp;&nbsp; sbdOut.pBuffers = &amp;sbOut;<br />
<br />
&nbsp;&nbsp; sbOut.cbBuffer = *pcbOut;<br />
&nbsp;&nbsp; sbOut.BufferType = SECBUFFER_TOKEN;<br />
&nbsp;&nbsp; sbOut.pvBuffer = pOut;<br />
<br />
&nbsp;&nbsp; // Prepare input buffer<br />
&nbsp;&nbsp; sbdIn.ulVersion = 0;<br />
&nbsp;&nbsp; sbdIn.cBuffers = 1;<br />
&nbsp;&nbsp; sbdIn.pBuffers = &amp;sbIn;<br />
<br />
&nbsp;&nbsp; sbIn.cbBuffer = cbIn;<br />
&nbsp;&nbsp; sbIn.BufferType = SECBUFFER_TOKEN;<br />
&nbsp;&nbsp; sbIn.pvBuffer = pIn;<br />
<br />
&nbsp;&nbsp; ss = _AcceptSecurityContext(&amp;pAS-&gt;hcred,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fInitialized ? &amp;pAS-&gt;hctxt : NULL, &amp;sbdIn, 0,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SECURITY_NATIVE_DREP, &amp;pAS-&gt;hctxt, &amp;sbdOut, &amp;fContextAttr,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;tsExpiry);<br />
&nbsp;&nbsp; if (ss &lt; 0)&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "AcceptSecurityContext failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; pAS-&gt;fHaveCtxtHandle = TRUE;<br />
<br />
&nbsp;&nbsp; // If necessary, complete token<br />
&nbsp;&nbsp; if (ss == SEC_I_COMPLETE_NEEDED || ss == SEC_I_COMPLETE_AND_CONTINUE) {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_CompleteAuthToken) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ss = _CompleteAuthToken(&amp;pAS-&gt;hctxt, &amp;sbdOut);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (ss &lt; 0)&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf(stderr, "CompleteAuthToken failed with %08X\n", ss);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fprintf (stderr, "CompleteAuthToken not supported.\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; *pcbOut = sbOut.cbBuffer;<br />
<br />
&nbsp;&nbsp; if (!pAS-&gt;fInitialized)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAS-&gt;fInitialized = TRUE;<br />
<br />
&nbsp;&nbsp; *pfDone = !(ss == SEC_I_CONTINUE_NEEDED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; || ss == SEC_I_COMPLETE_AND_CONTINUE);<br />
<br />
&nbsp;&nbsp; return TRUE;<br />
}<br />
<br />
<br />
///////////////////////////////////////////////////////////////////////////////<br />
<br />
<br />
BOOL WINAPI SSPLogonUser(LPTSTR szDomain, LPTSTR szUser, LPTSTR szPassword) {<br />
<br />
&nbsp;&nbsp; AUTH_SEQ&nbsp;&nbsp;&nbsp; asServer&nbsp;&nbsp; = {0};<br />
&nbsp;&nbsp; AUTH_SEQ&nbsp;&nbsp;&nbsp; asClient&nbsp;&nbsp; = {0};<br />
&nbsp;&nbsp; BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fDone&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = FALSE;<br />
&nbsp;&nbsp; BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fResult&nbsp;&nbsp;&nbsp; = FALSE;<br />
&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbOut&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br />
&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbIn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 0;<br />
&nbsp;&nbsp; DWORD&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbMaxToken = 0;<br />
&nbsp;&nbsp; PVOID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pClientBuf = NULL;<br />
&nbsp;&nbsp; PVOID&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pServerBuf = NULL;<br />
&nbsp;&nbsp; PSecPkgInfo pSPI&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = NULL;<br />
&nbsp;&nbsp; HMODULE&nbsp;&nbsp;&nbsp;&nbsp; hModule&nbsp;&nbsp;&nbsp; = NULL;<br />
<br />
&nbsp;&nbsp; SEC_WINNT_AUTH_IDENTITY ai;<br />
<br />
&nbsp;&nbsp; __try {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hModule = LoadSecurityDll();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!hModule)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get max token size<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _QuerySecurityPackageInfo(_T("NTLM"), &amp;pSPI);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbMaxToken = pSPI-&gt;cbMaxToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeContextBuffer(pSPI);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Allocate buffers for client and server messages<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pClientBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pServerBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbMaxToken);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Initialize auth identity structure<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ZeroMemory(&amp;ai, sizeof(ai));<br />
#if defined(UNICODE) || defined(_UNICODE)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Domain = szDomain;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.DomainLength = lstrlen(szDomain);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.User = szUser;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.UserLength = lstrlen(szUser);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Password = szPassword;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.PasswordLength = lstrlen(szPassword);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;<br />
#else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Domain = (unsigned char *)szDomain;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.DomainLength = lstrlen(szDomain);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.User = (unsigned char *)szUser;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.UserLength = lstrlen(szUser);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Password = (unsigned char *)szPassword;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.PasswordLength = lstrlen(szPassword);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ai.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;<br />
#endif<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Prepare client message (negotiate) .<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbOut = cbMaxToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!GenClientContext(&amp;asClient, &amp;ai, NULL, 0, pClientBuf, &amp;cbOut, &amp;fDone))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Prepare server message (challenge) .<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbIn = cbOut;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbOut = cbMaxToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!GenServerContext(&amp;asServer, pClientBuf, cbIn, pServerBuf, &amp;cbOut,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;fDone))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Most likely failure: AcceptServerContext fails with SEC_E_LOGON_DENIED<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // in the case of bad szUser or szPassword.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Unexpected Result: Logon will succeed if you pass in a bad szUser and<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // the guest account is enabled in the specified domain.<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Prepare client message (authenticate) .<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbIn = cbOut;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbOut = cbMaxToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!GenClientContext(&amp;asClient, &amp;ai, pServerBuf, cbIn, pClientBuf, &amp;cbOut,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;fDone))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Prepare server message (authentication) .<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbIn = cbOut;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cbOut = cbMaxToken;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!GenServerContext(&amp;asServer, pClientBuf, cbIn, pServerBuf, &amp;cbOut,<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;fDone))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __leave;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fResult = TRUE;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HANDLE hToken = NULL;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_QuerySecurityContextToken(&amp;asServer.hctxt, &amp;hToken) == 0)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (IsGuest(hToken))<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Logged in as Guest\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fResult = FALSE;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Logged in as the desired user\n");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CloseHandle(hToken);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
<br />
<br />
&nbsp;&nbsp; } __finally {<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Clean up resources<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (asClient.fHaveCtxtHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _DeleteSecurityContext(&amp;asClient.hctxt);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (asClient.fHaveCredHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeCredentialsHandle(&amp;asClient.hcred);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (asServer.fHaveCtxtHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _DeleteSecurityContext(&amp;asServer.hctxt);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (asServer.fHaveCredHandle)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _FreeCredentialsHandle(&amp;asServer.hcred);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (hModule)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UnloadSecurityDll(hModule);<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HeapFree(GetProcessHeap(), 0, pClientBuf);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HeapFree(GetProcessHeap(), 0, pServerBuf);<br />
<br />
&nbsp;&nbsp; }<br />
<br />
&nbsp;&nbsp; return fResult;<br />
}<br />
<br />
//--------------------------------------------------------------------<br />
// The GetConsoleInput function gets an array of characters from the <br />
// keyboard, while printing only asterisks to the screen.<br />
<br />
void GetConsoleInput(TCHAR* strInput, int intMaxChars)<br />
{<br />
	char ch;<br />
	char minChar = ' ';<br />
	minChar++;<br />
<br />
	ch = getch();<br />
	while (ch != '\r')<br />
	{<br />
		if (ch == '\b' &amp;&amp; strlen(strInput) &gt; 0)<br />
		{<br />
			strInput[strlen(strInput)-1]&nbsp;&nbsp; = '\0';<br />
			printf("\b \b");<br />
		}<br />
		else if (ch &gt;= minChar &amp;&amp; (int)strlen(strInput) &lt; intMaxChars)<br />
		{<br />
			strInput[strlen(strInput)+1] = '\0';<br />
			strInput[strlen(strInput)]&nbsp;&nbsp; = ch;<br />
			putch('*');<br />
		}<br />
		ch = getch();<br />
	}<br />
	putch('\n');<br />
}<br />
<br />
void _tmain(int argc, TCHAR **argv)<br />
{<br />
	TCHAR password[PWLEN+1];<br />
<br />
&nbsp;&nbsp; if (argc != 3) <br />
	{<br />
		_tprintf(_T("Usage: %s DomainName UserName\n"), argv[0]);<br />
		return;<br />
	}<br />
<br />
	_tprintf(_T("Enter password for the specified user : "));<br />
	password[0] = 0;<br />
	GetConsoleInput(password, PWLEN);<br />
	_tprintf(_T("\n"));<br />
&nbsp;&nbsp; // argv[1] - Domain Name<br />
&nbsp;&nbsp; // argv[2] - User Name<br />
&nbsp;&nbsp; if (SSPLogonUser(argv[1], argv[2], password))<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _tprintf(_T("User Credentials are valid\n"));<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; else<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _tprintf(_T("User Credentials are NOT valid\n"));<br />
}</p><p>&nbsp;</p><p>출처 : 다년간의 프로그램밍 경험&nbsp;샘플 코드는 msdn(<a target="_blank" class="con_link" href="http://support.microsoft.com/kb/180548">http://support.microsoft.com/kb/180548</a>)</p><p>http://blog.naver.com/process3?Redirect=Log&amp;logNo=20041204174<br />
</p></div>      </div></div><br/><br/>tag : <a href="/tag/로그온" rel="tag">로그온</a>,&nbsp;<a href="/tag/LogonUser" rel="tag">LogonUser</a>,&nbsp;<a href="/tag/SSPI" rel="tag">SSPI</a>			 ]]> 
		</description>
		<category> └Windows Internals</category>
		<category>로그온</category>
		<category>LogonUser</category>
		<category>SSPI</category>

		<comments>http://octet.egloos.com/1163423#comments</comments>
		<pubDate>Thu, 20 Dec 2007 01:15:33 GMT</pubDate>
		<dc:creator>narumee</dc:creator>
	</item>
</channel>
</rss>
