이번 덤프는 BugCheck 0xC5: DRIVER_CORRUPTED_EXPOOL인데 주로 풀 메모리 관련해서 유효하지 않은 주소 영역에 접근했을 때 발생한다.
0xA, 0x50 등과 함께 이런 오류 코드가 발생하면 누군가가 메모리를 손상시켰구나라고 생각하면 대부분 맞다.
기본 분석부터 시작해보자.
kd> !analyze -v
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************
DRIVER_CORRUPTED_EXPOOL (c5)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is
caused by drivers that have corrupted the system pool. Run the driver
verifier against any new (or suspect) drivers, and if that doesn't turn up
the culprit, then use gflags to enable special pool.
Arguments:
Arg1: 00000004, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000001, value 0 = read operation, 1 = write operation
Arg4: 8355a4c1, address which referenced memory
Debugging Details:
------------------
DUMP_CLASS: 1
DUMP_QUALIFIER: 402
BUILD_VERSION_STRING: 7601.18044.x86fre.win7sp1_gdr.130104-1431
SYSTEM_MANUFACTURER: LG Electronics
SYSTEM_PRODUCT_NAME: U460-MFBJL
SYSTEM_SKU: System SKUNumber
SYSTEM_VERSION: 1.0
BIOS_VENDOR: Phoenix Technologies Ltd.
BIOS_VERSION: UNCNSF09
BIOS_DATE: 04/22/2013
BASEBOARD_MANUFACTURER: LG Electronics
BASEBOARD_PRODUCT: U560
BASEBOARD_VERSION: 1.0
DUMP_TYPE: 0
BUGCHECK_P1: 4
BUGCHECK_P2: 2
BUGCHECK_P3: 1
BUGCHECK_P4: ffffffff8355a4c1
BUGCHECK_STR: 0xC5_2
CURRENT_IRQL: 2
FAULTING_IP:
nt!ExAllocatePoolWithTag+4b7
8355a4c1 897004 mov dword ptr [eax+4],esi
CPU_COUNT: 4
CPU_MHZ: 703
CPU_VENDOR: GenuineIntel
CPU_FAMILY: 6
CPU_MODEL: 3a
CPU_STEPPING: 9
CPU_MICROCODE: 6,3a,9,0 (F,M,S,R) SIG: 17'00000000 (cache) 17'00000000 (init)
DEFAULT_BUCKET_ID: WIN7_DRIVER_FAULT
PROCESS_NAME: explorer.exe
ANALYSIS_SESSION_HOST: PAUL-PC
ANALYSIS_SESSION_TIME: 11-14-2017 10:36:26.0368
ANALYSIS_VERSION: 10.0.10575.567 amd64fre
TRAP_FRAME: b683f62c -- (.trap 0xffffffffb683f62c)
ErrCode = 00000002
eax=00000000 ebx=835706c0 ecx=8b6ac9f0 edx=86b117b0 esi=83570840 edi=835706c4
eip=8355a4c1 esp=b683f6a0 ebp=b683f6e8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
nt!ExAllocatePoolWithTag+0x4b7:
8355a4c1 897004 mov dword ptr [eax+4],esi ds:0023:00000004=????????
Resetting default scope
LAST_CONTROL_TRANSFER: from 8355a4c1 to 8347ac7b
STACK_TEXT:
b683f62c 8355a4c1 badb0d00 86b117b0 b683f690 nt!KiTrap0E+0x2cf
b683f6e8 83ba6aa1 00000000 00000040 7843464d nt!ExAllocatePoolWithTag+0x4b7
b683f700 83ba8091 8ae62bf8 8bbdc820 869f1530 fltmgr!ExAllocateFromPagedLookasideList+0x27
b683f718 95224e17 86a2aaf8 00000010 00000010 fltmgr!FltAllocateContext+0xa9
b683f73c 952251b6 8bbdc820 b683f79c 86e48320 MyDrv+0x8e17
b683f758 952252cc 8bbdc820 b683f79c 86e48320 MyDrv+0x91b6
b683f77c 83ba7aeb 8bbdc820 b683f79c b683f7c8 MyDrv+0x92cc
b683f7e8 83baa9f0 b683f82c d39a3dd8 00000000 fltmgr!FltpPerformPreCallbacks+0x34d
b683f800 83bbe1fe b683f82c 83bc1f3c 00000000 fltmgr!FltpPassThroughInternal+0x40
b683f814 83bbe8b7 b683f82c d39a3dd8 86b76f80 fltmgr!FltpCreateInternal+0x24
b683f858 83470c0e 86e1c590 86e0f7e8 8b67de58 fltmgr!FltpCreate+0x2c9
b683f870 b65ddffa 86b76f80 8b67de58 86b76f80 nt!IofCallDriver+0x63
b683f900 83470c0e 8b67de58 d39a3dd8 86b76fdc SomeDrv+0x5ffa
b683f918 836803ee a90c97a7 b683fac0 00000000 nt!IofCallDriver+0x63
b683f9f0 8365fc1e 86d4ae20 85ebb9c8 89a08d20 nt!IopParseDevice+0xee6
b683fa6c 83670030 00000000 b683fac0 00000040 nt!ObpLookupObjectName+0x4fa
b683fac8 83666b0e 081df704 85ebb9c8 00000001 nt!ObOpenObjectByName+0x165
b683fb44 8366cc94 179b0980 00020000 081df704 nt!IopCreateFile+0x673
b683fb8c b65b9725 179b0980 00020000 081df704 nt!NtOpenFile+0x2a
b683fc14 8347789a 179b0980 00020000 081df704 BadDrv+0x1725
b683fc14 775c7094 179b0980 00020000 081df704 nt!KiFastCallEntry+0x12a
081df6d8 775c5ce4 740723ea 179b0980 00020000 ntdll!KiFastSystemCallRet
081df6dc 740723ea 179b0980 00020000 081df704 ntdll!ZwOpenFile+0xc
081df724 74072a9e 081df744 00000000 00020000 ntmarta!I_MartaFileNtOpenFile+0x4d
081df754 74072b79 179b0038 00020000 081df7a8 ntmarta!MartaGetFileParentContext+0x5c
081df7b4 740726fe 179b0038 081df7e0 00000005 ntmarta!MartaGetRightsFromContext+0xd1
081df808 6fca3819 17a823a0 00000001 00000005 ntmarta!AccRewriteGetNamedRights+0x7f
081df844 6fca3f7c 17a823a0 081df86c 00000000 ntshrui!CFolderAclEngine::_GetAcl+0x30
081df874 6fca3f05 17d65814 081df894 6fca316c ntshrui!CFolderAclEngine::_IsItemPrivate+0x63
081df88c 6fca4960 00000000 17d65814 081df8c8 ntshrui!CSmbShareEngine::GetItemSharingStatus+0x1b
081df8b4 6fca4a18 17d65814 17f28478 00659530 ntshrui!CSharingOverlayPrivate::_GetSharingStatus+0x85
081df8d4 76512ca8 00622348 17bafb04 00000010 ntshrui!CSharingOverlayPrivate::IsMemberOf+0x69
081df904 7662fcb4 081df958 00000010 00000064 SHELL32!CFSIconOverlayManager::_GetFileOverlayInfo+0x11a
081df920 76512bae 0062adc0 081df958 00000010 SHELL32!CFSIconOverlayManager::GetFileOverlayInfo+0x1b
081dfb64 76512ab8 040503f8 081dfbe4 00000001 SHELL32!CFSFolder::_GetOverlayInfo+0x10f
081dfb78 7651478b 17dd39c0 040503f8 081dfbe4 SHELL32!CFSFolder::GetOverlayIndex+0x28
081dfba0 765138fe 08db2d04 040503f8 081dfbe4 SHELL32!CDesktopFolder::GetOverlayIndex+0x40
081dfbc0 7014b2d0 08de60f8 040503f8 081dfbe4 SHELL32!CRegFolder::GetOverlayIndex+0x45
081dfbdc 7014b289 ffffffff 040503f8 0f741488 EXPLORERFRAME!CNscOverlayTask::_Extract+0x32
081dfbf4 701108f6 08de60e4 01000000 80000000 EXPLORERFRAME!CNscOverlayTask::InternalResumeRT+0x31
081dfc14 765763bb 0f74149c 7fffffff 08e15488 EXPLORERFRAME!CRunnableTask::Run+0xce
081dfc30 76578c43 081dfc6c 00000000 17a748d0 SHELL32!CShellTask::TT_Run+0x167
081dfc78 76578d77 081dfc90 7627b2b1 08e15488 SHELL32!CShellTaskThread::ThreadProc+0xa3
081dfc80 7627b2b1 08e15488 0902c6f8 081dfd04 SHELL32!CShellTaskThread::s_ThreadProc+0x1b
081dfc90 775ad897 17a748d0 7f78dcce 006287e0 SHLWAPI!ExecuteWorkItemThreadProc+0xe
081dfd04 775b0846 17a748d0 0902c6f8 7f78dfae ntdll!RtlpTpWorkCallback+0x11d
081dfe64 75d6ed6c 006287d8 081dfeb0 775e377b ntdll!TppWorkerThread+0x572
081dfe70 775e377b 006287d8 7f78df7a 00000000 kernel32!BaseThreadInitThunk+0xe
081dfeb0 775e374e 775b03e9 006287d8 00000000 ntdll!__RtlUserThreadStart+0x70
081dfec8 00000000 775b03e9 006287d8 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: kb
THREAD_SHA1_HASH_MOD_FUNC: d88f93992d675d51da0b6bb80cf72e33554c1a0c
THREAD_SHA1_HASH_MOD_FUNC_OFFSET: c610c8843f28f8a4ba858fe9abfac4088a42a8e1
THREAD_SHA1_HASH_MOD: a1e2774180770312a6efc2e8cf99f51b102d2427
FOLLOWUP_IP:
MyDrv+8e17
95224e17 8945f8 mov dword ptr [ebp-8],eax
FAULT_INSTR_CODE: 33f84589
SYMBOL_STACK_INDEX: 4
SYMBOL_NAME: MyDrv+8e17
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: MyDrv
IMAGE_NAME: MyDrv.sys
DEBUG_FLR_IMAGE_TIMESTAMP: 5281e1ad
FAILURE_BUCKET_ID: 0xC5_2_MyDrv+8e17
BUCKET_ID: 0xC5_2_MyDrv+8e17
PRIMARY_PROBLEM_CLASS: 0xC5_2_MyDrv+8e17
TARGET_TIME: 2014-02-03T12:10:15.000Z
OSBUILD: 7601
OSSERVICEPACK: 1000
SERVICEPACK_NUMBER: 0
OS_REVISION: 0
SUITE_MASK: 272
PRODUCT_TYPE: 1
OSPLATFORM_TYPE: x86
OSNAME: Windows 7
OSEDITION: Windows 7 WinNt (Service Pack 1) TerminalServer SingleUserTS
OS_LOCALE:
USER_LCID: 0
OSBUILD_TIMESTAMP: 2013-01-05 11:46:00
BUILDDATESTAMP_STR: 130104-1431
BUILDLAB_STR: win7sp1_gdr
BUILDOSVER_STR: 6.1.7601.18044.x86fre.win7sp1_gdr.130104-1431
ANALYSIS_SESSION_ELAPSED_TIME: 186c
ANALYSIS_SOURCE: KM
FAILURE_ID_HASH_STRING: km:0xc5_2_MyDrv+8e17
FAILURE_ID_HASH: {9dc76993-d915-564c-f684-d2b01a978dd4}
Followup: MachineOwner
---------
!analyze 내용의 콜 스택 아래 부분을 보면 FAILURE_BUCKET_ID: 0xC5_2_MyDrv+8e17 내용이 있다.
이 내용만 보면 MyDrv로 인해 문제가 발생한 것으로 오해하기 쉽다. 앞서 설명했지만 WinDbg에서는 nt 커널 모듈을 제외한 마지막 모듈을 보여주기 때문에 범인이 아닐 가능성도 있다.
물론 MyDrv가 범인일 수도 있기 때문에 철저한 분석을 통해 진짜 범인을 찾아보자.
우선 analyze 에서 시키는 대로 .trap 명령을 통해 문제가 발생한 부분으로 컨텍스트를 설정해보자.
kd> .trap 0xffffffffb683f62c
ErrCode = 00000002
eax=00000000 ebx=835706c0 ecx=8b6ac9f0 edx=86b117b0 esi=83570840 edi=835706c4
eip=8355a4c1 esp=b683f6a0 ebp=b683f6e8 iopl=0 nv up ei pl zr na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010246
nt!ExAllocatePoolWithTag+0x4b7:
8355a4c1
897004 mov dword ptr [eax+4],esi
ds:0023:00000004=????????
nt!ExAllocatePoolWithTag+0x4b7 시점에 mov dword ptr [eax+4],esi 명령을 수행하다 eax 가 0이라 eax+4인 00000004 주소에 접근했다.
그리고 00000004는 유효하지 않은 주소여서 문제가 발생했다.
그렇다면 문제 발생 시점의 앞 부분을 분석해서 eax에 0이 설정된 원인을 찾아야 한다.
먼저 kv 명령으로 콜 스택을 확인해서 문제 발생 상황을 살펴보자.
kd> kv
*** Stack trace for last set context - .thread/.cxr resets it
# ChildEBP RetAddr Args to Child
00 b683f6e8 83ba6aa1 00000000 00000040 7843464d nt!ExAllocatePoolWithTag+0x4b7// 2) BSOD 발생
01 b683f700 83ba8091 8ae62bf8 8bbdc820 869f1530 fltmgr!ExAllocateFromPagedLookasideList+0x27 (FPO: [Non-Fpo])
02 b683f718 95224e17 86a2aaf8 00000010 00000010 fltmgr!FltAllocateContext+0xa9 (FPO: [Non-Fpo])
03 b683f73c 952251b6 8bbdc820 b683f79c 86e48320 MyDrv+0x8e17 // 1) 필터매니저에게 컨텍스트 메모리 할당 요청
04 b683f758 952252cc 8bbdc820 b683f79c 86e48320 MyDrv+0x91b6
05 b683f77c 83ba7aeb 8bbdc820 b683f79c b683f7c8 MyDrv+0x92cc
06 b683f7e8 83baa9f0 b683f82c d39a3dd8 00000000 fltmgr!FltpPerformPreCallbacks+0x34d (FPO: [Non-Fpo])
07 b683f800 83bbe1fe b683f82c 83bc1f3c 00000000 fltmgr!FltpPassThroughInternal+0x40 (FPO: [Non-Fpo])
08 b683f814 83bbe8b7 b683f82c d39a3dd8 86b76f80 fltmgr!FltpCreateInternal+0x24 (FPO: [Non-Fpo])
09 b683f858 83470c0e 86e1c590 86e0f7e8 8b67de58 fltmgr!FltpCreate+0x2c9 (FPO: [Non-Fpo])
0a b683f870 b65ddffa 86b76f80 8b67de58 86b76f80 nt!IofCallDriver+0x63
0b b683f900 83470c0e 8b67de58 d39a3dd8 86b76fdc SomeDrv+0x5ffa
0c b683f918 836803ee a90c97a7 b683fac0 00000000 nt!IofCallDriver+0x63
0d b683f9f0 8365fc1e 86d4ae20 85ebb9c8 89a08d20 nt!IopParseDevice+0xee6
0e b683fa6c 83670030 00000000 b683fac0 00000040 nt!ObpLookupObjectName+0x4fa
0f b683fac8 83666b0e 081df704 85ebb9c8 00000001 nt!ObOpenObjectByName+0x165
10 b683fb44 8366cc94 179b0980 00020000 081df704 nt!IopCreateFile+0x673
11 b683fb8c b65b9725 179b0980 00020000 081df704 nt!NtOpenFile+0x2a
12 b683fc14 8347789a 179b0980 00020000 081df704 BadDrv+0x1725
13 b683fc14 775c7094 179b0980 00020000 081df704 nt!KiFastCallEntry+0x12a (FPO: [0,3] TrapFrame @ b683fc34)
... ...
1)번에서 미니필터로 동작 중인 MyDrv가 필터매니저(fltmgr)에게 메모리를 할당 받으려는 과정 중에 문제가 발생했다.
문제가 발생한 함수인 ExAllocatePoolWithTag를 디스어셈블링해서 eax가 손상된 원인을 찾아보자.
kd> u ExAllocatePoolWithTag+0x462 L18
nt!ExAllocatePoolWithTag+0x45d:
8355a467 ff742424 push dword ptr [esp+24h]
8355a46b e8f8efffff call nt!MiAllocatePoolPages (83559468)
8355a470 8bf0 mov esi,eax
8355a472 85f6 test esi,esi
8355a474 0f854e020000 jne nt!ExAllocatePoolWithTag+0x6bd (8355a6c8)
8355a47a ff442418 inc dword ptr [esp+18h]
8355a47e 837c241801 cmp dword ptr [esp+18h],1
8355a483 0f8504020000 jne nt!ExAllocatePoolWithTag+0x682 (8355a68d)
8355a489 f705b4305a8300020000 test dword ptr [nt!ExpPoolFlags (835a30b4)],200h
8355a493 0f84f4010000 je nt!ExAllocatePoolWithTag+0x682 (8355a68d)
8355a499 50 push eax
8355a49a 53 push ebx
8355a49b e8c0110000 call nt!ExDeferredFreePool (8355b660)
8355a4a0 e9e0feffff jmp nt!ExAllocatePoolWithTag+0x37b (8355a385)
8355a4a5 8b0e mov ecx,dword ptr [esi] // 4) ecx에 esi 값을 설정
8355a4a7 8b4104 mov eax,dword ptr [ecx+4]
8355a4aa 3bc6 cmp eax,esi
8355a4ac 0f85c8010000 jne nt!ExAllocatePoolWithTag+0x670 (8355a67a)
8355a4b2 8b5604 mov edx,dword ptr [esi+4]
8355a4b5 3932 cmp dword ptr [edx],esi
8355a4b7 0f85bd010000 jne nt!ExAllocatePoolWithTag+0x670 (8355a67a)
8355a4bd 8b01 mov eax,dword ptr [ecx] // 3) eax에 ecx 값을 설정
8355a4bf 8906 mov dword ptr [esi],eax // 2) esi에 eax 값을 설정
8355a4c1 897004 mov dword ptr [eax+4],esi // 1) eax+4에
esi 값을 설정하다 문제 발생
1)~4) 순서는 분석 흐름으로 실제 문제가 발생한 부분부터 거꾸로 확인한 순서이다. 문제가 발생한 1)번부터 확인해보면 결국 eax는 4)번에서 esi를 통해 ecx로 설정된 값이다.
2)번을 보면 esi에 eax 값을 넣고 있다. 한 번 확인해보자.
kd> r esi
Last set context:
esi=83570840
esi 값인 83570840도 확인해보자.
kd> dd 83570840 L1
83570840
00000000
esi에 0이 설정되어 있다. 3)번에서는 eax에 ecx 값을 넣고 있으니 ecx도 살펴보자.
kd> r ecx
Last set context:
ecx=8b6ac9f0
ecx 값인 8b6ac9f0도 확인해야 한다.
kd> dd 8b6ac9f0 L1
8b6ac9f0
00000000
ecx 역시 0이 설정되어 있다.
마지막으로 4)번에서 ecx를 설정한 esi를 살펴보자.
kd> r esi
Last set context:
esi=83570840
esi에 있는 83570840 값을 이번에는 ln 명령으로 커널의 특정 주소는 아닌지 확인해보자.
kd> ln 83570840
Browse module
Set bu breakpoint
(835706c0) nt!NonPagedPoolDescriptor+0x180
ln 명령으로 확인해보니 esi는 NonPagedPoolDescriptor 영역으로 확인된다. 그렇다면 풀 디스크립터인 esi에서 가져온 ecx 값은 풀 주소일 가능성이 높고, 이 주소를 누군가 0으로 손상시켰을 가능성이 의심된다.
esi 값인 83570840은 NonPagedPoolDescriptor 주소인 835706c0의 + 0x180 위치다. 디스크립터 시작 주소인 835706c0을 POOL_DESCRIPTOR 구조체에 넣어 확인해보자.
kd> dt _POOL_DESCRIPTOR 835706c0
nt!_POOL_DESCRIPTOR
+0x000 PoolType : 0 ( NonPagedPool )
+0x004 PagedLock : _KGUARDED_MUTEX
+0x004 NonPagedLock : 0xb683f6d0
+0x040 RunningAllocs : 0x262521c
+0x044 RunningDeAllocs : 0x25e6553
+0x048 TotalBigPages : 0x3d8c
+0x04c ThreadsProcessingDeferrals : 0
+0x050 TotalBytes : 0x5a47150
+0x080 PoolIndex : 0
+0x0c0 TotalPages : 0x2d28
+0x100 PendingFrees : 0x89895920 -> 0x8ba5a6e0 Void
+0x104 PendingFreeDepth : 6
+0x140 ListHeads : [512] _LIST_ENTRY [ 0x83570800 - 0x83570800 ]
0x140 에는 메모리 풀 주소들이 연결되어 있는 LIST_ENTRY 의 배열이 위치한다.
ecx 값이 디스크립터 시작 주소로부터 +0x180 위치라는 말은 0x140 위치의 ListHeads 필드로부터 +0x40 떨어진 위치라는 의미다. 따라서 계산을 통해 주소를 구할 수 있다.
kd> dt _LIST_ENTRY /v
ntdll!_LIST_ENTRY
struct _LIST_ENTRY, 2 elements, 0x8 bytes
+0x000 Flink : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes
+0x004 Blink : Ptr32 to struct _LIST_ENTRY, 2
elements, 0x8 bytes
LIST_ENTRY 의 크기를 확인하기 위해 dt 명령의 /v 옵션을 설정하니 8바이트라고 나온다. 0x40 / 8 = 8 이므로, 512개의 배열 중 8번째 인덱스 위치의 LIST_ENTRY가 ecx다.
ListHeads필드에 [n] 을 사용하면 인덱스로 접근 가능하다.
kd> dt _POOL_DESCRIPTOR 835706c0 ListHeads[8]
nt!_POOL_DESCRIPTOR
+0x140 ListHeads : [8] _LIST_ENTRY [ 0x0 - 0x86b117b0
]
8번째 인덱스를 확인해보니 LIST_ENTRY의 Flink가 정확하게 0 으로 확인된다. 이 값이 ecx에 설정된 것이다. 누군가 Flink 에 설정된 주소 값을 0으로 손상시켰음이 분명하다.
ecx가 문제였음이 확실해졌으니 이제 ecx를 !pool 명령으로 확인해보자.
kd> !pool ecx
Pool page 8b6ac9f0 region is Nonpaged pool
8b6ac808 doesn't look like a valid small pool allocation, checking to see
if the entire page is actually part of a large page allocation...
8b6ac808 is not valid pool. Checking for freed (or corrupt) pool
Bad previous allocation size @8b6ac808, last size was 0
***
*** An error (or corruption) in the pool was detected;
*** Attempting to diagnose the problem.
***
*** Use !poolval 8b6ac000 for more details.
Pool page [ 8b6ac000 ] is __inVALID.
Analyzing linked list...
Scanning for single bit errors...
None found
풀 헤더가 손상됐다고 나온다. 이런 경우 손상된 주소의 앞 쪽에서 누군가 메모리를 손상시켰을 가능성이 높다.
이제 1000 바이트 정도 앞 쪽 메모리 영역부터 확인해보자.
kd> !pool ecx-1000
Pool page 8b6ab9f0 region is Nonpaged pool
*8b6a0000 : large page allocation, tag is Ddk , size is 0xc808 bytes
Pooltag Ddk : Default for driver allocated memory (user's of ntddk.h)
손상된 풀 헤더의 앞에는 8b6a0000 주소부터 c808 바이트 크기로 할당된 꽤나 큰 라지 풀 영역이 존재한다.
풀 태그는 Ddk로 확인된다. Ddk는 nt 커널의 기본 풀 태그로 메모리 할당시 풀 태그를 지정하지 않으면 모두 이 태그로 할당된다. 따라서 이런 경우 누가 할당한 메모리인지 찾기 어렵다.
왠지 범인을 찾기가 쉽지 않을 것 같은 예감이 든다. 어쨌든 해당 풀의 마지막 부분부터 확인해보자.
kd> db 8b6a0000+c808-20
8b6ac7e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6a0000 풀의 끝 위치(8b6ac807) ↓
8b6ac7f8 00 00 00 00 00 00 00 00-5c 00 3f 00 3f 00 5c 00 ........\.?.?.\.
↓ 다음 풀 시작 위치(8b6ac808)
8b6ac808 43 00 3a 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 C.:.\.W.i.n.d.o.
8b6ac818 77 00 73 00 5c 00 53 00-79 00 73 00 74 00 65 00 w.s.\.S.y.s.t.e.
8b6ac828 6d 00 33 00 32 00 5c 00-00 00 00 00 00 00 00 00 m.3.2.\.........
8b6ac838 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac848 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac858 00 00
00 00 00 00 00 00-00 00 00 00 00 00 00 00
................
풀 시작 주소인 8b6a0000에 풀 크기인 c808을 더하면 8b6ac808인데, 0x20(32) 바이트 앞부터 확인한 내용이다.
이 Ddk 풀 태그의 라지 풀은 8b6ac808 앞인 8b6ac807 주소까지만 사용 가능하다. 그런데 할당된 영역을 넘어서 "\??\C:\Windows\System32\" 문자열로 덮어 써 버렸다.
이번에는 문제의 ecx 값인 8b6ac9f0까지 다시 확인해보자.
kd> db 8b6a0000+c808-20 L220
8b6ac7e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac7f8 00 00 00 00 00 00 00 00-5c 00 3f 00 3f 00 5c 00 ........\.?.?.\.
8b6ac808 43 00 3a 00 5c 00 57 00-69 00 6e 00 64 00 6f 00 C.:.\.W.i.n.d.o.
8b6ac818 77 00 73 00 5c 00 53 00-79 00 73 00 74 00 65 00 w.s.\.S.y.s.t.e.
8b6ac828 6d 00 33 00 32 00 5c 00-00 00 00 00 00 00 00 00 m.3.2.\.........
8b6ac838 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac848 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac858 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac868 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac878 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac888 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac898 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8a8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8b8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8d8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8e8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac8f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac908 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac918 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac928 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac938 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac948 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac958 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac968 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac978 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac988 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac998 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac9a8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac9b8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac9c8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
8b6ac9d8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
↓ ecx 주소(8b6ac9f0)
8b6ac9e8 00 00 00 00 00 00 00 00-00 00 00 00 40 08 57 83 ............@.W.
8b6ac9f8 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
덮어 쓴 문자열 뒷 부분부터 ecx 부분까지는 전부 0으로 설정되어 있다. 이제야 ecx에 0이 설정된 상황이 이해가 된다. 앞 쪽 라지 풀을 할당한 모듈에서 문자열을 복사하다 뒤에 있는 디스크립터 관련 풀 헤더 영역까지 손상시킨 것이다.
다행히 메모리가 손상된 원인은 확인했지만 큰 난관이 남아있다. 풀 태그가 기본 풀 태그인 Ddk기 때문에 도대체 어떤 모듈이 범인인지 찾기 어렵다.
풀 태그가 없기 때문에 풀 태그 문자열을 가지고 모듈을 찾는 방법은 사용할 수 없기 때문이다.
정작 중요한 범인을 찾지 못해 난감한 상황이 되었다. 이를 어쩐다.
차분하게 분석했던 내용을 다시 살펴보니 이상한 부분이 하나 보인다.
바로 문제를 일으킨 0xc808 바이트 크기의 라지 풀이다.
kd> ?c808
Evaluate expression: 51208 = 0000c808
해당 풀을 할당한 모듈의 입장에서 한 번 생각해 보았다.
무슨 일로 51,208 바이트나 되는 풀을 할당했을까? 이 정도 크기면 뭔가 임시로 할당했다 해제하는 메모리가 아니라 버퍼 같은 용도로 크게 할당해 놓고 주기적으로 사용하는 메모리가 아닐까?
아마도 버퍼 목적의 메모리라면 해당 버퍼 주소를 전역 변수 어딘가에 설정해놓지 않았을까?
이런 생각의 흐름을 통해 다음과 같은 코드를 상상해 보았다.
// 전역 작업 버퍼
PVOID g_pWorkBuffer;
VOID
InitBuffer()
{
// 이렇게 풀 태그를 지정하지 않으면 ‘Ddk’로 할당된다.
g_pWorkBuffer = ExAllocatePool(NonPagedPool, 0xc808);
... ...
}
VOID
SaveString(
IN PWSTR pszString,
IN ULONG cbString
)
{
memcpy(g_pWorkbuffer, pszString, cbString);
... ...
}
g_pWorkBuffer 같은 전역 변수가 있다면 라지 풀의 시작 주소인 8b6a0000 을 할당 받아 갖고 있을 가능성이 있다.
이를 검증하기 위해 모듈에서 풀 태그 문자열을 찾을 때 썼던 방법을 응용해보자. 풀 태그 문자열 대신 풀 주소 값인 8b6a0000을 넣어 보는 것이다.
메모리에 있는 문자열이 아닌 바이트 값을 검색하려면 문자열 검색 옵션인 -a는 설정하지 않아야 한다.
또 한 가지는 8b6a0000을 그대로 입력하면 안 된다. 반대로 한 글자 씩 ‘00 00 6a 8b’로 입력해야 한다. DWORD로 표시되는 8b6a0000 값은 실제 메모리에는 반대로 저장되어 있기 때문이다(이를 리틀 엔디언 방식이라고 한다. 이해가 안 된다면 리틀 엔디언과 빅 엔디언의 차이를 찾아보자).
kd> !for_each_module s @#Base @#End 00 00 6a 8b
6cc3ff72 00 00 6a 8b c1 6c ff ff-ff ff 00 00 00 00 ff ff ..j..l..........
b65be3a4 00 00 6a 8b 26 00 00 00-00
00 00 00 01 00 00 00
..j.&...........
유저 영역 주소인 6cc3ff72를 제외하면 커널 영역 주소로 b65be3a4가 하나 검색된다. 왠지 모르게 등골이 서늘해진다.
정말 예상대로 어떤 모듈의 주소가 맞을까? lmva 명령으로 확인해보자.
kd> lmva b65be3a4
Browse full module list
start end module name
b65b8000 b65c7700 BadDrv (no symbols)
Loaded symbol image file: BadDrv.sys
Image path: \??\C:\Windows\system32\drivers\BadDrv.sys
Image name: BadDrv.sys
Browse all global symbols functions data
Timestamp: Tue Mar 22 11:33:52 2011 (4D880A90)
CheckSum: 0001DB7F
ImageSize: 0000F700
Translations: 0000.04b0 0000.04e4 0409.04b0 0409.04e4
와우! 라지 풀의 주소인 8b6a0000이 있는 b65be3a4 값은 b65b8000에 위치한 BadDrv 모듈 내의 주소다. 정말 짜릿한 순간이다.
이번에는 b65be3a4 주소가 정확하게 BadDrv 모듈의 어느 부분인지 살펴보자.
!dh 명령을 모듈 시작 주소와 함께 사용하면 PE 헤더 형식으로 내용을 보여준다.
kd> !dh b65b8000
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
14C machine (i386)
5 number of sections
4D880A90 time date stamp Tue Mar 22 11:33:52 2011
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
10E characteristics
Executable
Line numbers stripped
Symbols stripped
32 bit word machine
OPTIONAL HEADER VALUES
10B magic #
6.00 linker version
6280 size of code
9200 size of initialized data
0 size of uninitialized data
50B9 address of entry point
280 base of code
----- new -----
00010000 image base
80 section alignment
80 file alignment
1 subsystem (Native)
5.00 operating system version
5.00 image version
1.10 subsystem version
F700 size of image
280 size of headers
1DB7F checksum
00040000 size of stack reserve
00001000 size of stack commit
00100000 size of heap reserve
00001000 size of heap commit
0 DLL characteristics
0 [ 0] address [size] of Export Directory
E600 [ 3C] address [size] of Import Directory
EB80 [ 4A8] address [size] of Resource Directory
0 [ 0] address [size] of Exception Directory
0 [ 0] address [size] of Security Directory
F080 [ 4F8] address [size] of Base Relocation Directory
370 [ 1C] address [size] of Debug Directory
0 [ 0] address [size] of Description Directory
0 [ 0] address [size] of Special Directory
0 [ 0] address [size] of Thread Storage Directory
0 [ 0] address [size] of Load Configuration Directory
0 [ 0] address [size] of Bound Import Directory
280 [ F0] address [size] of Import Address Table Directory
0 [ 0] address [size] of Delay Import Directory
0 [ 0] address [size] of COR20 Header Directory
0 [ 0] address [size] of Reserved Directory
SECTION HEADER #1
.text name
5CFE virtual size
280 virtual address
5D00 size of raw data
280 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
68000020 flags
Code
Not Paged
(no align specified)
Execute Read
Debug Directories(1)
Type Size Address Pointer
cv 94 0 f700 [Debug data not mapped]
SECTION HEADER #2
.data name
8680 virtual size // 데이터 섹션 크기
5F80 virtual address // 데이터 섹션 시작 위치
8680 size of raw data
5F80 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
C8000040 flags
Initialized Data
Not Paged
(no align specified)
Read Write
SECTION HEADER #3
INIT name
556 virtual size
E600 virtual address
580 size of raw data
E600 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
E2000020 flags
Code
Discardable
(no align specified)
Execute Read Write
SECTION HEADER #4
.rsrc name
4A8 virtual size
EB80 virtual address
500 size of raw data
EB80 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
42000040 flags
Initialized Data
Discardable
(no align specified)
Read Only
SECTION HEADER #5
.reloc name
61E virtual size
F080 virtual address
680 size of raw data
F080 file pointer to raw data
0 file pointer to relocation table
0 file pointer to line numbers
0 number of relocations
0 number of line numbers
42000040 flags
Initialized Data
Discardable
(no align specified)
Read Only
확인된 내용을 보면 전역 변수들이 위치하는 데이터 섹션(.data)은 b65bdf80부터 b65c6600까지다.
// 데이터 섹션 시작 위치(virtual address)
kd> ?b65b8000 + 5f80
Evaluate expression: -1235492992 = b65bdf80
// 데이터 섹션 크기(virtual size)
kd> ?b65bdf80 + 8680
Evaluate
expression: -1235458560 = b65c6600
데이터 섹션의 범위가 b65bdf80 – b65c6600 이므로 찾은 b65be3a4 값은 정확하게 데이터 섹션 내의 주소다. 예상대로 범인인 라지 풀은 BadDrv 모듈 전역 변수에 설정된 버퍼였던 것이다.
이번 예제도 다른 메모리 손상 이슈와 비슷하게 BadDrv 모듈에서 전역 버퍼로 할당한 메모리를 잘못 관리하여 뒷 부분 메모리까지 손상시킨 것이 원인으로 밝혀졌다.
이런 예제를 분석할 때면 어려운 퍼즐을 푸는 것 같은 스릴을 느낀다.
이번 분석의 교훈은 답이 보이지 않을 때는 범인의 입장에서 생각해보자이다. 숨겨진 퍼즐의 답을 찾을 수 있을 것이다.
'Dump Analysis' 카테고리의 다른 글
[0x133] DPC_WATCHDOG_VIOLATION (0) | 2018.07.29 |
---|---|
[0xC5] 해제 리스트 손상 (0) | 2018.07.19 |
[0x1A] 페이지 손상 (0) | 2018.07.12 |
[0x50] 해제된 핸들 (0) | 2018.07.09 |
[0x50] 숨겨진 콜 스택 (0) | 2018.07.07 |