성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>windbg - 메모리 덤프로부터 DateTime 형식의 값을 알아내는 방법</h1> <p> 예를 들어, 다음과 같이 DateTime을 사용하는 프로그램의<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; namespace ConsoleApp1 { class Program { DateTime _now = DateTime.Now; DateTime _utcNow = DateTime.UtcNow; DateTime _uninitialized; static void Main(string[] args) { Program pg = new Program(); Console.ReadLine(); } } } </pre> <br /> 메모리 덤프를 windbg에 로드하면 객체의 DateTime 필드를 다음과 같이 접근할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!DumpObj /d</span> 030230dc Name: ConsoleApp1.Program MethodTable: 01174d4c EEClass: 011713c4 Size: 32(0x20) bytes File: C:\temp\ConsoleApp1\bin\Debug\ConsoleApp1.exe Fields: MT Field Offset Type VT Attr Value Name <span style='color: blue; font-weight: bold'>5c75f748</span> 4000001 4 System.DateTime 1 instance <span style='color: blue; font-weight: bold'>030230e0 _now</span> <span style='color: blue; font-weight: bold'>5c75f748</span> 4000002 c System.DateTime 1 instance <span style='color: blue; font-weight: bold'>030230e8 _utcNow</span> <span style='color: blue; font-weight: bold'>5c75f748</span> 4000003 14 System.DateTime 1 instance <span style='color: blue; font-weight: bold'>030230f0 _uninitialized</span> </pre> <br /> 그다음, 개별적으로 값을 덤프할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>!DumpVC /d</span> 5c75f748 030230e0 Name: System.DateTime MethodTable: 5c75f748 EEClass: 5c2fe6c4 Size: 16(0x10) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 5c75d1d0 40002ce 0 System.UInt64 1 instance <span style='color: blue; font-weight: bold'>9859791360354292701 dateData</span> 5c763bc8 40002bf 68 System.Int32[] 0 shared static DaysToMonth365 >> Domain:Value 011eb430:0302315c << 5c763bc8 40002c0 6c System.Int32[] 0 shared static DaysToMonth366 >> Domain:Value 011eb430:0302319c << 5c75f748 40002c1 60 System.DateTime 1 shared static MinValue >> Domain:Value 011eb430:04021088 << 5c75f748 40002c2 64 System.DateTime 1 shared static MaxValue >> Domain:Value 011eb430:0402108c << 0:000> <span style='color: blue; font-weight: bold'>!DumpVC /d</span> 5c75f748 030230e8 Name: System.DateTime MethodTable: 5c75f748 EEClass: 5c2fe6c4 Size: 16(0x10) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 5c75d1d0 40002ce 0 System.UInt64 1 instance <span style='color: blue; font-weight: bold'>5248105017926914794 dateData</span> ...[생략]... 0:000> <span style='color: blue; font-weight: bold'>!DumpVC /d</span> 5c75f748 030230f0 Name: System.DateTime MethodTable: 5c75f748 EEClass: 5c2fe6c4 Size: 16(0x10) bytes File: C:\WINDOWS\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll Fields: MT Field Offset Type VT Attr Value Name 5c75d1d0 40002ce 0 System.UInt64 1 instance <span style='color: blue; font-weight: bold'>0 dateData</span> ...[생략]... </pre> <br /> 리플렉션으로 보면, ulong dateData 필드가 DateTime 타입의 유일한 인스턴스 데이터임을 알 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Serializable, __DynamicallyInvokable] public struct DateTime : IComparable, IFormattable, IConvertible, ISerializable, IComparable<DateTime>, IEquatable<DateTime> { // Fields <span style='color: blue; font-weight: bold'>private ulong dateData;</span> ...[생략]... // Methods ...[생략]... [__DynamicallyInvokable] <span style='color: blue; font-weight: bold'>public DateTime(long ticks)</span> { if ((ticks < 0L) || (ticks > 0x2bca2875f4373fffL)) { throw new ArgumentOutOfRangeException("ticks", Environment.GetResourceString("ArgumentOutOfRange_DateTimeBadTicks")); } <span style='color: blue; font-weight: bold'>this.dateData = (ulong) ticks;</span> } ...[생략]... } </pre> <br /> 따라서, (예제 코드의 _uninitialized 멤버처럼) dateData 값이 0이라는 점은 값 형식에 한 번도 값이 할당된 적이 없는 것입니다. 그리고 각각 UTC, Local에 대해 다음과 같이 dateData 값이 할당됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > dateData == 5248105017926914794 (UTC) dateData == 9859791360354292701 (Local) </pre> <br /> 이 값으로부터 어떻게 원래 시간 정보를 알 수 있을까요? DateTime 생성자에 보면 인자로 전달한 값(ticks)을 그대로 dateData 변수에 대입하는 것을 볼 수 있는데요. 실제로 그렇게 해보면 0x2bca2875f4373fffL 한계에 걸려 ArgumentOutOfRangeException 예외가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.ArgumentOutOfRangeException: Ticks must be between DateTime.MinValue.Ticks and DateTime.MaxValue.Ticks. Parameter name: ticks at System.DateTime..ctor(Int64 ticks) at ConsoleApp1.Program.Main(String[] args) </pre> <br /> 9859791360354292701, 5248105017926914794 값은 0x2bca2875f4373fff(10진수 3155378975999999999)값보다 크기 때문인데, 이유를 알기 위해 좀 더 추적해 보면 DateTime.Now에서 단서를 찾을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public static DateTime Now { [__DynamicallyInvokable] get { DateTime utcNow = UtcNow; bool isAmbiguousLocalDst = false; long ticks = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utcNow, out isAmbiguousLocalDst).Ticks; long num2 = utcNow.Ticks + ticks; if (num2 > 0x2bca2875f4373fffL) { return new DateTime(0x2bca2875f4373fffL, DateTimeKind.Local); } if (num2 < 0L) { return new DateTime(0L, DateTimeKind.Local); } <span style='color: blue; font-weight: bold'>return new DateTime(num2, DateTimeKind.Local, isAmbiguousLocalDst);</span> } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [__DynamicallyInvokable] public DateTime(long ticks, DateTimeKind kind) { if ((ticks < 0L) || (ticks > 0x2bca2875f4373fffL)) { throw new ArgumentOutOfRangeException("ticks", Environment.GetResourceString("ArgumentOutOfRange_DateTimeBadTicks")); } if ((kind < DateTimeKind.Unspecified) || (kind > DateTimeKind.Local)) { throw new ArgumentException(Environment.GetResourceString("Argument_InvalidDateTimeKind"), "kind"); } <span style='color: blue; font-weight: bold'>this.dateData = (ulong) (ticks | (((long) kind) << 0x3e));</span> } </pre> <br /> 보는 바와 같이 DateTime.dateData의 값은 원래의 시간 정보를 담은 ticks 값에 DateTimeKind 값을 OR 연산한 값이기 때문입니다.<br /> <br /> 따라서, 해당 값의 DateTimeKind를 알 수 있다면 다음과 같이 변환해서 원래의 시간 정보를 알아낼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > long u = 5248105017926914794; long mask = (long)DateTimeKind.Utc; // 또는 DateTimeKind.Local <span style='color: blue; font-weight: bold'>u = (u ^ (mask << 0x3e));</span> DateTime dt = new DateTime(u, (DateTimeKind)mask); Console.WriteLine(dt); </pre> <br /> 결국 DateTimeKind 값에 의해 dateData 값이 바뀌므로 이를 자동화하면 다음과 같은 식으로 코딩할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static DateTime ToDateTime(long u) { bool tryGetDateTime(long ticks, DateTimeKind kind, out DateTime result) // 로컬 함수 C# 7.0 { result = DateTime.MinValue; long checkMask = (long)kind << 0x3e; long masked = ticks & checkMask; if (masked == checkMask) { result = new DateTime(ticks ^ checkMask, kind); return true; } return false; }; switch (u) { // 패턴 매칭 C# 7.0 case long value when tryGetDateTime(value, DateTimeKind.Utc, out DateTime time) == true: // out 사용 개선 C# 7.0 return time; case long value when tryGetDateTime(value, DateTimeKind.Local, out DateTime time) == true: // out 사용 개선 C# 7.0 return time; } return new DateTime(u, DateTimeKind.Unspecified); } </pre> <br /> 이를 이용해 windbg에서 알아낸 값을 다음과 같이 친숙한 시간 값으로 바꿔줄 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > long u = 5248105017926914794; DateTime dt = ToDateTime(u); Console.WriteLine(dt); // 2017-09-25 오전 1:32:29 u = (long)9859791360354292701; dt = ToDateTime(u); Console.WriteLine(dt); // 2017-09-25 오전 10:32:29 </pre> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, DateTime 생성자를 통해 dateData 값을 넣어줄 필요 없이 Reflection을 이용하는 방법도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법 // ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11312'>http://www.sysnet.pe.kr/2/0/11312</a> unchecked { long u = 5248105017926914794; DateTime dt = new DateTime(); FieldInfo fi = typeof(DateTime).GetField("dateData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); TypedReference tr = __makeref(dt); fi.SetValueDirect(tr, (ulong)u); Console.WriteLine(dt); // 2017-09-25 오전 1:32:29 u = (long)9859791360354292701; fi.SetValueDirect(tr, (ulong)u); Console.WriteLine(dt); // 2017-09-25 오전 10:32:29 } </pre> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1172&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1090
(왼쪽의 숫자를 입력해야 합니다.)