Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

windbg - x64 덤프 분석 시 메서드의 인자 또는 로컬 변수의 값을 확인하는 방법

전에도 관련 주제로 글을 쓴 적이 있는데요.

x64 콜 스택 인자 추적과 windbg의 Child-SP, RetAddr, Args to Child 값 확인
; https://www.sysnet.pe.kr/2/0/10832

windbg - x64 역어셈블 코드에서 닷넷 메서드 호출의 인자를 확인하는 방법
; https://www.sysnet.pe.kr/2/0/11348

windbg - x64 SOS 확장의 !clrstack 명령어가 출력하는 Child SP 값의 의미
; https://www.sysnet.pe.kr/2/0/11349

그때는 직접 RSP의 값을 구해가면서, 또는 Child-SP 칼럼을 이용하면서 인자 값 추적을 했는데 이번에는 windbg의 ".frame" 명령어를 이용해 간단하게 해결해 보겠습니다. 우선 실습 예제를 정하고,

// x64 + Release 빌드

using System;
using System.IO;

class Program
{
    static void Main(string[] args)
    {
        string path = "c:\\temp\\test.txt";
        int argLen = args.Length;

        if (argLen >= 1)
        {
            path = args[0];
        }

        FileProcess(path);
    }

    private static void FileProcess(string filePath)
    {
        FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        Console.WriteLine(fs.Length);
        fs.Close();
    }
}

실행해서 예외가 발생했을 때 비주얼 스튜디오에서 "Call Stack" 창에 열거된 함수 목록 중,

stack_frame_dbg_1.png

예외 발생은 가장 상단의 WinIOError 메서드이지만, 가장 하단의 "Program.Main" 항목을 더블 클릭해서 해당 메서드를 호출한 시점의 문맥으로 전환했기 때문에 우측의 "Watch" 창에 "args", "argLen", "path" 변수가 정상적으로 값을 담게 됩니다.

만약 위의 상태에서 "Call Stack" 창의 "Program.FileProcess" 메서드를 더블 클릭하면 역시 그 호출 스택의 문맥으로 변환하므로,

stack_frame_dbg_2.png

현재 선택된 콜 스택 프레임을 가리키는 화살표가 표시되고 그와 함께 해당 프레임에 속하지 않은 인자, 지역 변수는 Watch 창에서 회색 처리됩니다.

바로 이런 식의 프레임 문맥 전환을 할 수 있는 windbg 명령어가 ".frame"입니다. (전환된 문맥을 다시 해제하려면 ".cxr" 명령을 사용합니다.)




그럼 저렇게 예외가 발생했을 때 뜬 덤프를,

C:\temp\ConsoleApp1\bin\Release> procdump -e -ma -x "c:\temp" "C:\temp\ConsoleApp1\bin\Release\ConsoleApp1.exe"

ProcDump v9.0 - Sysinternals process dump utility
Copyright (C) 2009-2017 Mark Russinovich and Andrew Richards
Sysinternals - www.sysinternals.com

Process:               ConsoleApp1.exe (3412)
CPU threshold:         n/a
Performance counter:   n/a
Commit threshold:      n/a
Threshold seconds:     n/a
Hung window check:     Disabled
Log debug strings:     Disabled
Exception monitor:     Unhandled
Exception filter:      [Includes]
                       *
                       [Excludes]
Terminate monitor:     Disabled
Cloning type:          Disabled
Concurrent limit:      n/a
Avoid outage:          n/a
Number of dumps:       1
Dump folder:           c:\temp\
Dump filename/mask:    PROCESSNAME_YYMMDD_HHMMSS
Queue to WER:          Disabled
Kill after dump:       Disabled


Press Ctrl-C to end monitoring without terminating the process.

[10:02:06] Exception: 04242420
[10:02:06] Exception: E0434352.CLR

Unhandled Exception: System.IO.FileNotFoundException: Could not find file 'c:\temp\test.txt'.
   at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
   at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access)
   at Program.FileProcess(String filePath) in C:\temp\ConsoleApp1\bin\Release\ConsoleApp1\Program.cs:line 23
   at Program.Main(String[] args) in C:\temp\ConsoleApp1\bin\Release\ConsoleApp1\Program.cs:line 18
