利用masm32输出PE文件头的基本属性

利用masm32输出PE文件头的基本属性


注:本文章作者水平较低,本文仅为作者自主实验时的学习笔记,如有不妥之处,还请指正,文中斜体皆为参考权威书籍
参考:《X86汇编语言》王爽
《加密与解密》第四版
《逆向工程核心原理》
工具:PEView,ollydbg,masm32环境

一、masm32中的INVOKE

INVOKE伪指令仅在32位的情况下使用,将参数逆序(与声明参数顺序相反)入栈,它是call的方便替代品,在一行代码内就可以传送多个参数,例如:

;原型声明
Print PROTO stdcall:DWORD,:DWORD,:DWORD,:DWORD
;.....
.code
;过程定义
Print PROC res:DWORD,blank:DWORD,blank1:DWORD,t:DWORD
    ;......
    ret
Print ENDP

在该例中,Print函数接收四个Dword类型参数:res,blank,blank1,t
其中 PROTO是原型伪指令,stdcall是调用约定,PROC和ENDP中间夹着的是过程的程序指令
1、PROTO
PROTO伪指令为现有的过程创建原型(名称和参数列表),MASM要求所有INVOKE调用的过程都有原型,所以,标准格式如下

func PROTO
;原型声明
INVOKE func
;过程调用
;实现
func PROC
;.....
func ENDP

2、调用约定
我们先来看看invoke调用过程的具体汇编代码:

.data
str1 BYTE "abcde",0
num1 DWORD 66h
func1 PROTO :DWORD,:DWORD
func2 PROTO stdcall :DWORD,:DWORD
.code

func1 PROC n1:DWORD,n2:DWORD
MOV eax,n1
ret
func1 ENDP

func2 PROC n1:DWORD,n2:DWORD
MOV eax,n2
ret
func2 ENDP

main PROC
INVOKE func1 ,num1,addr str1
INVOKE func2 ,num1,addr str1
INVOKE ExitProcess,0
main ENDP
END main

汇编以后放入OLLYDBG中在这里插入图片描述
可以看到,在INVOKE经过汇编器后,转化成了正常的函数调用,即
①将参数和返回地址入栈②将程序流程转到对应函数
在这里插入图片描述
看到函数内部,INVOKE自动帮我们完成了栈帧的建立(每个函数前两句汇编),并且
①加入leave②将ret变成retn,帮助我们清除了栈帧
下面陈述栈帧与调用约定的关系
在这里插入图片描述
①在函数调用开始前,调用函数的程序将参数(arguments)压入栈中
call指令执行,栈中压入call下一条指令的地址,并将EIP设置成函数第一条指令的地址
③那么函数是怎么描述自己参数的呢?答案是靠相对位置,栈底位置(地址)保存在ebp中,而栈顶位置保存在esp中,在新的函数中有新的临时变量和参数,程序需要利用参数和ebp相对的位置[ebp+x]描述参数或者变量所以需要更新旧的ebp。故,将last ebp入栈保存->push ebp。并将ebp设置为现在的栈底->mov ebp,esp(还没有往新的函数栈里面添加临时变量,所以栈顶栈底同位置)
④返回时,将ebp赋给esp,将栈顶瞬间缩至栈底,并且将旧ebp的值弹出至ebp,这两步在leave中实现
最后retn是将返回地址弹出给EIP,再将栈缩小操作数个字节,把栈顶调到参数之下(图上的下)
至此:栈在函数调用前后保持一致,可继续工作!
而这种由被调用函数进行栈清空的方式叫做stdcall!

(摘自百度百科)__stdcall表示
1.参数从右向左压入堆栈
2.函数被调用者修改堆栈
3.函数名(在编译器这个层次)自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸

二、PE头格式

