성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
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'>C# - 디버깅 중인 프로세스를 강제로 다른 디버거에서 연결하는 방법</h1> <p> 윈도우 운영체제의 경우 특정 프로세스에 디버거는 하나만 연결이 됩니다. (또는, <a target='tab' href='http://www.sysnet.pe.kr/2/0/12116'>배타적으로 다른 디버거가 연결</a>할 수는 있습니다.) 그래서 다음 글에서 설명하는 것과 같은,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Debug Blocker ; <a target='tab' href='https://kblab.tistory.com/311'>https://kblab.tistory.com/311</a> </pre> <br /> 디버깅 방해가 가능한 것입니다. 그런데, 이때 디버거와의 통신을 위해 debuggee 측에서는 특별하게 "DebugPort"를 열게 되는데요, 바로 이 값이 "EPROCESS" 구조체에 DebugPort라는 필드에 저장되어 있습니다. <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:006> <span style='color: blue; font-weight: bold'>dt _EPROCESS</span> ntdll!_EPROCESS +0x000 Pcb : _KPROCESS +0x2e0 ProcessLock : _EX_PUSH_LOCK +0x2e8 UniqueProcessId : Ptr64 Void +0x2f0 ActiveProcessLinks : _LIST_ENTRY +0x300 RundownProtect : _EX_RUNDOWN_REF ...[생략]... +0x418 ObjectTable : Ptr64 _HANDLE_TABLE <span style='color: blue; font-weight: bold'>+0x420 DebugPort : Ptr64 Void</span> +0x428 WoW64Process : Ptr64 _EWOW64PROCESS ...[생략]... +0x868 ParentSecurityDomain : Uint8B +0x870 CoverageSamplerContext : Ptr64 Void +0x878 MmHotPatchContext : Ptr64 Void </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;' > Anti Revering Techniques [zer0day].pdf ; <a target='tab' href='http://cfile7.uf.tistory.com/attach/21611F5057EC7DCD2FC7C8'>http://cfile7.uf.tistory.com/attach/21611F5057EC7DCD2FC7C8</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> 이 기법은 간단하게 말하면 자기 자신은 디버깅 하는 기법으로, 자식 프로세스를 만들고 그 프로세스가 부모 프로세스를 디버깅 하는 방식으로 이뤄진다. 한번에 한 프로세스를 디버깅 할 수 있는 사실을 이용해 외부 디버거가 디버깅을 하지 못하게 할 수 있는 좋은 기법 중 하나다. <span style='color: blue; font-weight: bold'>하지만 이 기법의 단점은 EPROCESS 구조체의 DebugPort 값을 0 으로 설정을 하면 우회가 가능하다.</span> </div><br /> <br /> 그냥 DebugPort를 덮어쓰면 된다고 나옵니다. 어디 직접 한 번 해볼까요? ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 아쉽게도 EPROCESS 영역은 커널 메모리라서 KernelMemoryIO 드라이버의 힘을 빌려야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - KernelMemoryIO 드라이버를 이용해 실행 프로그램을 숨기는 방법(DKOM: Direct Kernel Object Modification) ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12111'>http://www.sysnet.pe.kr/2/0/12111</a> </pre> <br /> 이것을 이용하면 다음과 같이 간단하게 DebugPort 필드의 값을 읽을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // Install-Package KernelStructOffset using (KernelMemoryIO memoryIO = new KernelMemoryIO()) { if (memoryIO.IsInitialized == false) { Console.WriteLine("Failed to open device"); return; } Console.WriteLine($"Cid offset: {ethreadOffset["Cid"]:x}"); // Check that access is valid { IntPtr clientIdPtr = ethreadOffset.GetPointer(ethreadPtr, "Cid"); // windows 10 1909 == 0x648; _CLIENT_ID cid = memoryIO.ReadMemory<_CLIENT_ID>(clientIdPtr); if (cid.Pid != processId) { Console.WriteLine($"PID: {cid.Pid} ({cid.Pid:x})"); Console.WriteLine("Invalid access"); } } IntPtr processPtr = kthreadOffset.GetPointer(ethreadPtr, "Process"); IntPtr eprocessPtr = memoryIO.ReadMemory<IntPtr>(processPtr); <span style='color: blue; font-weight: bold'>IntPtr debugPortPtr = eprocessOffset.GetPointer(eprocessPtr, "DebugPort"); IntPtr debugPortValue = memoryIO.ReadMemory<IntPtr>(debugPortPtr);</span> Console.WriteLine($"DebugPort: {debugPortValue.ToInt64():x}"); Console.ReadLine(); } </pre> <br /> 실제로 위의 프로그램을 (단독으로) 실행시켜 보면 "DebugPort" 값이 0으로 나옵니다. 반면, Visual Studio 환경에서 F5 키를 눌러 디버깅을 시작하면 "DebugPort: ffffd60dc0d1ff40"와 같은 식으로 출력이 됩니다.<br /> <br /> DebugPort에 값이 있는 상황에서 또 다른 디버거, 예를 들어 windbg로 연결하려고 하면 0xC0000048 오류가 출력됩니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Could not attach to process 70856, NTSTATUS 0xC0000048<br /> <br /> The process that you are attempting to attach to <span style='color: blue; font-weight: bold'>is already being debugged</span>. Only one debugger can be invasively attached to a process at a time. <span style='color: blue; font-weight: bold'>A non-invasive attach is still possible</span> when another debugger is attached. </div><br /> <br /> (물론 <a target='tab' href='https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/noninvasive-debugging--user-mode-'>non-invasive attach 방식으로 연결</a>할 수 있지만) 그래도 다음과 같이 DebugPort의 값을 0으로 만들어 버리면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > memoryIO.WriteMemory<IntPtr>(debugPortPtr, IntPtr.Zero); </pre> <br /> 이후, windbg에서 아무런 오류 없이 attach가 됩니다. 대신 기존 연결된 디버거는 아무런 동작도 하지 않으며 windbg가 "qd" 명령어로 detach시켜도 DebugPort 값이 해제되었기 때문에 기존 디버거는 영원히 제어권을 잃어버리게 됩니다.<br /> <br /> 이런 의미에서 봤을 때 "하지만 이 기법의 단점은 EPROCESS 구조체의 DebugPort 값을 0으로 설정을 하면 우회가 가능하다."라는 것은 별로 의미가 없을 수 있습니다. 왜냐하면, 기존 디버거 입장에서는 더 이상 debuggee와 통신이 안 되는 것을 금방 알 수 있으므로 또 다른 조치를 취할 수 있는 여지가 다분하기 때문입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1533&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 재미 삼아서 ^^ DebugPort 값을 0이 아닌 임의의 숫자를 넣으면 어떻게 될까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > memoryIO.WriteMemory<IntPtr>(debugPortPtr, <span style='color: blue; font-weight: bold'>new IntPtr(1)</span>); </pre> <br /> 그럼 "쓰자마자" "Stop code: SYSTEM SERVICE EXCEPTION"의 BSOD 화면이 뜨는 것을 볼 수 있습니다. 부팅 후 남겨진 이벤트 로그는 다음과 같고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Log Name: System Source: Microsoft-Windows-WER-SystemErrorReporting Date: 2020-01-15 오전 01:24:27 Event ID: 1001 Task Category: None Level: Error Keywords: Classic User: N/A Computer: TESTPC Description: The computer has rebooted from a bugcheck. The bugcheck was: 0x0000003b (0x00000000c0000005, 0xfffff80042639320, 0xfffff4052db698e0, 0x0000000000000000). A dump was saved in: C:\WINDOWS\MEMORY.DMP. Report Id: e7c3b934-9a67-4e72-bd66-ce935a0c3492. </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Log Name: System Source: Microsoft-Windows-WER-SystemErrorReporting Date: 2020-01-15 오전 01:24:56 Event ID: 1018 Task Category: None Level: Information Keywords: Classic User: N/A Computer: TESTPC Description: The dump file at location: C:\WINDOWS\MEMORY.DMP was deleted because the disk volume had less than 25 GB free space. </pre> <br /> "C:\WINDOWS\MEMORY.DMP"라고 알려주는 덤프 파일은 아쉽게도 디스크 용량 부족으로 삭제되었고 대신 미니 덤프 파일을 "C:\Windows\Minidump" 경로에서 "011520-534078-01.dmp" 파일로 남겨진 것을 볼 수 있습니다.<br /> <br /> 이를 windbg에서 로드해 "!analyze -v"로 분석하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 3: kd> <span style='color: blue; font-weight: bold'>!analyze -v</span> ******************************************************************************* * * * Bugcheck Analysis * * * ******************************************************************************* SYSTEM_SERVICE_EXCEPTION (3b) An exception happened while executing a system service routine. Arguments: Arg1: 00000000c0000005, Exception code that caused the bugcheck Arg2: fffff80042639320, Address of the instruction which caused the bugcheck Arg3: fffff4052db698e0, Address of the context record for the exception that caused the bugcheck Arg4: 0000000000000000, zero. Debugging Details: ------------------ ...[생략]... DUMP_TYPE: 2 BUGCHECK_P1: c0000005 BUGCHECK_P2: fffff80042639320 BUGCHECK_P3: fffff4052db698e0 BUGCHECK_P4: 0 EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s. FAULTING_IP: nt!ExAcquireFastMutex+100 fffff800`42639320 f00fba3700 lock btr dword ptr [rdi],0 CONTEXT: fffff4052db698e0 -- (.cxr 0xfffff4052db698e0) rax=0000000000000000 rbx=0000000000000001 rcx=ffffb48fa3673080 rdx=0000000000000019 rsi=ffffb48fa3673400 rdi=0000000000000019 rip=fffff80042639320 rsp=fffff4052db6a2d0 rbp=0000000000000001 r8=fffff4052db6a328 r9=0000000000000000 r10=fffff80042ccd110 r11=ffffb48fa390a308 r12=0000000000000019 r13=ffffb48fa1692080 r14=fffff4052db6a360 r15=ffffb48fa3673080 iopl=0 nv up ei pl zr na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00050246 nt!ExAcquireFastMutex+0x100: fffff800`42639320 f00fba3700 lock btr dword ptr [rdi],0 ds:002b:00000000`00000019=???????? Resetting default scope BUGCHECK_STR: 0x3B_c0000005 ...[생략]... CPU_MICROCODE: 6,5e,3,0 (F,M,S,R) SIG: FFFFFFFF'00000000 (cache) FFFFFFFF'00000000 (init) BLACKBOXBSD: 1 (!blackboxbsd) BLACKBOXNTFS: 1 (!blackboxntfs) BLACKBOXPNP: 1 (!blackboxpnp) BLACKBOXWINLOGON: 1 CUSTOMER_CRASH_COUNT: 1 DEFAULT_BUCKET_ID: WIN8_DRIVER_FAULT PROCESS_NAME: ConsoleApp2.exe CURRENT_IRQL: 1 ANALYSIS_SESSION_HOST: TESTPC ANALYSIS_SESSION_TIME: 01-15-2020 10:36:35.0843 ANALYSIS_VERSION: 10.0.18362.1 amd64fre LAST_CONTROL_TRANSFER: from fffff80042e4a231 to fffff80042639320 STACK_TEXT: fffff405`2db6a2d0 fffff800`42e4a231 : 00000000`00000000 00000000`00000000 00000000`00000001 00000000`00000000 : nt!ExAcquireFastMutex+0x100 fffff405`2db6a320 fffff800`42e4ba00 : ffffe301`ccfb9180 00000000`00000000 ffffb48f`a3673180 00000000`00000000 : nt!DbgkpQueueMessage+0x1b9 fffff405`2db6a520 fffff800`42e4c33d : 00000000`00000000 fffff405`2db6a869 00000000`00000000 fffff800`3ebb55a0 : nt!DbgkpSendApiMessage+0xa4 fffff405`2db6a570 fffff800`42dcae11 : ffffb48f`00000000 00000000`00000000 00000000`00000000 ffffb48f`a1692360 : nt!DbgkExitThread+0x8d fffff405`2db6a6c0 fffff800`42ccd143 : ffffb48f`00000000 00000119`a455d200 00000000`00000000 fffff405`2db6a8b4 : nt!PspExitThread+0x16cd49 fffff405`2db6a7d0 fffff800`4263e551 : 00000000`00000000 fffff800`42bf0fde ffff43cc`cce36257 fffff800`00000002 : nt!KiSchedulerApcTerminate+0x33 fffff405`2db6a810 fffff800`427c5a60 : ffffb48f`a3683c01 fffff405`2db6a8d0 ffffb48f`a41fd090 00000000`00000000 : nt!KiDeliverApc+0x481 fffff405`2db6a8d0 fffff800`427d2dbf : 00000000`0000017c ffffe301`ccfb9180 fffff405`2db6aa00 00000000`00000000 : nt!KiInitiateUserApc+0x70 fffff405`2db6aa10 00007ff8`43c7fa54 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSystemServiceExit+0x9f 0000000c`1b7ff448 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : 0x00007ff8`43c7fa54 THREAD_SHA1_HASH_MOD_FUNC: b5f1f2dcacd920241e342a8fe3877e894c43e7e5 THREAD_SHA1_HASH_MOD_FUNC_OFFSET: dddc76a8a315a318b1c64e11f185ac97b3e96ad4 THREAD_SHA1_HASH_MOD: 9f457f347057f10e1df248e166a3e95e6570ecfe FOLLOWUP_IP: nt!ExAcquireFastMutex+100 fffff800`42639320 f00fba3700 lock btr dword ptr [rdi],0 FAULT_INSTR_CODE: 37ba0ff0 SYMBOL_STACK_INDEX: 0 SYMBOL_NAME: nt!ExAcquireFastMutex+100 FOLLOWUP_NAME: MachineOwner MODULE_NAME: nt IMAGE_NAME: ntkrnlmp.exe DEBUG_FLR_IMAGE_TIMESTAMP: 12dcb470 IMAGE_VERSION: 10.0.18362.535 STACK_COMMAND: .cxr 0xfffff4052db698e0 ; kb BUCKET_ID_FUNC_OFFSET: 100 FAILURE_BUCKET_ID: 0x3B_c0000005_nt!ExAcquireFastMutex BUCKET_ID: 0x3B_c0000005_nt!ExAcquireFastMutex PRIMARY_PROBLEM_CLASS: 0x3B_c0000005_nt!ExAcquireFastMutex ...[생략]... </pre> <br /> 딱히 DebugPort를 건드려서 발생했을 것 같은 단서는 찾을 수 없지만... 혹시나 싶어 다시 시도해 보면 여지없이 DebugPort에 0이 아닌 값을 쓰는 순간 BSOD를 보게 됩니다. ^^<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
4936
(왼쪽의 숫자를 입력해야 합니다.)