[10:02:06] Unhandled: E0434352.CLR
[10:02:06] Dump 1 initiated: c:\temp\ConsoleApp1.exe_191202_100206.dmp
[10:02:07] Dump 1 writing: Estimated dump file size is 96 MB.
[10:02:07] Dump 1 complete: 96 MB written in 0.1 seconds
[10:02:07] Dump count reached.

windbg에서 열어, 덤프 생성 시점의 Native call stack과 Managed call stack을 각각 다음과 같이 구할 수 있습니다.

0:000> k
 # Child-SP          RetAddr           Call Site
00 00000053`6eb3e780 00007ff9`faeba3f1 KERNELBASE!RaiseException+0x69
01 00000053`6eb3e860 00007ff9`faebb864 clr!RaiseTheExceptionInternalOnly+0x31f
02 00000053`6eb3e980 00007ff9`f7bbbeac clr!IL_Throw+0x114
03 00000053`6eb3eb30 00007ff9`f7c0701a mscorlib_ni+0x53beac
04 00000053`6eb3eb80 00007ff9`f7be3f1d mscorlib_ni+0x58701a
05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d
06 00000053`6eb3ed00 00007ff9`9b9508c4 0x00007ff9`9b950922
07 00000053`6eb3ed40 00007ff9`faea6c53 0x00007ff9`9b9508c4
08 00000053`6eb3ed70 00007ff9`faea6b68 clr!CallDescrWorkerInternal+0x83
09 00000053`6eb3edb0 00007ff9`faea73d0 clr!CallDescrWorkerWithHandler+0x4e
0a 00000053`6eb3edf0 00007ff9`fb027002 clr!MethodDescCallSite::CallTargetWorker+0x102
0b 00000053`6eb3eef0 00007ff9`fb0279c3 clr!RunMain+0x25f
0c 00000053`6eb3f0d0 00007ff9`fb027877 clr!Assembly::ExecuteMainMethod+0xb7
0d 00000053`6eb3f3c0 00007ff9`fb0271c3 clr!SystemDomain::ExecuteMainMethod+0x643
0e 00000053`6eb3f9c0 00007ff9`fb027141 clr!ExecuteEXE+0x3f
0f 00000053`6eb3fa30 00007ff9`fb0285b4 clr!_CorExeMainInternal+0xb2
10 00000053`6eb3fac0 00007ff9`fc278c01 clr!CorExeMain+0x14
11 00000053`6eb3fb00 00007ff9`fd9ea56c mscoreei!CorExeMain+0x112
12 00000053`6eb3fb60 00007ffa`0b0f7bd4 mscoree!CorExeMain_Exported+0x6c
13 00000053`6eb3fb90 00007ffa`0c44ced1 kernel32!BaseThreadInitThunk+0x14
14 00000053`6eb3fbc0 00000000`00000000 ntdll!RtlUserThreadStart+0x21


0:000> .loadby sos clr

0:000> !clrstack
OS Thread Id: 0xa820 (0)
        Child SP               IP Call Site
000000536eb3ea48 00007ffa09f8a839 [HelperMethodFrame: 000000536eb3ea48] 
000000536eb3eb30 00007ff9f7bbbeac System.IO.__Error.WinIOError(Int32, System.String)
000000536eb3eb80 00007ff9f7c0701a System.IO.FileStream.Init(System.String, System.IO.FileMode, System.IO.FileAccess, Int32, Boolean, System.IO.FileShare, Int32, System.IO.FileOptions, SECURITY_ATTRIBUTES, System.String, Boolean, Boolean, Boolean)
000000536eb3ec50 00007ff9f7be3f1d System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess)
000000536eb3ed00 00007ff99b950922 Program.FileProcess(System.String) [c:\temp\ConsoleApp1\Program.cs @ 23]
000000536eb3ed40 00007ff99b9508c4 Program.Main(System.String[]) [c:\temp\ConsoleApp1\Program.cs @ 18]
000000536eb3ef58 00007ff9faea6c53 [GCFrame: 000000536eb3ef58] 

