判断应用程序是32位还是64位
阅读原文时间:2021年04月19日阅读:1

VC++检测可执行程序DLL、EXE等是32位还是64位

1.首先介绍PE结构

    Windows系统下的可执行文件,是基于Microsoft设计的一种新的文件结构,此结构被称之为PE结构。PE的意思是Portable Executable(可移植的执行体),所有Win32执行体都是用PE文件格式,其中包括SYS、DLL、EXE、COM、OCX等。(不管是学习逆向、破解还是安全,了解PE文件格式都是非常必要的。)

    PE文件的第一个部分是IMAGE_DOS_HEADER,大小为64B,这里面有两个重要的数据成员。第一个为e_magic,这个必须为MZ,即0x5A4D。当然,0x5A4D这是典型的小端格式(Little Endian);另一个重要的数据成员是最后一个成员e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移。在IMAGE_DOS_HEADER和IMAGE_NT_HEADERS之间一个DOS Stub,这段程序用于在DOS环境中显示一个字符串,“This program cannot be run in DOS mode”,现在DOS早已灭绝,这段数据由编译器生成,可以用0填充或者删除(删除的话要移动很多数据)。

IMAGE_DOS_HEADER的定义如下:

IMAGE_DOS_HEADER STRUCT
{//(注:最左边是文件头的偏移量。)
+0h WORD e_magic //Magic DOS signature MZ(4Dh 5Ah) DOS可执行文件标记
+2h WORD e_cblp //Bytes on last page of file
+4h WORD e_cp //Pages in file
+6h WORD e_crlc //Relocations
+8h WORD e_cparhdr //Size of header in paragraphs
+0ah WORD e_minalloc //Minimun extra paragraphs needs
+0ch WORD e_maxalloc //Maximun extra paragraphs needs
+0eh WORD e_ss //intial(relative)SS value DOS代码的初始化堆栈SS
+10h WORD e_sp //intial SP value DOS代码的初始化堆栈指针SP
+12h WORD e_csum //Checksum
+14h WORD e_ip //intial IP value DOS代码的初始化指令入口[指针IP]
+16h WORD e_cs //intial(relative)CS value DOS代码的初始堆栈入口
+18h WORD e_lfarlc //File Address of relocation table
+1ah WORD e_ovno //Overlay number
+1ch WORD e_res[4] //Reserved words
+24h WORD e_oemid //OEM identifier(for e_oeminfo)
+26h WORD e_oeminfo //OEM information;e_oemid specific
+29h WORD e_res2[10] // Reserved words
+3ch DWORD e_lfanew //Offset to start of PE header 指向PE文件头
} IMAGE_DOS_HEADER ENDS

用C32Asm查看一个EXE程序的结构: 

PE Header 是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里边包含着许多PE装载器用到的重要字段。IMAGE_NT_HEADERS 结构的定义: 
IMAGE_NT_HEADERS STRUCT  
{  
+0h  DWORD  Signature; 
+4h  IMAGE_FILE_HEADER  FileHeader; 
+18h IMAGE_OPTIONAL_HEADER32  OptionalHeader; 
} IMAGE_NT_HEADERS ENDS 
Signature 字段:在一个有效的 PE 文件里,Signature 字段被设置为00004550h,ASCII 码字符是“PE00”。标志这 PE 文件头的开始。“PE00” 字符串是 PE 文件头的开始,DOS 头部的 e_lfanew 字段正是指向这里,如上图所示。 

IMAGE_FILE_HEADER 结构定义: 
typedef struct _IMAGE_FILE_HEADER  

+04h // 运行平台 
+06h  WORD  NumberOfSections; // 文件的区块数目 
+08h  DWORD TimeDateStamp; // 文件创建日期和时间 
+0Ch  DWORD PointerToSymbolTable;// 指向符号表(主要用于调试) 
+10h  DWORD NumberOfSymbols; // 符号表中符号个数(同上) 
+14h  WORD  SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32 结构大小 
+16h  WORD  Characteristics;  // 文件属性 
} IMAGE_FILE_HEADER,  *PIMAGE_FILE_HEADER; 
(1)Machine:可执行文件的目标CPU类型。 
IMAGE_FILE_MACHINE_I386 0x014c  x86 
IMAGE_FILE_MACHINE_IA64 0x0200  Intel Itanium 
IMAGE_FILE_MACHINE_AMD64 0x8664 x64
(2)NumberOfSection: 区块的数目。(注:区块表是紧跟在 IMAGE_NT_HEADERS 后边的) 
(3)TimeDataStamp: 表明文件是何时被创建的。 
这个值是自1970年1月1日以来用格林威治时间(GMT)计算的秒数,这个值是比文件系统(FILESYSTEM)的日期时间更加精确的指示器。 
提示:VC的话可以用_ctime 函数或者 gmtime 函数。 
(4)PointerToSymbolTable: COFF 符号表的文件偏移位置,现在基本没用了。 
(5)NumberOfSymbols: 如果有COFF 符号表,它代表其中的符号数目,COFF符号是一个大小固定的结构,如果想找到COFF 符号表的结束位置,则需要这个变量。 
(6)SizeOfOptionalHeader: 紧跟着IMAGE_FILE_HEADER 后边的数据结构(IMAGE_OPTIONAL_HEADER)的大小。(对于32位PE文件,这个值通常是00E0h;对于64位PE32+文件,这个值是00F0h )。 
(7)Characteristics: 文件属性,有选择的通过几个值可以运算得到。( 这些标志的有效值是定义于 winnt.h 内的 IMAGE_FILE_** 的值,具体含义见下表。普通的EXE文件这个字段的值一般是 0100h,DLL文件这个字段的值一般是 210Eh。)

    2.然后我们通过读取PE文件的运行平台字段判断是32位还是64位:(这里我写的VC++6.0是Win32控制台应用程序)