在这里插入图片描述
从上到下从左到右除蓝黑色以外的部分分别是(图中为小端序)
IMAGE_DOS_HEADER:
e_magic: 5a4d
e_lfanew:000000e8
IMAGE_NT_HEADER:
Signature:00004550
IMAGE_FILE_HEADE:
NumberOfSections: 0003
TimeDateStamp:45d69be5
Characteristic: 010f
IMAGE_OPTIONAL_HEADER:
AddressOfEntryPoint:000073a5
ImageBase:01000000
SectionAligment:00001000
FileAligment:00000200

条件一:其中,除了蓝黑色的DOS存根以外,IMAGE_DOS_HEADER,IMAGE_NT_HEADER,IMAGE_FILE_HEADER是的长度是定长的

条件二:IMAGE_OPTINONAL_HEADER中显示的几项距离IMAGE_OPTINONAL_HEADER起始的偏移也是一定的

所以仅仅需要用CreateFile SetFilePointer ReadFile将文件读入,按照先后顺序利用基址变址寻址即可实现文件任意部分的读取
例如读入文件数据的首地址为buf,则WORD PTR [buf+esi] (esi此时为0)读取的就是4D 5A即e_magic,后只需要改变esi和取值时PTR类型即可读取文件的后续部分

解决DOS存根:在DOS头中,红色部分为e_lfanew其中存储的是NT头的文件偏移,正因为DOS存根不定长,所以需要此属性,在读取该属性后,赋给存变址的寄存器,加上基址就是程序中读入的NT头偏移了

三、读取代码

读一个文件用到的Windows API函数有CreateFile、SetFilePointer、ReadFile、CloseHandle。
CreateFile的MSDN文档地址添加链接描述

SetFilePointer函数的MSDN文档地址 添加链接描述

ReadFile函数的MSDN文档地址添加链接描述

CloseHandle函数的MSDN文档地址
添加链接描述
通过路径得到句柄,打开程序和文件的联系->通过句柄得到指向文件的指针->通过句柄读取文件内容->关闭文件和程序的连接

.386
.model flat, stdcall
option casemap:none
include D:\masm32\include\windows.inc
include D:\masm32\include\kernel32.inc
include D:\masm32\include\masm32.inc
includelib D:\masm32\lib\kernel32.lib
includelib D:\masm32\lib\masm32.lib
include D:\masm32\include\user32.inc 
includelib D:\masm32\lib\user32.lib
ExitProcess PROTO, dwEXITCODE: DWORD

.data
;待处理文件绝对路径
path BYTE "C:\Users\Em1ya\Desktop\Re\NOTE.exe",0

;存放待处理文件的句柄
filehandle DWORD ?

;以下数据全为显示字符,没有实际含义
str0 BYTE "    ",0
str1 BYTE "IMAGE_DOS_HEADER:",0
str2 BYTE "IMAGE_NT_HEADER:",0
str3 BYTE "IMAGE_FILE_HEADE:",0
str4 BYTE "IMAGE_OPTIONAL_HEADER:",0
str5 BYTE "e_magic:",0
str6 BYTE "e_lfanew:",0
str7 BYTE "Signature:",0
str8 BYTE "NumberOfSections:",0
str9 BYTE "TimeDateStamp:",0
stra BYTE "Characteristic:",0
strb BYTE "AddressOfEntryPoint:",0
strc BYTE "ImageBase:",0
strd BYTE "SectionAligment:",0
stre BYTE "FileAligment:",0
strn BYTE 0ah,00h

filebuf BYTE 4000 DUP(0)
filebase DWORD ?
var DWORD ?
;下面声明的是把十六进制值转为字符串输出的函数
Print PROTO stdcall:DWORD,:DWORD,:DWORD
Res BYTE "00000000",0
table BYTE "0123456789ABCDEF",0

.code
Print PROC res:DWORD,hexdw:DWORD,t:DWORD
    ;参数res待写缓冲区,hexdw待转换十六进制值,t数据种类,控制最后输出的字符个数
    MOV edx,0
    MOV edi,hexdw
    MOV ecx,8h
    MOV eax,res
    ;edi指向待转换的值,ecx为循环变量,eax指向待写的缓冲区字符串
    ;循环将最高位保留做与运算,拿到最低位作为索引在table里面拿值