.frame 명령어는 native call stack의 '#' 칼럼 값을 필요로 하기 때문에 닷넷 메서드의 경우 k 명령어의 "RetAddr" 칼럼과 "!clrstack"의 "IP" 값을 이용해 적절하게 매칭해 구해야 합니다. (또는 간단하게 windbg가 자동으로 구해서 보여주는 Child-SP 값을 비교하면 됩니다.)

 # Child-SP          RetAddr           Call Site
...[생략]...
02 00000053`6eb3e980 00007ff9`f7bbbeac clr!IL_Throw+0x114
03 00000053`6eb3eb30 00007ff9`f7c0701a mscorlib_ni+0x53beac  ==>   System.IO.__Error.WinIOError(Int32, System.String))
04 00000053`6eb3eb80 00007ff9`f7be3f1d mscorlib_ni+0x58701a  ==>   System.IO.FileStream.Init
05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d  ==>   System.IO.FileStream..ctor
06 00000053`6eb3ed00 00007ff9`9b9508c4 0x00007ff9`9b950922   ==>   Program.FileProcess
07 00000053`6eb3ed40 00007ff9`faea6c53 0x00007ff9`9b9508c4   ==>   Program.Main

따라서, Program.FileProcess 메서드의 문맥으로 전환하고 싶다면 다음과 같이 명령어를 수행하면 됩니다.

0:000> .frame /c /r 6
06 00000053`6eb3ed00 00007ff9`9b9508c4 0x00007ff9`9b950922
rax=0000000000000005 rbx=000000536eb3ee88 rcx=0000000000000050
rdx=000000536eb3e400 rsi=00000167136c2e20 rdi=00000167136c6008
rip=00007ff99b950922 rsp=000000536eb3ed00 rbp=000000536eb3ed90
 r8=00007ff9fb06197a  r9=0000000000000000 r10=0000000000000000
r11=0000000011a81750 r12=000000536eb3f110 r13=000000536eb3ee10
r14=000000536eb3ee88 r15=0000000000000004
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000200
00007ff9`9b950922 488bcf          mov     rcx,rdi

이렇게 문맥 전환이 되었을 때 가장 도움이 되는 값은 바로 rsp와 rip입니다. rip의 경우 sos.dll의 "!u" 명령어와 결합하면,

0:000> !u 00007ff99b950922
Normal JIT generated code
Program.FileProcess(System.String)
Begin 00007ff99b9508f0, size 58

c:\temp\ConsoleApp1\Program.cs @ 22:
00007ff9`9b9508f0 57              push    rdi
00007ff9`9b9508f1 56              push    rsi
00007ff9`9b9508f2 4883ec28        sub     rsp,28h
00007ff9`9b9508f6 488bf1          mov     rsi,rcx
00007ff9`9b9508f9 48b9384d72f7f97f0000 mov rcx,offset mscorlib_ni+0xa4d38 (00007ff9`f7724d38) (MT: System.IO.FileStream)
00007ff9`9b950903 e868c9565f      call    clr!JIT_NewCrossContext_Portable (00007ff9`faebd270)
00007ff9`9b950908 488bf8          mov     rdi,rax
00007ff9`9b95090b 488bcf          mov     rcx,rdi
00007ff9`9b95090e 488bd6          mov     rdx,rsi
00007ff9`9b950911 41b803000000    mov     r8d,3
00007ff9`9b950917 41b901000000    mov     r9d,1
00007ff9`9b95091d e876f9ffff      call    00007ff9`9b950298

c:\temp\ConsoleApp1\Program.cs @ 23:
>>> 00007ff9`9b950922 488bcf          mov     rcx,rdi
00007ff9`9b950925 e85627eeff      call    00007ff9`9b833080
00007ff9`9b95092a 488bc8          mov     rcx,rax
00007ff9`9b95092d e8ae20b25c      call    mscorlib_ni+0xdf29e0 (00007ff9`f84729e0) (System.Console.WriteLine(Int64), mdToken: 0000000006000b77)

c:\temp\ConsoleApp1\Program.cs @ 24:
00007ff9`9b950932 488bcf          mov     rcx,rdi
00007ff9`9b950935 48b8d030839bf97f0000 mov rax,7FF99B8330D0h
00007ff9`9b95093f 4883c428        add     rsp,28h
00007ff9`9b950943 5e              pop     rsi
00007ff9`9b950944 5f              pop     rdi
00007ff9`9b950945 48ffe0          jmp     rax

보는 바와 같이 해당 메서드의 전체 코드를 덤프하고 예외가 발생한 시점의 명령어(위에서는 call 00007ff9`9b950298) 바로 다음 명령어의 위치를 ">>>" 기호로 친절하게 알려줍니다.

