성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 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...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
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# - PEB(Process Environment Block)를 통해 로드된 모듈 목록 열람</h1> <p> 닷넷에서도 ProcessModule을 이용해 로드된 모듈의 목록을 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > foreach (ProcessModule pm in Process.GetCurrentProcess().Modules) { Console.WriteLine(pm.FileName + " at " + pm.BaseAddress.ToString("x")); } </pre> <br /> 심심하니까 ^^ 위의 목록을 PEB를 통해서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PEB structure ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb'>https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb</a> Anatomy of the Process Environment Block (PEB) (Windows Internals) ; <a target='tab' href='https://ntopcode.wordpress.com/2018/02/26/anatomy-of-the-process-environment-block-peb-windows-internals/'>https://ntopcode.wordpress.com/2018/02/26/anatomy-of-the-process-environment-block-peb-windows-internals/</a> </pre> <br /> 구해보겠습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 이를 위해 우선 TEB 및 그것으로부터 PEB를 구하면 됩니다. 이 작업을 대신해 주는 메서드를 만들어 놨으니 그것을 호출해 주고,<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 IntPtr pebAddress = EnvironmentBlockInfo.GetPebAddress(out IntPtr tebAddress); </pre> <br /> _PEB 구조체 정의를 마이크로소프트 공식 문서에 공개된 범위에 따라 다음과 같이 만들어 주면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // PEB structure // <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb'>https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb</a> [StructLayout(LayoutKind.Sequential)] public unsafe struct _PEB { public fixed byte Reserved1[2]; public byte BeingDebugged; public fixed byte Reserved2[1]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public IntPtr[] Reserved3; public IntPtr Ldr; public IntPtr ProcessParameters; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public IntPtr[] Reserved4; public IntPtr AtlThunkSListPtr; public IntPtr Reserved5; public uint Reserved6; public IntPtr Reserved7; public uint Reserved8; public uint AtlThunkSListPtr32; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 45)] public IntPtr[] Reserved9; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 96)] public byte[] Reserved10; public IntPtr PostProcessInitRoutine; public fixed byte Reserved11[128]; public IntPtr Reserved12; public uint SessionId; public static _PEB Create(IntPtr pebAddress) { _PEB peb = (_PEB)Marshal.PtrToStructure(pebAddress, typeof(_PEB)); return peb; } } </pre> <br /> 간단하게 로드된 이미지의 이중 연결 리스트를 가리키는 Ldr 값을 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > _PEB peb = _PEB.Create(pebAddress); Console.WriteLine("Ldr: " + peb.Ldr.ToString("x")); </pre> <br /> 마찬가지로 Ldr이 가리키는 포인터로부터 _PEB_LDR_DATA 구조체의 값을 채울 수 있고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // PEB_LDR_DATA structure // <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data'>https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data</a> [StructLayout(LayoutKind.Sequential)] public unsafe struct _PEB_LDR_DATA { public fixed byte Reserved1[8]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public IntPtr[] Reserved2; public _LIST_ENTRY InMemoryOrderModuleList; public static _PEB_LDR_DATA Create(IntPtr ldrAddress) { _PEB_LDR_DATA ldrData = (_PEB_LDR_DATA)Marshal.PtrToStructure(ldrAddress, typeof(_PEB_LDR_DATA)); return ldrData; } } [StructLayout(LayoutKind.Sequential)] public unsafe struct _LDR_DATA_TABLE_ENTRY { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public IntPtr [] Reserved1; public _LIST_ENTRY InMemoryOrderLinks; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] public IntPtr[] Reserved2; public IntPtr DllBase; public IntPtr EntryPoint; public IntPtr SizeOfImage; public _UNICODE_STRING FullDllName; public fixed byte Reserved4[8]; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] public IntPtr[] Reserved5; public IntPtr Reserved6; public uint TimeDateStamp; public static _LDR_DATA_TABLE_ENTRY Create(IntPtr memoryOrderLink) { IntPtr head = memoryOrderLink - Marshal.SizeOf(typeof(_LIST_ENTRY)); _LDR_DATA_TABLE_ENTRY entry = (_LDR_DATA_TABLE_ENTRY)Marshal.PtrToStructure( head, typeof(_LDR_DATA_TABLE_ENTRY)); return entry; } } </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;' > _PEB_LDR_DATA ldrData = _PEB_LDR_DATA.Create(peb.Ldr); IntPtr memoryOrderLink = ldrData.InMemoryOrderModuleList.Flink; Console.WriteLine("InMemoryOrderModuleList: " + memoryOrderLink.ToString("x")); _LDR_DATA_TABLE_ENTRY item = _LDR_DATA_TABLE_ENTRY.Create(memoryOrderLink); while (true) { string fullDllName = item.FullDllName.GetText(); Console.WriteLine(fullDllName); if (item.InMemoryOrderLinks.Flink == memoryOrderLink) { break; } item = _LDR_DATA_TABLE_ENTRY.Create(item.InMemoryOrderLinks.Flink); } </pre> <br /> 출력 결과는 Process.GetCurrentProcess().Modules의 것과 일치합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 기왕 해보는 김에 지난 글의 코드와 엮어서,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - 로딩된 Native DLL의 export 함수 목록 출력 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/12093'>http://www.sysnet.pe.kr/2/0/12093</a> </pre> <br /> 다음과 같이 로드 중인 "kernel32.dll"이 export한 함수 목록을 출력해 보는 것도 가능합니다.<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 WindowsPE _LDR_DATA_TABLE_ENTRY kernel32 = _LDR_DATA_TABLE_ENTRY.Find(memoryOrderLink, "kernel32.dll"); PEImage kernel32Image = PEImage.ReadFromMemory(kernel32.DllBase, (int)kernel32.SizeOfImage); foreach (var item in kernel32Image.EnumerateExportFunctions()) { Console.WriteLine("\t" + item.Name + " at " + (kernel32.DllBase + (int)item.RvaAddress).ToString("x")); } /* 출력 결과: <a target='tab' href='https://devblogs.microsoft.com/oldnewthing/20230623-00/?p=108371'>AcquireSRWLockExclusive</a> at 7ffe18eb2c6f AcquireSRWLockShared at 7ffe18eb2ca5 ActivateActCtx at 7ffe18e3e640 ActivateActCtxWorker at 7ffe18e3a950 AddAtomA at 7ffe18e41650 AddAtomW at 7ffe18e30840 ...[생략]... */ </pre> <br /> 실제로 windbg를 이용해 AddAtomA의 0x7ffe18e41650 주소를 역어셈블하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:007> <span style='color: blue; font-weight: bold'>u 7ffe18e41650</span> <span style='color: blue; font-weight: bold'>KERNEL32!AddAtomA:</span> 00007ffe`18e41650 4c8bc1 mov r8,rcx 00007ffe`18e41653 4533c9 xor r9d,r9d 00007ffe`18e41656 b101 mov cl,1 00007ffe`18e41658 33d2 xor edx,edx 00007ffe`18e4165a e919f2feff jmp KERNEL32!InternalAddAtom (00007ffe`18e30878) 00007ffe`18e4165f cc int 3 </pre> <br /> 해당 함수의 Body에 대한 코드를 볼 수 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 아래의 코드는 몇몇 도우미 함수를 WindowsPE, KernelStructOffset 라이브러리에 포함시킨 후 이 글의 예제 코드를 정리한 것입니다.<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 // Install-Package WindowsPE using KernelStructOffset; using System; using System.IO; using WindowsPE; namespace ConsoleApp1 { class Program { static void Main(string[] args) { /* foreach (ProcessModule pm in Process.GetCurrentProcess().Modules) { Console.WriteLine(pm.FileName + " at " + pm.BaseAddress.ToString("x")); } IntPtr pebAddress = EnvironmentBlockInfo.GetPebAddress(out IntPtr tebAddress); Console.WriteLine("_TEB: " + tebAddress.ToString("x")); Console.WriteLine("_PEB: " + pebAddress.ToString("x")); */ _PEB peb = EnvironmentBlockInfo.GetPeb(); _PEB_LDR_DATA ldrData = _PEB_LDR_DATA.Create(peb.Ldr); IntPtr memoryOrderLink = ldrData.InMemoryOrderModuleList.Flink; Console.WriteLine("InMemoryOrderModuleList: " + memoryOrderLink.ToString("x")); _LDR_DATA_TABLE_ENTRY kernel32 = ldrData.Find("kernel32.dll"); PEImage kernel32Image = PEImage.ReadFromMemory(kernel32.DllBase, (int)kernel32.SizeOfImage); foreach (var item in kernel32Image.EnumerateExportFunctions()) { Console.WriteLine("\t" + item.Name + " at " + (kernel32.DllBase + (int)item.RvaAddress).ToString("x")); } } } } </pre> <br /> 마지막으로 주의할 점이 하나 있는데, 이중 연결 리스트에 대한 thread-safe한 동기화 과정을 거치지 않으므로 운이 나쁘다면 링크 열거 시 잘못된 포인터 참조가 발생할 여지가 있습니다. 따라서 그냥 지식으로만 ^^ 알아두시고 일반적인 프로그램이라면 Process.GetCurrentProcess().Modules을 사용하는 것이 바람직합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1877
(왼쪽의 숫자를 입력해야 합니다.)