On Self Dumps of Secure String API

Sometimes we get crashes with the following stack trace pattern:

0:000> kv 100
ChildEBP RetAddr  Args to Child             
0012cecc 7c91df4a 7c8094fe 00000002 0012cef8 ntdll!KiFastSystemCallRet
0012ced0 7c8094fe 00000002 0012cef8 00000001 ntdll!ZwWaitForMultipleObjects+0xc
0012cf6c 7c80a085 00000002 0012d09c 00000000 kernel32!WaitForMultipleObjectsEx+0x12c
0012cf88 6990763c 00000002 0012d09c 00000000 kernel32!WaitForMultipleObjects+0x18
0012d91c 699082b1 0012f090 00000001 00198312 faultrep!StartDWException+0×5df
0012e990 7c8635d1 0012f090 00000001 00000000 faultrep!ReportFault+0×533
0012f030 78138a09 0012f090 00000022 c000000d kernel32!UnhandledExceptionFilter+0×587
0012f368 781443d1 00000000 00000000 00000000 msvcr80!_invoke_watson+0xc4

0012f38c 0040b02f 0012f538 00000104 004f80a0 msvcr80!strcat_s+0×29
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f39c 0012f538 00000104 00430848 0012f538 Application+0xb020

0:000> .exptr 0012f090

----- Exception record at 0012f040:
ExceptionAddress: 781443d1 (msvcr80!strcat_s+0x00000029)
   ExceptionCode: c000000d
  ExceptionFlags: 00000000
NumberParameters: 0

----- Context record at 0012f098:
eax=f2c4dacf ebx=00000000 ecx=00000002 edx=7c91e514 esi=00000022 edi=00000000
eip=781443d1 esp=0012f36c ebp=004f7658 iopl=0 nv up ei ng nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000286
781443d1 83c414          add     esp,14h

0:000> !error c000000d
Error code: (NTSTATUS) 0xc000000d (3221225485) - An invalid parameter was passed to a service or function.

We clearly see that the crash involved strcat_s but we don’t see any invalid instruction or access violation call. The ADD instruction is perfectly valid above. However, if we disassemble EIP backwards we would see the call to _invalid_parameter function and it looks like it has a name association with Dr. Watson:

0:000> ub 781443d1
781443c4 5e              pop     esi
781443c5 8930            mov     dword ptr [eax],esi
781443c7 53              push    ebx
781443c8 53              push    ebx
781443c9 53              push    ebx
781443ca 53              push    ebx
781443cb 53              push    ebx
781443cc e89f46ffff      call    msvcr80!_invalid_parameter (78138a70)

0:000> uf 78138a70
Flow analysis was incomplete, some code may be missing
78138945 55              push    ebp
78138946 8dac2458fdffff  lea     ebp,[esp-2A8h]
7813894d 81ec28030000    sub     esp,328h


78138a70 55              push    ebp
78138a71 8bec            mov     ebp,esp
78138a73 ff3528401c78    push    dword ptr [msvcr80!__pInvalidArgHandler (781c4028)]
78138a79 e85ba1ffff      call    msvcr80!_decode_pointer (78132bd9)
78138a7e 85c0            test    eax,eax
78138a80 59              pop     ecx
78138a81 7403            je      msvcr80!_invalid_parameter+0×16 (78138a86)

78138a83 5d              pop     ebp
78138a84 ffe0            jmp     eax

78138a86 6a02            push    2
78138a88 e806330000      call    msvcr80!_crt_debugger_hook (7813bd93)
78138a8d 59              pop     ecx
78138a8e 5d              pop     ebp
78138a8f e9b1feffff      jmp     msvcr80!_invoke_watson (78138945)

According to MSDN documentation, _s secure functions by default use a postmortem debugger mechanism:

Secure-enhanced CRT parameter validation

So we have something that is similar to Self-Dump pattern here. The same parameter checking is seen in the case of C++ STL exceptions. In case of custom unhandled exception filters not resorting to WER faulrep.dll other stacks can show process termination, for example, with wcscpy_s:

0:000> kL 100
ChildEBP RetAddr 
0111cb64 7c947c39 ntdll!KiFastSystemCallRet
0111cb68 7c80202b ntdll!ZwTerminateProcess+0xc
0111cb78 78138a2b kernel32!TerminateProcess+0×20
0111ceb4 78144ba1 MSVCR80!_invoke_watson+0xe6

0111ced8 67dbb47d MSVCR80!wcscpy_s+0×29
0111cf00 67dbc93b Application!CopyName+0×5d

Here the specified size of the destination buffer was smaller than the size of source NULL-terminated strings, which was a good thing anyway: old strcpy or strcat function would definitely caused buffer overflow effects. Now we have a nice side effect too, the dump is saved, ready for a postmortem analysis and subsequent code improvement.

- Dmitry Vostokov @ DumpAnalysis.org -

Leave a Reply

You must be logged in to post a comment.