또한 rsp 값(위에서는 000000536eb3ed00)을 통해 해당 메서드가 수행되기까지 쌓인 스택의 내용을 확인할 수 있습니다.

0:000> dq /c1 @rsp 
00000053`6eb3ed00  00000167`136c6008
00000053`6eb3ed08  00000167`136c2e20
00000053`6eb3ed10  00000000`00000003
00000053`6eb3ed18  00000000`00000001
00000053`6eb3ed20  00000053`6eb3ee10
00000053`6eb3ed28  00000167`136c2e08
00000053`6eb3ed30  00000053`6eb3ef58
00000053`6eb3ed38  00007ff9`9b9508c4
00000053`6eb3ed40  00000167`136c2e20
00000053`6eb3ed48  00000000`00000000
00000053`6eb3ed50  00000167`136c38dc
00000053`6eb3ed58  00000053`6eb3eb30
00000053`6eb3ed60  00000053`6eb3ee58
00000053`6eb3ed68  00007ff9`faea6c53
00000053`6eb3ed70  00000167`136c2e08
00000053`6eb3ed78  00007ff9`9b844148

재미있는 것은, sos.dll의 "!dso" 확장 명령어는 (현재 문맥의 rsp를 인지하지 않고) 덤프 시점의 rsp를 기준으로 닷넷 관련 객체를 덤프한다는 것입니다.

0:000> !dso
OS Thread Id: 0xa820 (0)
RSP/REG          Object           Name
rsi              00000167136c2e20 System.String    c:\temp\test.txt
rdi              00000167136c6008 System.IO.FileStream
000000536EB3E798 00000167136cc240 System.RuntimeType
000000536EB3E870 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3E888 00000167136cc1b0 System.Text.StringBuilder
000000536EB3E898 00000167136cc1b0 System.Text.StringBuilder
000000536EB3E8D8 00000167136c50b8 System.Globalization.CultureInfo
000000536EB3E8E0 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3E908 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3E960 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3E968 00000167136c7c38 System.Object[]    (System.Object[])
000000536EB3E970 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3E980 00000167136c50b8 System.Globalization.CultureInfo
000000536EB3E988 00000167136cc240 System.RuntimeType
000000536EB3E9A0 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3E9C0 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3E9D8 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EA10 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3EA28 00000167136c50b8 System.Globalization.CultureInfo
000000536EB3EA30 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EA88 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3EA90 00000167136c7c38 System.Object[]    (System.Object[])
000000536EB3EA98 00000167136c7c58 System.IO.FileNotFoundException
000000536EB3EAA0 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EB30 00000167136c50b8 System.Globalization.CultureInfo
000000536EB3EB38 00000167136cc070 System.String    Could not find file '{0}'.
000000536EB3EB40 00000167136c7c38 System.Object[]    (System.Object[])
000000536EB3EB70 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EB88 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EC10 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3EC18 00000167136c6008 System.IO.FileStream
000000536EB3EC38 00000167136c7690 System.String    test.txt
000000536EB3EC50 00000167136c6008 System.IO.FileStream
000000536EB3EC58 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3ECA0 00000167136c7690 System.String    test.txt
000000536EB3ECC0 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3ECD0 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3ECD8 00000167136c6008 System.IO.FileStream
000000536EB3ED00 00000167136c6008 System.IO.FileStream
000000536EB3ED08 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3ED28 00000167136c2e08 System.String[]
000000536EB3ED40 00000167136c2e20 System.String    c:\temp\test.txt
000000536EB3ED70 00000167136c2e08 System.String[]
000000536EB3EE58 00000167136c2e08 System.String[]
000000536EB3F0E0 00000167136c2e08 System.String[]
000000536EB3F0E8 00000167136c2e08 System.String[]

보는 바와 같이 .frame 명령어로 인해 rsp == "000000536EB3ED00"로 전환이 되었지만, !dso는 그것에 상관없이 스택 덤프를 하고 있으므로 중간에 "FileNotFoundException"이나 파일 경로를 담고 있는 System.String 객체등을 보여주게 됩니다.




