본문 바로가기

개발/C#

특정 파일 Machine Type이 X32인지 X64인지 확인하는 방법

목적

진행중인 x64dbg Refactoring 작업 중 C#을 이용한 코드를 이용하여 Debugee을 로드하면 Debugee의 Machine Type이 64인지 32인지 확인하여 알맞은 버전의 디버거(x64dbg 또는 x32dbg)를 실행시켜야 했다. 

이에 방법을 찾아보게 되었다.

 

배경 및 알고가야 할 기본 

윈도우에서의 실행 파일을 PE(Portable Executable)파일이라고 한다.

전체적으로 PE헤더와 PE바디 부분으로 나눠져 있다.

 

PE헤더를 자세히 보자면

  • IMAGE_DOS_HEADER는 PE파일의 여부를 알려주는 시그니처 (MZ = 5A4D)
  • IMAGE_NT_HEADERS는 파일이 실행되는 플랫폼을 나타내준다.

자세한 사항은 내가 참고한 아래 페이지를 확인하자

 

m.blog.naver.com/PostView.nhn?blogId=wsi5555&logNo=221259767736&proxyReferer=https:%2F%2Fwww.google.com%2F

 

[ Reversing ] PE 파일 기본 구조

1. PE 파일 기본 구조 윈도우에서는 실행파일을 PE(Portable Executable)파일이라고 합니다. UNI...

blog.naver.com

 

구현방법

인터넷으로 찾아본 결과 실행중이 아닌 파일의 Machine Type을 C#에서 Programmatic하게 확인하는 유일한 방법은 파일의 BinaryReader을 불러 File Header를 확인하는 방법 밖에 없었다.

 

아래는 마이크로소프트 공식 홈페이지에서 퍼온 Machine Type 리스트이다.

IMAGE_FILE_MACHINE_UNKNOWN 0x0 The content of this field is assumed to be applicable to any machine type
IMAGE_FILE_MACHINE_AM33 0x1d3 Matsushita AM33
IMAGE_FILE_MACHINE_AMD64 0x8664 x64
IMAGE_FILE_MACHINE_ARM 0x1c0 ARM little endian
IMAGE_FILE_MACHINE_ARM64 0xaa64 ARM64 little endian
IMAGE_FILE_MACHINE_ARMNT 0x1c4 ARM Thumb-2 little endian
IMAGE_FILE_MACHINE_EBC 0xebc EFI byte code
IMAGE_FILE_MACHINE_I386 0x14c Intel 386 or later processors and compatible processors
IMAGE_FILE_MACHINE_IA64 0x200 Intel Itanium processor family
IMAGE_FILE_MACHINE_M32R 0x9041 Mitsubishi M32R little endian
IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16
IMAGE_FILE_MACHINE_MIPSFPU 0x366 MIPS with FPU
IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 with FPU
IMAGE_FILE_MACHINE_POWERPC 0x1f0 Power PC little endian
IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC with floating point support
IMAGE_FILE_MACHINE_R4000 0x166 MIPS little endian
IMAGE_FILE_MACHINE_RISCV32 0x5032 RISC-V 32-bit address space
IMAGE_FILE_MACHINE_RISCV64 0x5064 RISC-V 64-bit address space
IMAGE_FILE_MACHINE_RISCV128 0x5128 RISC-V 128-bit address space
IMAGE_FILE_MACHINE_SH3 0x1a2 Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP 0x1a3 Hitachi SH3 DSP
IMAGE_FILE_MACHINE_SH4 0x1a6 Hitachi SH4
IMAGE_FILE_MACHINE_SH5 0x1a8 Hitachi SH5
IMAGE_FILE_MACHINE_THUMB 0x1c2 Thumb
IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 MIPS little-endian WCE v2
출처 : https://docs.microsoft.com/en-us/windows/win32/debug/pe-format

 

테스트 코드 작성

using System.IO;

// 모든 Machine Type을 Enum으로 등록
public enum MachineType : ushort
{
    IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
    IMAGE_FILE_MACHINE_AM33 = 0x1d3,
    IMAGE_FILE_MACHINE_AMD64 = 0x8664,
    IMAGE_FILE_MACHINE_ARM = 0x1c0,
    IMAGE_FILE_MACHINE_EBC = 0xebc,
    IMAGE_FILE_MACHINE_I386 = 0x14c,
    IMAGE_FILE_MACHINE_IA64 = 0x200,
    IMAGE_FILE_MACHINE_M32R = 0x9041,
    IMAGE_FILE_MACHINE_MIPS16 = 0x266,
    IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
    IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
    IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
    IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
    IMAGE_FILE_MACHINE_R4000 = 0x166,
    IMAGE_FILE_MACHINE_SH3 = 0x1a2,
    IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
    IMAGE_FILE_MACHINE_SH4 = 0x1a6,
    IMAGE_FILE_MACHINE_SH5 = 0x1a8,
    IMAGE_FILE_MACHINE_THUMB = 0x1c2,
    IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
}


private void button1_Click(object sender, EventArgs e)
{
  //MessageBox.Show(Environment.Is64BitProcess.ToString());

  var fileContent = string.Empty;
  var filePath = string.Empty;

  using (OpenFileDialog openFileDialog = new OpenFileDialog())
  {
    openFileDialog.InitialDirectory = "c:\\";
    openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
    openFileDialog.FilterIndex = 2;
    openFileDialog.RestoreDirectory = true;

    if (openFileDialog.ShowDialog() == DialogResult.OK)
    {
      //Get the path of specified file
      filePath = openFileDialog.FileName;
    }
  }

  switch (isMachineType64(filePath))
  {
    case null:
      MessageBox.Show("Unknown!");
      break;
    case true:
      MessageBox.Show("64");
      break;
    case false:
      MessageBox.Show("32");
      break;
  }
}

// return
// -1 if file is neither 32 nor 64
// 0 if file is 64 bit
// 1 if file is 32 bit
private static MachineType GetMachineTypeFromFile(string filePath)
{
	MachineType machineType;

	using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
	using (var binaryReader = new BinaryReader(fileStream))
	{
		// if (binaryReader.BaseStream.Length == 0) = if file length is zero 
		// if (binaryReader.ReadUInt16() != 0x5a4d) = if it's a valid image("MZ")
		if (binaryReader.BaseStream.Length == 0 || binaryReader.ReadUInt16() != 0x5a4d)
		{
			return 0x0;    //file size is zero OR invalid image("MZ")
		}

		fileStream.Position = 60; // this location contains the offset for the PE header
		var peOffset = binaryReader.ReadUInt32();

		fileStream.Position = peOffset + 4; //contains the architecture
		machineType = (MachineType) binaryReader.ReadUInt16();
	}
	return machineType;
 }

  private static bool? isMachineType64(string filePath)
  {
    switch (GetMachineTypeFromFile(filePath))
    {
      case MachineType.IMAGE_FILE_MACHINE_AMD64:  // if machine type is 64
      case MachineType.IMAGE_FILE_MACHINE_IA64:   // if machine type is 64
      	return true;
      case MachineType.IMAGE_FILE_MACHINE_I386:   // if machine type is 32
      	return false;
      default:    // all other types
      	return null;
    }
  }
}        

 

출처1 : https://www.neowin.net/forum/topic/732648-check-if-exe-is-x64/
출처2 : https://stackoverflow.com/questions/1001404/check-if-unmanaged-dll-is-32-bit-or-64-bit?lq=1