성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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#으로 다루는 MBR(Master Boot Record)</h1> <p> 최근에 트위터를 통해 공유된 다음의 자료를 읽게 되었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows MBR 분석 (MBR 분석을 통한 자동화 도구 제작 및 악성코드 분석) ; <a target='tab' href='https://drive.google.com/file/d/0B3t7Uu3cQBgYZG5TS04wVTVOVkE/view'>https://drive.google.com/file/d/0B3t7Uu3cQBgYZG5TS04wVTVOVkE/view</a> </pre> <br /> 그래서, C#으로 간단하게 MBR 내용을 보여주는 프로그램을 만들어 보았습니다.<br /> <br /> 먼저 해야 할 일이 하드 디스크를 File System을 통하지 않고 직접 읽을 수 있어야 하는데요. 이는 다음의 코드를 이용하면 쉽게 구현할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CCS LABS C#: Low Level Disk Access ; <a target='tab' href='https://code.msdn.microsoft.com/windowsapps/CCS-LABS-C-Low-Level-Disk-91676ca9'>https://code.msdn.microsoft.com/windowsapps/CCS-LABS-C-Low-Level-Disk-91676ca9</a> </pre> <a name='win32_diskdrive'></a> <br /> 이 소스 코드에서 디스크 장치 목록을 구하는 것은 다음의 코드입니다.<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.Management; // Install-Package System.Management internal class Program { static void Main(string[] args) { foreach (Win32_DiskDrive item in GetDriveList()) { Console.WriteLine(item.DeviceID); Console.WriteLine(item.Caption); Console.WriteLine(item.Description); Console.WriteLine(item.Index); Console.WriteLine(item.SerialNumber); Console.WriteLine(item.PNPDeviceID); Console.WriteLine(); } } public static List<Win32_DiskDrive> GetDriveList() { List<Win32_DiskDrive> drives = new List<Win32_DiskDrive>(); try { ManagementObjectSearcher searcher = new ManagementObjectSearcher("root\\CIMV2", "SELECT * FROM Win32_DiskDrive"); foreach (ManagementObject queryObj in searcher.Get()) { Win32_DiskDrive item = new Win32_DiskDrive(); item.DeviceID = queryObj["DeviceID"].ToString(); item.Caption = queryObj["Caption"].ToString(); item.Description = queryObj["Description"].ToString(); item.Index = (uint)queryObj["Index"]; item.SerialNumber = queryObj["SerialNumber"].ToString().Trim(); item.PNPDeviceID = queryObj["PNPDeviceID"].ToString(); drives.Add(item); } } catch (ManagementException) { return null; } return drives; } } public class Win32_DiskDrive { public string DeviceID; public string Caption; public string Description; public uint Index; public string SerialNumber; public string PNPDeviceID; } </pre> <br /> drivelist에 담겨지는 장치명은 이런 식입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > \\.\PHYSICALDRIVE2 Samsung SSD 980 PRO 1TB Disk drive 2 0025_38B8_11C6_1162. SCSI\DISK&VEN_NVME&PROD_SAMSUNG_SSD_980\5&EBD909B&0&000000 \\.\PHYSICALDRIVE1 Samsung SSD 980 PRO 1TB Disk drive 1 0025_38B8_11C6_117E. SCSI\DISK&VEN_NVME&PROD_SAMSUNG_SSD_980\5&20DA63D3&0&000000 \\.\PHYSICALDRIVE0 WDC WD20EZBX-00AYRA0 Disk drive 0 WD-WXT2AC0JRNHX SCSI\DISK&VEN_WDC&PROD_WD20EZBX-00AYRA0\4&E91BE7&0&040000 </pre> <br /> 위의 출력은 디스크가 3개 있는 경우입니다. 그다음 이 장치명을 이용해 CreateFile을 호출하면 되는데요. 이 역시 다음의 소스 코드를 이용해 간단하게 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal byte[] DumpSector(string drive, double sector, int bytesPerSector) { uint GENERIC_READ = 0x80000000; uint OPEN_EXISTING = 3; uint FILE_SHARE_READWRITE = 0x01 | 0x02; SafeFileHandle handleValue = UnsafeNativeMethods.CreateFile(drive, GENERIC_READ, FILE_SHARE_READWRITE, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); if (handleValue.IsInvalid) { Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); } double sec = sector * bytesPerSector; int size = int.Parse(bytesPerSector.ToString()); byte[] buf = new byte[size]; int read = 0; int moveToHigh; UnsafeNativeMethods.SetFilePointer(handleValue, int.Parse(sec.ToString()), out moveToHigh, EMoveMethod.Begin); ReadFile(handleValue, buf, size, out read, IntPtr.Zero); handleValue.Close(); return buf; } </pre> <br /> 위의 메서드를 이용해 MBR이 저장된 0번 섹터의 내용을 읽어들이면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > byte[] sector = DumpSector(driveName, 0, bytesPerSector); </pre> <br /> 이제 sector에 담겨진 내용을 "<a target='tab' href='https://drive.google.com/file/d/0B3t7Uu3cQBgYZG5TS04wVTVOVkE/view'>Windows MBR 분석 (MBR 분석을 통한 자동화 도구 제작 및 악성코드 분석)</a>" 문서에서 설명한 포맷에 맞게 해석만 하면 됩니다. C#이라면, 우선 다음과 같은 구조체로 출발할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential, Size = 512, Pack = 1)] public struct MBR { [MarshalAs(UnmanagedType.ByValArray, SizeConst = 446)] public byte[] BootCode; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] public PartitionEntry [] Partitions; public ushort Signature; } [StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)] public unsafe struct PartitionEntry { [FieldOffset(0)] public byte BootFlag; [FieldOffset(1)] fixed byte _startingCHSAddress[3]; public unsafe int StartingCHSAddress { get { fixed (byte* pBytes = _startingCHSAddress) { return ToInt32(pBytes); } } } [FieldOffset(4)] public byte Type; [FieldOffset(5)] fixed byte _endingCHSAddress[3]; public int EndingCHSAddress { get { fixed (byte *pBytes = _endingCHSAddress) { return ToInt32(pBytes); } } } [FieldOffset(8)] public uint StartingLBAAddress; [FieldOffset(12)] public uint SizeOfSector; unsafe int ToInt32(byte* pBytes) { byte[] intBytes = new byte[4]; intBytes[0] = *pBytes; intBytes[1] = *(pBytes + 1); intBytes[2] = *(pBytes + 2); return BitConverter.ToInt32(intBytes, 0); } } </pre> <br /> 이후 작업은 간단합니다. 디스크로부터 읽어들인 바이트 배열에서 MBR 구조체를 다음과 같이 복원해 주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > internal static bool TryParse(byte[] sector, out MBR mbr) { mbr = ByteArrayToStructure<MBR>(sector); return mbr.Signature == 0xaa55; } // which marshalling method is better? // <a target='tab' href='http://stackoverflow.com/questions/14465722/which-marshalling-method-is-better'>http://stackoverflow.com/questions/14465722/which-marshalling-method-is-better</a> static T ByteArrayToStructure<T>(byte[] bytes) where T : struct { GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); handle.Free(); return stuff; } </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;' > \\.\PHYSICALDRIVE0 Bootable = True, Type = NTFS , SectorSize = 0x000af000, CHS = 0x002120 ~ 0x2c12be, BeginLBA = 0x00000800 Bootable = False, Type = Extended, SectorSize = 0x0a6d383f, CHS = 0x2c33dd ~ 0xfffffe, BeginLBA = 0x000affc1 Bootable = False, Type = NTFS , SectorSize = 0x1356e800, CHS = 0xfffffe ~ 0xfffffe, BeginLBA = 0x0a783800 (undefined) \\.\PHYSICALDRIVE1 Bootable = False, Type = GPT(protective), SectorSize = 0xffffffff, CHS = 0x000200 ~ 0xffffff, BeginLBA = 0x00000001 (undefined) (undefined) (undefined) </pre> <br /> PHYSICALDRIVE1의 출력이 흥미로운데요. 해당 디스크가 MBR 형식이 아닌 GPT 파티션으로 되어 있기 때문에 분석이 저렇게 밖에 안된 것입니다. <br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=989&boardid=331301885'>첨부한 파일은 위의 코드를 모두 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
2341
(왼쪽의 숫자를 입력해야 합니다.)