아쉽게도 Program.FileProcess 메서드의 경우 rsp를 통해 직접적으로 알 수 있는 인자나 로컬 변수가 없습니다. 반면 운이 좋다면 System.IO.FileStream의 생성자처럼,

0:000> !u 00007ff9f7be3f1d
preJIT generated code
System.IO.FileStream..ctor(System.String, System.IO.FileMode, System.IO.FileAccess)
Begin 00007ff9f7be3ea0, size 8b
00007ff9`f7be3ea0 55              push    rbp
00007ff9`f7be3ea1 4157            push    r15
00007ff9`f7be3ea3 4156            push    r14
00007ff9`f7be3ea5 57              push    rdi
00007ff9`f7be3ea6 56              push    rsi
00007ff9`f7be3ea7 53              push    rbx
00007ff9`f7be3ea8 4883ec78        sub     rsp,78h
00007ff9`f7be3eac 488dac24a0000000 lea     rbp,[rsp+0A0h]
00007ff9`f7be3eb4 488bf9          mov     rdi,rcx
00007ff9`f7be3eb7 488bf2          mov     rsi,rdx
00007ff9`f7be3eba 418bd8          mov     ebx,r8d
00007ff9`f7be3ebd 458bf1          mov     r14d,r9d
00007ff9`f7be3ec0 488bce          mov     rcx,rsi
00007ff9`f7be3ec3 90              nop
00007ff9`f7be3ec4 e8a72b0200      call    mscorlib_ni+0x586a70 (00007ff9`f7c06a70) (System.IO.Path.GetFileName(System.String), mdToken: 000000000600191a)
00007ff9`f7be3ec9 4c8bf8          mov     r15,rax
00007ff9`f7be3ecc b901000000      mov     ecx,1
00007ff9`f7be3ed1 90              nop
00007ff9`f7be3ed2 e8492c0200      call    mscorlib_ni+0x586b20 (00007ff9`f7c06b20) (System.IO.FileStream.GetSecAttrs(System.IO.FileShare), mdToken: 0000000006001854)
00007ff9`f7be3ed7 33c9            xor     ecx,ecx
00007ff9`f7be3ed9 894c2420        mov     dword ptr [rsp+20h],ecx
00007ff9`f7be3edd 894c2428        mov     dword ptr [rsp+28h],ecx
00007ff9`f7be3ee1 c744243001000000 mov     dword ptr [rsp+30h],1
00007ff9`f7be3ee9 c744243800100000 mov     dword ptr [rsp+38h],1000h
00007ff9`f7be3ef1 894c2440        mov     dword ptr [rsp+40h],ecx
00007ff9`f7be3ef5 4889442448      mov     qword ptr [rsp+48h],rax
00007ff9`f7be3efa 4c897c2450      mov     qword ptr [rsp+50h],r15
00007ff9`f7be3eff 894c2458        mov     dword ptr [rsp+58h],ecx
00007ff9`f7be3f03 894c2460        mov     dword ptr [rsp+60h],ecx
00007ff9`f7be3f07 894c2468        mov     dword ptr [rsp+68h],ecx
00007ff9`f7be3f0b 488bcf          mov     rcx,rdi
00007ff9`f7be3f0e 488bd6          mov     rdx,rsi
00007ff9`f7be3f11 448bc3          mov     r8d,ebx
00007ff9`f7be3f14 458bce          mov     r9d,r14d
00007ff9`f7be3f17 ff156310b4ff    call    qword ptr [mscorlib_ni+0xa4f80 (00007ff9`f7724f80)]
>>> 00007ff9`f7be3f1d 90              nop
00007ff9`f7be3f1e 488d65d8        lea     rsp,[rbp-28h]
00007ff9`f7be3f22 5b              pop     rbx
00007ff9`f7be3f23 5e              pop     rsi
00007ff9`f7be3f24 5f              pop     rdi
00007ff9`f7be3f25 415e            pop     r14
00007ff9`f7be3f27 415f            pop     r15
00007ff9`f7be3f29 5d              pop     rbp
00007ff9`f7be3f2a c3              ret

"rsp + [offset]"의 연산을 하고 있는 코드가 있다면 인자 값이나 로컬 변수의 값 추적이 쉬워집니다. 즉, 아래와 같은 식의 연산을 보게 되면,

mov dword ptr [rsp+20h],ecx

우선 저 메서드가 수행될 시점의 rsp 값을 알아야 하는데 바로 이럴 때 ".frame" 명령어를 사용하면 되는 것입니다.

0:000> .cxr
Resetting default scope

0:000> .frame /c /r 5
05 00000053`6eb3ec50 00007ff9`9b950922 mscorlib_ni+0x563f1d
rax=0000000000000005 rbx=0000000000000003 rcx=0000000000000050
rdx=000000536eb3e400 rsi=00000167136c2e20 rdi=00000167136c6008
rip=00007ff9f7be3f1d rsp=000000536eb3ec50 rbp=000000536eb3ecf0
 r8=00007ff9fb06197a  r9=0000000000000000 r10=0000000000000000
r11=0000000011a81750 r12=000000536eb3f110 r13=000000536eb3ee10
r14=0000000000000001 r15=00000167136c7690
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000200
mscorlib_ni+0x563f1d:
00007ff9`f7be3f1d 90              nop