#include
#include
int CrnGetImageFileMachine(char* lpFileName);
int main()
{
int n = CrnGetImageFileMachine("C:\\Users\\Administrator\\Desktop\\VS版 ADT控制卡程序 x64\\adt8948.dll");//需要检测的可执行文件
if (n == 0x014C) printf("x86\n");//32位
else if (n == 0x0200) printf("IA64\n");//纯64位
else if (n == 0x8664) printf("x64\n");//64位
else printf("未知\n");

return 1;

}
int CrnGetImageFileMachine(char* lpFileName)
{
IMAGE_DOS_HEADER idh;
FILE *f = fopen(lpFileName, "rb");
fread(&idh, sizeof(idh), 1, f);

IMAGE_FILE_HEADER ifh;
fseek(f, idh.e_lfanew + 4, SEEK_SET);
fread(&ifh, sizeof(ifh), 1, f);
fclose(f);

return ifh.Machine;

}

如何判断一个运行的程序是64位的还是32位的

有些程序是32位的,有些是64位的, 怎么用程序判断? 判断的目的是注入对应的dll, 64位程序注入64位dll, 32位程序注入32位dll.

打开目标进程 ->获取进程pbi -> pbi中找peb ->peb中找LoaderData ->LoaderData 里面有个InLoadOrderModuleList ,
这个是本进程中按次序进入内存的模块信息的链表,第一个就是当前exe ,这个模块信息中 有个BaseAddress ,就是exe虚拟内存基址-> 通过这个基址找dos_header -> dos_header中找 pe_header -> pe_header.OptionalHeader.Magic 就是要判断的 IMAGE_NT_OPTIONAL_HDR32_MAGIC or IMAGE_NT_OPTIONAL_HDR64_MAGIC

帖个代码
typedef LONG (WINAPI *PROCNTQSIP)(HANDLE,UINT,PVOID,ULON
请问:cisco交换机的端口信息都说明什么

G,PULONG);
PROCNTQSIP NtQueryInformationProcess;
NtQueryInformationProcess = (PROCNTQSIP)GetProcAddress(GetModuleHandle("ntdll"),"NtQueryInformationProcess");
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 3580);

PROCESS_BASIC_INFORMATION pbi;
PEB buf;
DWORD readnum;
PEB_LDR_DATA ldrData;

NtQueryInformationProcess(hProcess,0,(PVOID)&pbi,sizeof(PROCESS_BASIC_INFORMATION),NULL);
ReadProcessMemory(hProcess,pbi.PebBaseAddress,&buf,sizeof(PEB),&readnum);
ReadProcessMemory(hProcess,buf.LoaderData,&ldrData,sizeof(PEB_LDR_DATA),&readnum);

LDR_MODULE ldrModule;

LIST_ENTRY* pListEntry=ldrData.InLoadOrderModuleList.Flink;

ReadProcessMemory(hProcess,pListEntry,&ldrModule,sizeof(LDR_MODULE),&readnum);

LPVOID pBase=ldrModule.BaseAddress;
IMAGE_DOS_HEADER dos_header;
ReadProcessMemory(hProcess,pBase,&dos_header,sizeof(IMAGE_DOS_HEADER),&readnum);

IMAGE_NT_HEADERS pe_header;
ReadProcessMemory(hProcess,(LPVOID)((LONG)pBase + dos_header.e_lfanew),&pe_header,sizeof(IMAGE_NT_HEADERS),&readnum);

cout<<hex<< pe_header.OptionalHeader.Magic ;
cout<< IMAGE_NT_OPTIONAL_HDR32_MAGIC ;

最开始是 使用一个ntdll 里面http://www.wzlsch.com的导出函数找pbi屠龙的本事有了,却不知道怎么用刀……..参考file命令的实行。运行中的process, 还是exe文件?原帖由 芙蓉 于 2008-5-9 18:58 发表 http://bbs.chinaunix.net/images/common/back.gif
运行中http://www.tnncn.com/的process, 还是exe文件? 

运行中的.原帖由 jamesr 于 2008-5-9 18:56 发表 http://bbs.chinaunix.net/images/common/back.gif
参考file命令的实行。 

linux都学傻了吧:luya:http://dnjonline.com/article.aspx?ID=jun07_access3264
这个虽然不着边,搞不好对LZ有用呢恐怕还是要通过QuerySystemInformation来得到那个程序的磁盘映像才行。
然后读取IMAGE_OPTIONAL_HEADER来判断IMAGE_NT_OPTIONAL_HDR32_MAGIC还是IMAGE_NT_OPTIONAL_HDR32_MAGIC从目前来看,64位程序还不多,故可以先试验性地注入32位dll,如果程序出错,再注入64位,这样成功的机会不比通过判断后再注入少多少。