Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

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 at outlook.com

비밀번호

댓글 작성자
 




1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13497정성태12/22/20232276오류 유형: 885. Visual Studiio - error : Could not connect to the remote system. Please verify your connection settings, and that your machine is on the network and reachable.
13496정성태12/21/20232281Linux: 66. 리눅스 - 실행 중인 프로세스 내부의 환경변수 설정을 구하는 방법 (gdb)
13495정성태12/20/20232273Linux: 65. clang++로 공유 라이브러리의 -static 옵션 빌드가 가능할까요?
13494정성태12/20/20232469Linux: 64. Linux 응용 프로그램의 (C++) so 의존성 줄이기(ReleaseMinDependency) - 두 번째 이야기
13493정성태12/19/20232521닷넷: 2185. C# - object를 QueryString으로 직렬화하는 방법
13492정성태12/19/20232247개발 환경 구성: 699. WSL에 nopCommerce 예제 구성
13491정성태12/19/20232200Linux: 63. 리눅스 - 다중 그룹 또는 사용자를 리소스에 권한 부여
13490정성태12/19/20232296개발 환경 구성: 698. Golang - GLIBC 의존을 없애는 정적 빌드 방법
13489정성태12/19/20232057개발 환경 구성: 697. GoLand에서 ldflags 지정 방법
13488정성태12/18/20231992오류 유형: 884. HTTP 500.0 - 명령행에서 실행한 ASP.NET Core 응용 프로그램을 실행하는 방법
13487정성태12/16/20232292개발 환경 구성: 696. C# - 리눅스용 AOT 빌드를 docker에서 수행 [1]
13486정성태12/15/20232092개발 환경 구성: 695. Nuget config 파일에 값 설정/삭제 방법
13485정성태12/15/20231986오류 유형: 883. dotnet build/restore - error : Root element is missing
13484정성태12/14/20232058개발 환경 구성: 694. Windows 디렉터리 경로를 WSL의 /mnt 포맷으로 구하는 방법
13483정성태12/14/20232193닷넷: 2184. C# - 하나의 resource 파일을 여러 프로그램에서 (AOT 시에도) 사용하는 방법파일 다운로드1
13482정성태12/13/20232725닷넷: 2183. C# - eFriend Expert OCX 예제를 .NET Core/5+ Console App에서 사용하는 방법 [2]파일 다운로드1
13481정성태12/13/20232173개발 환경 구성: 693. msbuild - .NET Core/5+ 프로젝트에서 resgen을 이용한 리소스 파일 생성 방법파일 다운로드1
13480정성태12/12/20232480개발 환경 구성: 692. Windows WSL 2 + Chrome 웹 브라우저 설치
13479정성태12/11/20232206개발 환경 구성: 691. WSL 2 (Ubuntu) + nginx 환경 설정
13477정성태12/8/20232409닷넷: 2182. C# - .NET 7부터 추가된 Int128, UInt128 [1]파일 다운로드1
13476정성태12/8/20232140닷넷: 2181. C# - .NET 8 JsonStringEnumConverter의 AOT를 위한 개선파일 다운로드1
13475정성태12/7/20232200닷넷: 2180. .NET 8 - 함수 포인터에 대한 Reflection 정보 조회파일 다운로드1
13474정성태12/6/20232068개발 환경 구성: 690. 닷넷 코어/5+ 버전의 ilasm/ildasm 실행 파일 구하는 방법 - 두 번째 이야기
13473정성태12/5/20232261닷넷: 2179. C# - 값 형식(Blittable)을 메모리 복사를 이용해 바이트 배열로 직렬화/역직렬화파일 다운로드1
13472정성태12/4/20232080C/C++: 164. Visual C++ - InterlockedCompareExchange128 사용 방법
13471정성태12/4/20232129Copilot - To enable GitHub Copilot, authorize this extension using GitHub's device flow
1  2  3  4  [5]  6  7  8  9  10  11  12  13  14  15  ...