이로 인해 결국 다음의 코드 수행으로 인해 저장된 값들은,

00007ff9`f7be3ed9 894c2420        mov     dword ptr [rsp+20h],ecx
00007ff9`f7be3edd 894c2428        mov     dword ptr [rsp+28h],ecx
00007ff9`f7be3ee1 c744243001000000 mov     dword ptr [rsp+30h],1
00007ff9`f7be3ee9 c744243800100000 mov     dword ptr [rsp+38h],1000h
00007ff9`f7be3ef1 894c2440        mov     dword ptr [rsp+40h],ecx
00007ff9`f7be3ef5 4889442448      mov     qword ptr [rsp+48h],rax
00007ff9`f7be3efa 4c897c2450      mov     qword ptr [rsp+50h],r15
00007ff9`f7be3eff 894c2458        mov     dword ptr [rsp+58h],ecx
00007ff9`f7be3f03 894c2460        mov     dword ptr [rsp+60h],ecx
00007ff9`f7be3f07 894c2468        mov     dword ptr [rsp+68h],ecx

이렇게 간단하게 확인할 수 있습니다.

0:000> dq /c1 rsp+0x20 LA
00000053`6eb3ec70  00000000`00000000
00000053`6eb3ec78  00000000`00000000
00000053`6eb3ec80  00000000`00000001
00000053`6eb3ec88  00000000`00001000
00000053`6eb3ec90  00000000`00000000
00000053`6eb3ec98  00000000`00000000
00000053`6eb3eca0  00000167`136c7690
00000053`6eb3eca8  00000000`00000000
00000053`6eb3ecb0  00000000`00000000
00000053`6eb3ecb8  00000167`00000000

실제로 FileStream 생성자를 .NET Reflector로 보면,

[SecuritySafeCritical]
public FileStream(string path, FileMode mode, FileAccess access)
    : this(path, mode, access, FileShare.Read, 4096, FileOptions.None, Path.GetFileName(path), bFromProxy: false)
{
}

7번째 인자(rsp+48h)가 Path.GetFileName(path)이므로 System.String 참조 객체가 전달되는데, 그렇다면 당연히 sos.dll의 "!do" 확장 명령어로 보면,