L2:
    MOV ebx,edi
    AND ebx,0f0000000h
    SHR ebx,28
    MOV dl,BYTE PTR [table+ebx]
    MOV BYTE PTR[eax],dl
    INC eax
    SHL edi,4
LOOP L2

    ;将res的前t位变成空格
    MOV ecx,t
    MOV eax,res
L1: 
    CMP ecx,0h
    JE L999
    MOV BYTE PTR[eax],20h
    INC eax
    DEC ecx
    JMP L1
L999:
    ;输出
    INVOKE StdOut, res
    MOV eax,hexdw
    ret
Print ENDP


main PROC
INVOKE CreateFile,addr path,\
                  GENERIC_READ,\
                  FILE_SHARE_READ,\
                  0,\
                  OPEN_EXISTING,\
                  FILE_ATTRIBUTE_ARCHIVE,\
                  0
MOV filehandle,eax
INVOKE SetFilePointer, filehandle,\
                 0,\
                 0,\
                 FILE_BEGIN
INVOKE ReadFile, filehandle,\
                 addr filebuf,\
                 3900,\
                 0,\
                 0
MOV esi,OFFSET filebuf
;以上操作将文件的内容写到了filebuf,之后读取filebuf就可以拿到PE文件的内容
;以下所有,esi为目标量在内存中的地址,eax为该地址对应值,print函数把eax传入,输出对应8-t个字符串
INVOKE StdOut,addr str1
INVOKE StdOut,addr strn
MOV eax,0h
INVOKE StdOut,addr str0
INVOKE StdOut,addr str5
;读第一个量
MOV ax,WORD PTR [esi]
MOV filebase,esi
INVOKE Print,addr Res,\
             eax,\
             4
INVOKE StdOut,addr strn


ADD esi,3ch
INVOKE StdOut,addr str0
INVOKE StdOut,addr str6
;读第二个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
;此处要注意,该数据为NT头的偏移量,所以将esi调整至NT头->内存基址加上NT头偏移
ADD eax,filebase
MOV esi,eax
INVOKE StdOut,addr strn

INVOKE StdOut,addr str2
INVOKE StdOut,addr strn
INVOKE StdOut,addr str0
INVOKE StdOut,addr str7
;读第三个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn

INVOKE StdOut,addr str3
INVOKE StdOut,addr strn
ADD esi,6h
INVOKE StdOut,addr str0
INVOKE StdOut,addr str8
;读第四个量
MOV eax,0
MOV ax,WORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             4
INVOKE StdOut,addr strn

ADD esi,2h
INVOKE StdOut,addr str0
INVOKE StdOut,addr str9
;读第五个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn

ADD esi,0Eh
INVOKE StdOut,addr str0
INVOKE StdOut,addr stra
MOV eax,0
;读第六个量
MOV ax,WORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             4
INVOKE StdOut,addr strn

INVOKE StdOut,addr str4
INVOKE StdOut,addr strn
ADD esi,18
INVOKE StdOut,addr str0
INVOKE StdOut,addr strb
;读第七个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn

ADD esi,0ch
INVOKE StdOut,addr str0
INVOKE StdOut,addr strc
;读第八个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn

ADD esi,4h
INVOKE StdOut,addr str0
INVOKE StdOut,addr strd
;读第九个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn

ADD esi,4h
INVOKE StdOut,addr str0
INVOKE StdOut,addr stre
;读第十个量
MOV eax,DWORD PTR [esi]
INVOKE Print,addr Res,\
             eax,\
             0
INVOKE StdOut,addr strn
INVOKE CloseHandle,filehandle
INVOKE ExitProcess,0
main ENDP
END main

读取->转化->变址->读取
在这里插入图片描述
在这里插入图片描述
对照PEView验证
在这里插入图片描述

在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值