성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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'>해당 DLL이 Managed인지 / Unmanaged인지 확인하는 방법 - 두 번째 이야기</h1> <p> <br /> 예전에 다음과 같은 글을 쓴 적이 있었는데요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 해당 DLL이 Managed인지 / Unmanaged인지 확인하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1279'>http://www.sysnet.pe.kr/2/0/1279</a> </pre> <br /> 그때 당시에, 좀 더 개선될 수 있는 것으로 PE 파일 포맷 분석하는 방법을 남겨놓았었는데요. 혹시나, ^^ 어떤 분이 그에 관해서 글을 쓰시지 않을까 하는... 기대를 갖고 있었는데, 오늘 기준으로 아무도 안 쓰시네요. ^^; 별로 관심 사항은 아닌가 봅니다.<br /> <br /> 어쨌든, 이번에는 PE 파일을 직접 살펴보고 Managed/Unmanaged 판별을 해낼 텐데요.<br /> <br /> 사실, 그다지 어려운 문제는 아닙니다. 게다가 PE 파일 포맷에 대해 잘 설명된 책도 있고요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows 시스템 실행파일의 구조와 원리 ; <a target='tab' href='http://www.yes24.com/24/goods/1493278?scode=029'>http://www.yes24.com/24/goods/1493278?scode=029</a> </pre> <br /> 그럼, 우선 IMAGE_DOS_HEADER 먼저 구조체를 채워보는 것부터 시작하겠습니다.<br /> <br /> 책에 설명된 데로, 해당 구조체는 WinNT.h에 정의되어 있지만 여기서는 C# 에서 구현할 것이기 때문에 적절한 Interop 구조체가 필요합니다. 역시 ^^ pinvoke.net이 그 해답이겠지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > IMAGE_DOS_HEADER ; <a target='tab' href='http://www.pinvoke.net/default.aspx/Structures/IMAGE_DOS_HEADER.html'>http://www.pinvoke.net/default.aspx/Structures/IMAGE_DOS_HEADER.html</a> </pre> <br /> 위의 정의를 이용하면 간단하게 다음과 같이 IMAGE_DOS_HEADER의 내용을 채울 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))</span> { // IMAGE_DOS_HEADER를 읽어들이고, IMAGE_DOS_HEADER dosHeader = new IMAGE_DOS_HEADER(); { int dosHeaderSize = Marshal.SizeOf(dosHeader); byte[] dosHeaderBytes = new byte[dosHeaderSize]; <span style='color: blue; font-weight: bold'>fs.Read(dosHeaderBytes, 0, dosHeaderSize);</span> fixed (byte* p = dosHeaderBytes) { IntPtr ptr = new IntPtr(p); <span style='color: blue; font-weight: bold'>object objDosHeader = Marshal.PtrToStructure(ptr, typeof(IMAGE_DOS_HEADER));</span> dosHeader = (IMAGE_DOS_HEADER)objDosHeader; } if (dosHeader.isValid == false) // _e_magic == "MZ" { return false; } } } </pre> <br /> (이후의 PE 포맷에 관련된 헤더들도 모두 기본적으로는 위와 같은 구조를 따릅니다.)<br /> <br /> IMAGE_DOS_HEADER 이후에는 IMAGE_NT_HEADERS 구조체를 읽어들여야 하는데, 아쉽게도 IMAGE_NT_HEADERS 내부에 포함하고 있는 IMAGE_OPTIONAL_HEADER가 32비트/64비트 모듈에 따라 달라지므로 이 2가지를 한꺼번에 지원하기 위해 이에 대한 판단을 먼저 해야 합니다.<br /> <br /> 그러려면, 우선 'PE Signature' 바이트를 읽어서 넘어가고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // IMAGE_NT_HEADERS - signature 를 읽어들이고, { fs.Position = dosHeader.e_lfanew; byte [] ntSignature = new byte[4]; <span style='color: blue; font-weight: bold'>fs.Read(ntSignature, 0, 4);</span> // PE 헤더임을 확인 (<span style='color: blue; font-weight: bold'>IMAGE_NT_SIGNATURE == 0x00004550</span>) if (ntSignature[0] != 80 || ntSignature[1] != 69 || ntSignature[2] != 0 || ntSignature[3] != 0) { return false; } } </pre> <br /> 다행히 IMAGE_NT_HEADERS 내에 IMAGE_FILE_HEADER 구조체까지 이어서 읽어들이면 32비트/64비트 구별을 할 수 있게 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // IMAGE_NT_HEADERS - IMAGE_FILE_HEADER를 읽어들임 IMAGE_FILE_HEADER ntFileHeader = new IMAGE_FILE_HEADER(); bool is64BitHeader = false; { int ntFileHeaderSize = Marshal.SizeOf(ntFileHeader); byte[] ntFileHeaderBytes = new byte[ntFileHeaderSize]; fs.Read(ntFileHeaderBytes, 0, ntFileHeaderSize); fixed (byte* p = ntFileHeaderBytes) { IntPtr ptr = new IntPtr(p); object objNtFileHeader = Marshal.PtrToStructure(ptr, typeof(IMAGE_FILE_HEADER)); ntFileHeader = (IMAGE_FILE_HEADER)objNtFileHeader; } <span style='color: blue; font-weight: bold'> if (ntFileHeader.Machine == IMAGE_FILE_MACHINE_I386) { is64BitHeader = false; } else if (ntFileHeader.Machine == IMAGE_FILE_MACHINE_AMD64 || ntFileHeader.Machine == IMAGE_FILE_MACHINE_IA64) { is64BitHeader = true; }</span> else { return false; } } </pre> <br /> 이걸로 모든 준비는 끝났습니다. .NET DLL/EXE의 경우, IMAGE_OPTIONAL_HEADER 내부의 DataDirectory 구조체 중의 하나인 CLRRuntimeHeader 값을 포함하고 있기 때문에 그 값이 유효한지를 판단해 내면 됩니다. 다음은 32비트인 경우에 사용될 수 있는 코드입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ushort optionalHeaderSize = ntFileHeader.SizeOfOptionalHeader; if (is64BitHeader == false) { IMAGE_OPTIONAL_HEADER32 optionalHeader32 = new IMAGE_OPTIONAL_HEADER32(); byte[] optionalHeaderBytes = new byte[optionalHeaderSize]; fs.Read(optionalHeaderBytes, 0, optionalHeaderSize); fixed (byte* p = optionalHeaderBytes) { IntPtr ptr = new IntPtr(p); object objOptionalHeader = Marshal.PtrToStructure(ptr, typeof(IMAGE_OPTIONAL_HEADER32)); optionalHeader32 = (IMAGE_OPTIONAL_HEADER32)objOptionalHeader; if (optionalHeader32.Magic != MagicType.IMAGE_NT_OPTIONAL_HDR32_MAGIC || optionalHeader32.NumberOfRvaAndSizes != 16) { return false; } } return <span style='color: blue; font-weight: bold'>optionalHeader32.CLRRuntimeHeader.VirtualAddress != 0;</span> } else { ...[생략: 32비트와 유사]... } </pre> <br /> 원래 DataDirectory는 16개의 배열로 정의되는 것이 보통인데, 위의 코드에서는 C# Interop 코드에서 명시적으로 15번째 COM descriptor 요소를 CLRRuntimeHeader 속성으로 제공하고 있어 쉽게 접근하고 있습니다. 따라서, 그 값이 0이면 Native DLL이고, 그렇지 않으면 .NET DLL이 됩니다.<br /> <br /> <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=728&boardid=331301885'>첨부된 파일은 지난번 글에 포함된 소스 코드에다 이번 글의 IsManagedFromPE 메서드를 추가 구현한 것을 포함</a>하고 있습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
9820
(왼쪽의 숫자를 입력해야 합니다.)