0:000> !do 00000167`136c7690
Name:        System.String
MethodTable: 00007ff9f76a59c0
EEClass:     00007ff9f7682ec0
Size:        42(0x2a) bytes
File:        C:\WINDOWS\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      test.txt
Fields:
              MT    Field   Offset                 Type VT     Attr            Value Name
00007ff9f76a85a0  4000285        8         System.Int32  1 instance                8 m_stringLength
00007ff9f76a6838  4000286        c          System.Char  1 instance               74 m_firstChar
00007ff9f76a59c0  400028a       e8        System.String  0   shared           static Empty
                                 >> Domain:Value  0000016711a15f20:NotInit  <<

위와 같이 "test.txt" 파일명이 전달된 것을 확인할 수 있습니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 12/19/2019 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 




1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...
NoWriterDateCnt.TitleFile(s)
12130정성태1/26/2020655VS.NET IDE: 142. Visual Studio에서 windbg의 "Open Executable..."처럼 EXE를 직접 열어 디버깅을 시작하는 방법
12129정성태1/26/20202355.NET Framework: 882. C# - 키움 Open API+ 사용 시 Registry 등록 없이 KHOpenAPI.ocx 사용하는 방법 [1]
12128정성태1/26/2020765오류 유형: 591. The code execution cannot proceed because mfc100.dll was not found. Reinstalling the program may fix this problem.
12127정성태1/28/2020752.NET Framework: 881. C# DLL에서 제공하는 Win32 export 함수의 내부 동작 방식(VT Fix up Table)파일 다운로드1
12126정성태1/25/2020734.NET Framework: 880. C# - PE 파일로부터 IMAGE_COR20_HEADER 및 VTableFixups 테이블 분석파일 다운로드1
12125정성태1/24/2020553VS.NET IDE: 141. IDE0019 - Use pattern matching
12124정성태1/24/20201301VS.NET IDE: 140. IDE1006 - Naming rule violation: These words must begin with upper case characters: ...
12123정성태1/23/20201025웹: 39. Google Analytics - gtag 함수를 이용해 페이지 URL 수정 및 별도의 이벤트 생성 방법
12122정성태1/22/2020893.NET Framework: 879. C/C++의 UNREFERENCED_PARAMETER 매크로를 C#에서 우회하는 방법(IDE0060 - Remove unused parameter '...')파일 다운로드1
12121정성태1/24/2020563VS.NET IDE: 139. Visual Studio - Error List: "Could not find schema information for the ..."파일 다운로드1
12120정성태1/20/2020880.NET Framework: 878. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 네 번째 이야기(IL 코드로 직접 구현)파일 다운로드1
12119정성태1/17/2020962디버깅 기술: 160. Windbg 확장 DLL 만들기 (3) - C#으로 만드는 방법
12118정성태1/17/2020975개발 환경 구성: 466. C# DLL에서 Win32 C/C++처럼 dllexport 함수를 제공하는 방법 - 세 번째 이야기 [1]
12117정성태1/15/2020963디버깅 기술: 159. C# - 디버깅 중인 프로세스를 강제로 다른 디버거에서 연결하는 방법파일 다운로드1
12116정성태1/15/2020797디버깅 기술: 158. Visual Studio로 디버깅 시 sos.dll 확장 명령어를 (비롯한 windbg의 다양한 기능을) 수행하는 방법
12115정성태1/14/2020844디버깅 기술: 157. C# - PEB.ProcessHeap을 이용해 디버깅 중인지 확인하는 방법파일 다운로드1
12114정성태1/13/20201177디버깅 기술: 156. C# - PDB 파일로부터 심벌(Symbol) 및 타입(Type) 정보 열거 [1]파일 다운로드3
12113정성태1/12/20201672오류 유형: 590. Visual C++ 빌드 오류 - fatal error LNK1104: cannot open file 'atls.lib' [1]
12112정성태1/12/2020695오류 유형: 589. PowerShell - 원격 Invoke-Command 실행 시 "WinRM cannot complete the operation" 오류 발생
12111정성태3/23/20201804디버깅 기술: 155. C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) [12]
12110정성태6/23/20201253디버깅 기술: 154. Patch Guard로 인해 블루 스크린(BSOD)가 발생하는 사례 [5]파일 다운로드1
12109정성태1/10/2020957오류 유형: 588. Driver 프로젝트 빌드 오류 - Inf2Cat error -2: "Inf2Cat, signability test failed."
12108정성태1/10/2020749오류 유형: 587. Kernel Driver 시작 시 127(The specified procedure could not be found.) 오류 메시지 발생
12107정성태1/10/2020840.NET Framework: 877. C# - 프로세스의 모든 핸들을 열람 - 두 번째 이야기
12106정성태1/8/2020951VC++: 136. C++ - OSR Driver Loader와 같은 Legacy 커널 드라이버 설치 프로그램 제작 [1]
12105정성태1/8/2020881디버깅 기술: 153. C# - PEB를 조작해 로드된 DLL을 숨기는 방법
1  2  3  4  5  6  7  8  9  10  11  12  [13]  14  15  ...