用程序修改PE使其可显示一个消息框

该程序可从命令行提取一文件名,或出现对话框要求选择一文件,该文件必须是有效的PE可执行文件,如果该程序中曾使用动态链接库USER32.dll中的函数MessageBoxA,则向该程序中新添加一段指令,用来显示一个消息框,若未曾使用此函数,则不再修改此文件。由于本人对PE格式文件认识肤浅,程序可能不够完善,仅供初学者参考,高手看看,可提些改进意见!

本程序在运行时要修改代码段,编译方法可参考“系列11:把数据写到代码段”。

本程序不增加源程序大小,不适合对手工处理的PE文件操作。

----------------------------------------------------------------------
;文件名:19.asm
此文用老罗的代码着色器处理,在此向老罗表示感谢!
.386
.model flat,stdcall
Option CaseMap:none
include windows.inc
include kernel32.inc
include user32.inc
include comdlg32.inc
includelib kernel32.lib
includelib user32.lib
includelib comdlg32.lib
    .data
ofn       OPENFILENAME <0>           ;打开文件对话框要用到该结构
FileName    db 256 dup(0)
Caption     db 'GetCommandLine',0
FilterString  db '可执行文件(*.exe)',0,'*.exe',0,0 ;文件过滤器字符串
DialogTitle   db '请选择要打开的文件', 0
MessageTitle  db '你选择的文件',0
NoFileError   db '程序没有选择文件,不能继续进行!',0
ErrorTitle   db '警告',0
ErrorPEFormat  db '无效的PE格式文件',0
RightPEFormat  db '有效的PE格式文件',0
szDll      db 'USER32.dll',0
szFunction   db 'MessageBoxA',0
ErrorMessage  db '所选文件中不曾使用 MessageBoxA 函数!',0
CanNotModify  db '原代码段内不足以放下新增部分!',0
OkModify    db '所选程序已经被成功修改!',0
    .data?
hFile      dd ?
hMap      dd ?
pMapAddr    dd ?
ptext      dd ?
prdata     dd ?
MsgBoxA     dd ?
GetFileNameFromCommandLine  PROTO  :LPSTR
ModifyFile PROTO :LPSTR
    .code
start:
    call GetFileName  ;取文件名
    .if eax==NULL    ;无文件名
    invoke MessageBox,NULL,addr NoFileError,addr ErrorTitle,MB_ICONEXCLAMATION
    .else
    call WinMain  ;修改文件
    .endif
    invoke ExitProcess,NULL
WinMain proc
    invoke CreateFile, \         ;打开文件,该函数具有多种功能
    addr FileName,\            ;指向要打开的文件名字符串
    GENERIC_READ or GENERIC_WRITE ,\   ;打开的文件具有读写的权限
    FILE_SHARE_READ or FILE_SHARE_WRITE,\ ;别人也可读写此文件
    NULL, \                ;95下不用
    OPEN_EXISTING, \           ;要打开的文件必须存在
    FILE_ATTRIBUTE_NORMAL,\        ;文件的属性
    NULL                 ;95下必须是NULL
    .if eax!=INVALID_HANDLE_VALUE     ;判断文件是否已正常打开
    mov hFile, eax           ;保存文件句柄
    invoke CreateFileMapping, \ ;Creat File mapping Object
    hFile, \          ;Identifies the file from which to create a mapping object
    NULL, \           ;ignored
    PAGE_READWRITE, \      ;access
    0, \             ;high-order 32 bits of the maximum size
    0, \             ;low-order 32 bits of the maximum size
    NULL             ;the mapping object is created without a name
    .if eax!=NULL        ;
    mov hMap,eax       ;the return value is a handle to the file-mapping object
    invoke MapViewOfFile,hMap,FILE_MAP_WRITE,0,0,NULL ;映射文件到内存
    .if eax!=NULL
    mov pMapAddr,eax         ;保存返回的内存块首地址
    invoke ModifyFile,pMapAddr    ;修改内存块内容
    invoke UnmapViewOfFile,pMapAddr ;解除文件映射
    .endif
    invoke CloseHandle,hMap    ;关闭内存映射文件
    .endif
    invoke CloseHandle, hFile     ;关闭文件
    .endif
    ret
WinMain endp
;获取要处理的文件名
; 返回:若eax=NULL则表示没提供要处理的文件名
;    否则eax指向文件名地址
GetFileName Proc
    invoke GetFileNameFromCommandLine,addr FileName
    .if eax==NULL
    call GetFileNameFromDialog
    .endif
    ret
GetFileName endp
;返回时若eax=NULL则表示无法从命令行提取被处理的文件
GetFileNameFromCommandLine proc uses esi edi ,lpString:LPSTR
    mov edi,lpString
    invoke GetCommandLine  ;取命令行参数
    mov esi,eax
    xor eax,eax
    cld
G1:
    lodsb
    cmp al,'"'   ;程序名项是以双引号开始吗?
    jnz G3
G2:
    lodsb
    cmp al,'"'   ;遇到双引号则表示程序项名的结束
    jnz G2
G3:
    lodsb
    cmp al,' '   ;程序项名后总有一个空格
    jnz G3
G4:
    lodsb
    cmp al,0    ;无参数,转结束
    jz G7
    cmp al,' '   ;跳过无效字符,可能是空格,也可能是制表符
    jz G4
    cmp al,9
    jz G4
    cmp al,'"'   ;文件名项也是以双引号开始吗?
    jnz G5
    stosb
G4p:
    lodsb
    stosb
    cmp al,'"'   ;遇到双引号则表示文件名项的结束!
    jnz G4p
    jmp G6
G5:
    stosb
    lodsb
    cmp al,0     
    jz G6
    cmp al,' '   ;若文件名项没用双引号,则空格、制表或0表示文件名项的结束
    jz G6
    cmp al,9
    jnz G5
G6:
    xor al,al    ;以Ascii码0作为结束
    stosb
    mov eax,lpString
G7:
    ret
GetFileNameFromCommandLine endp
;显示打开对话框要求用户选择一个要处理的文件
GetFileNameFromDialog  proc
    mov ofn.lStructSize,sizeof ofn      ;结构的大小
    mov ofn.lpstrFilter,offset FilterString  ;文件过滤器
    mov ofn.lpstrFile,offset FileName     ;文件名的存放位置
    mov ofn.nMaxFile,256           ;文件名的最大长度
    mov ofn.Flags,OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_LONGNAMES
    mov ofn.lpstrTitle,offset DialogTitle   ;“打开”对话框的标题
    invoke GetOpenFileName,addr ofn      ;显示打开对话框
    .if eax!=NULL
    lea eax,FileName
    .endif
    ret
GetFileNameFromDialog  endp
;修改内存块的内容,就相当于修改文件的内容
ModifyFile proc uses ebx esi edi,lpBufferAddress:LPSTR
    mov edi,lpBufferAddress   ;取内存块地址
    call CheckPEValid
    .if eax==NULL
    invoke MessageBox,NULL,addr ErrorPEFormat,addr ErrorTitle,MB_ICONEXCLAMATION
    .else
    ;若是有效的PE,则edi中有PE头的地址
    invoke MessageBox,NULL,addr RightPEFormat,addr MessageTitle,MB_ICONINFORMATION
    call IniData    ;设置.text及.rdata的位置
    call HaveMessageBox
    .if eax!=0
    call AddCode
    .if eax==0
    invoke MessageBox,NULL,addr CanNotModify,addr ErrorTitle,MB_ICONEXCLAMATION
    .else
    invoke MessageBox,NULL,addr OkModify,addr MessageTitle,MB_ICONINFORMATION
    .endif
    .else
    invoke MessageBox,NULL,addr ErrorMessage,addr ErrorTitle,MB_ICONEXCLAMATION
    .endif
    .endif    
    ret
ModifyFile endp
;检查PE文件标记以确定其有效性
;若有效,eax指向PE头
; 否则eax=0
CheckPEValid  proc
    xor eax,eax
    assume edi:ptr IMAGE_DOS_HEADER
    .if [edi].e_magic==IMAGE_DOS_SIGNATURE   ;是否有DOS头标记
    add edi,[edi].e_lfanew
    assume edi:ptr IMAGE_NT_HEADERS
    .if [edi].Signature==IMAGE_NT_SIGNATURE ;是否有PE头标记
    mov eax,edi             ;eax指向PE头
    .endif
    .endif
    ret
CheckPEValid  endp
;检查程序中是否使用MessageBoxA函数
;返回:没用,eax=NULL
;    用,eax含存放函数入口的单元的地址
HaveMessageBox proc
    mov esi,prdata           ;esi指向section table中的 .rdata
    assume esi:ptr IMAGE_SECTION_HEADER
    mov eax,[esi].PointerToRawData   ; .rdata在文件中的绝对偏移位置
    mov ebx,[esi].VirtualAddress    ; .rdata在内存中的VRA
    sub eax,ebx
    add eax,pMapAddr  ;加上文件在内存中的起始位置
    mov ecx,eax
    assume edi:ptr IMAGE_NT_HEADERS
    add eax,[edi].OptionalHeader.DataDirectory.VirtualAddress \
    +sizeof IMAGE_DATA_DIRECTORY
    mov esi,eax   ;esi指向.rdata section
    assume esi:ptr IMAGE_IMPORT_DESCRIPTOR
Have1:
    mov eax,[esi].Name1
    or eax,eax
    jz end2               ;结束,转!
    add eax,ecx             ;对应到文件内存块中的地址
    push ecx
    invoke lstrcmp,eax,addr szDll    ;看是否引用USER32.dll库
    pop ecx
    or eax,eax
    jz Have3              ;有引用,转!
    add esi,sizeof IMAGE_IMPORT_DESCRIPTOR
    jmp Have1              ;继续测试下一个
Have3:
    call CheckFunction
end2:    ;结束,没有USER32.Dll库
    ret
HaveMessageBox endp
;esi指向IMAGE_IMPORT_DESCRIPTOR 结构
;若有MessageBoxA函数,则eax含有(存放函数入口的)单元地址
; 否则eax=0
CheckFunction  proc
    assume esi:ptr IMAGE_IMPORT_DESCRIPTOR
    mov edx,[esi].FirstThunk    ;指向(存放函数入口的)单元地址
    mov ebx,[esi].OriginalFirstThunk
    add ebx,ecx           ;ebx指向内存块中的IMAGE_THUNK_DATA结构
Check1:
    mov eax,[ebx]
    or eax,eax
    jz Check3
    add eax,ecx           ;eax指向内存块中的函数字符串
    push ecx
    push edx
    invoke lstrcmp,eax,addr szFunction ;是MessageBoxA吗?
    pop edx
    pop ecx
    or eax,eax
    jz Check2
    add ebx,4
    add edx,4
    jmp Check1
Check2:
    mov eax,edx
Check3:
    ret
CheckFunction  endp
;EDI指向 PE Header
IniData proc
    assume edi:ptr IMAGE_NT_HEADERS
    mov ebx,[edi].OptionalHeader.NumberOfRvaAndSizes
    lea esi,[edi+8*ebx].OptionalHeader.DataDirectory.VirtualAddress
;此时esi指向section table
IniData1:
    mov eax,DWORD ptr [esi]
    cmp eax,7865742eh    ;是 .text吗?
    jnz IniData2
    mov ptext,esi
    jmp IniData3
IniData2:
    cmp eax,6164722eh    ;是 .rdata吗?
    jz IniData4
IniData3:
    add esi,sizeof IMAGE_SECTION_HEADER
    jmp IniData1
IniData4:
    mov prdata,esi
    ret
IniData endp
;将代码加到原程序中!
AddCode proc
    mov esi,ptext
    assume esi:ptr IMAGE_SECTION_HEADER
    call ModifyCode         ;修改新增的指令部分
    assume edi:ptr IMAGE_NT_HEADERS
    mov eax,[esi].Misc.VirtualSize ;取原指令的长度
    mov ebx,[esi].PointerToRawData ;指令在文件中的绝对位置
    add ebx,eax           ;指向原指令后
    add ebx,pMapAddr        ;内存中的该位置
    add eax,FirstRunLen       ;加上新指令的长度
    cmp eax,[esi].SizeOfRawData ;和调整后的Section长度相比
    ja AddCode1           ;放不下新加的指令,结束
    push eax
    mov eax,[edi].OptionalHeader.BaseOfCode       ;原代码开始处
    add eax,[esi].Misc.VirtualSize           ;指向新的入口
    mov [edi].OptionalHeader.AddressOfEntryPoint,eax  ;修改指令入口指向我们的程序
    pop eax
    mov [esi].Misc.VirtualSize,eax ;设置新的代码段长度
    mov ecx,FirstRunLen       ;新指令的长度
    cld
    mov esi,offset FirstRun     ;指向新怎指令
    mov edi,ebx           ;设置文件内存块中的目标地址
    rep movsb            ;将新增指令加到原指令后
    ret
AddCode1:
    xor eax,eax   ;没能修改原程序,返回eax=0
    ret
AddCode endp
;eax含入口地址
ModifyCode proc
    assume edi:ptr IMAGE_NT_HEADERS
    add eax,[edi].OptionalHeader.ImageBase     ;调整eax的值 
    mov MustModify1,eax               ;设置MessageBoxA的入口地址
    mov eax,[edi].OptionalHeader.AddressOfEntryPoint;原程序的入口地址VRA
    mov edx,[edi].OptionalHeader.BaseOfCode     ;计算新加程序后第一个字节的VRA
    add edx,[esi].Misc.VirtualSize
    add edx,FirstRunLen
    sub eax,edx                   ;段内直接寻址
    mov MustModify2,eax               ;修改此值以转向原指令处继续执行
    ret
ModifyCode endp
;这是加到其他程序中要首先执行的部分
FirstRun proc
    push MB_ICONINFORMATION     ;消息框中的图标
    call FirstRun1         ;消息框的标题
    db '怜香欢迎您',0
FirstRun1:
    call FirstRun2         ;消息框的文字内容
    db '这是程序被修改后首先显示的信息!',0
FirstRun2:
    push NULL
MustModify1 equ this dword+2    ;要修改此处以使其可正常执行MessageBoxA函数
    call MsgBoxA
MustModify2 equ this dword+1    ;要修改此处以使其可转到原程序去执行
    jmp start
FirstRun  endp
FirstRunLen equ $-FirstRun     ;计算新加代码的长度
    end start
------------------------------------------------------------------------------

后记:

为编本程序,把MM都给气哭啦,好几天都不理我,说我心理没有她,难呀,男人!

时间: 2024-10-31 10:00:59

用程序修改PE使其可显示一个消息框的相关文章

c#弹出一个消息框,3秒后自动消失

问题描述 c#弹出一个消息框,3秒后自动消失 发现c#中MessageBox没有构造函数,无法生成对象,就无法销毁对象,请问如何实现这个功能? 解决方案 http://nxhujiee.blog.163.com/blog/static/298444220155238351302/http://www.cnblogs.com/ap0606122/archive/2012/10/23/2735325.htmlhttp://blog.csdn.net/huangshunle/article/detai

用vbscript实现在消息框中显示一个超链接_vbs

问: 嗨,Scripting Guy!可以在消息框中包含指向某个 Web 页面的超链接吗? -- CB 答: 嗨,CB.听起来您好像想要向用户显示一个消息框,并且希望在消息框中包含一个超链接,以便用户可以点击这个链接,从而获得更多信息.例如,假设某个用户试图访问一些资源,但被拒绝了.这种情况下,您可以弹出一个消息框,告诉用户访问被拒绝,然后向他们提供一个链接,这个链接指向的页面会告诉用户访问所需的权限.一般说来,这种办法会比简单地说"访问被拒绝"好得多. 那么,是否可以用脚本编写语言实

关于微信开发,jsp页面上弹出一个文本框

问题描述 关于微信开发,jsp页面上弹出一个文本框 微信开发不支持 prompt ,怎么样点击一个按钮然后弹出一个输入框?求大神帮助 解决方案 给按钮就一个click事件,,里面动态生成一个文本框就行了,,, 解决方案二: 是弹出一个文本框还是显示一个文本框?如果是显示一个文本框那就使用js,加入一个点击事件,动态生成或事先预备好文本框都可以.如果是弹出文本框的效果,那就可以采用第三方插件bootstrap或jqueryUI去实现

android-安卓后台下载图片失败放一个弹出消息框的问题

问题描述 安卓后台下载图片失败放一个弹出消息框的问题 安卓后台下载,类似百度音乐有一个进度显示在通知里面,下载完成显示一个消息.需要用服务实现么? 解决方案 参考:http://download.csdn.net/detail/lixiaodaoaaa/8311831 解决方案二: 需要 service更新通知栏进度http://blog.csdn.net/liuhe688/article/details/6623924

html app 安卓 苹果-如果做一个html页面通过app来访问,怎么样使app的显示能够同时适配在安卓和苹果系统?

问题描述 如果做一个html页面通过app来访问,怎么样使app的显示能够同时适配在安卓和苹果系统? 如果做一个html页面通过app来访问,怎么样使app的显示能够同时适配在安卓和苹果系统? 解决方案 可以使用bootstrap来构建你的web app.它支持响应式布局,是开发现代化web的好框架. 解决方案二: 有没有其他方式呢?我不会bootstrap啊 解决方案三: jquery mobile 可以 解决方案四: <!doctype html> 武当七侠 武当七侠 2015-07-17

如何使MDI父窗体调用子窗体(子窗体同时只能显示一个)

问题描述 如题,有多少种方法 解决方案 解决方案二: 6种方法使MDI父窗体调用子窗体(子窗体同时只能显示一个)解决方案三: 就楼主这小气样,想不通还有人回答你问题

在你的MFC应用程序中显示一个JPG文件

在VB中,我可以通过创建一个图像控件来显示一个JPG或GIF文件,但是我如何在我的MFC应用程序中显示一个JGP文件呢? 好问题!有时使用VB的程序员觉得这个很容易.只要往你的表中拖入一个图像控件,然后你就可以往下做了--然而C++程序员就不得不感到烦恼和头疼.那我们要做些什么呢,编写我们自己的JPG解压函数吗? 当然不是这样的!事实上,C/C++程序员能够使用与VB程序员所使用的非常类似(可以说是差不多)的图像控件.我并没有开玩笑.VB图像控件是基于一个叫"IPicture"的系统C

树形菜单上的值从xml文件中读取就只能显示一个树形菜单

问题描述 树形菜单上的值从xml文件中读取就只能显示一个树形菜单 为什么树形菜单在前台设置他的项就可以显示多个树形菜单,但是使树形菜单上的值从xml文件中读取就只能显示一个树形菜单 解决方案 是什么树形菜单,在什么开发环境下使用的呢?是不是xml文件中读取到的数据不全导致的呢?

跪求 帮忙-VC++6.0 环境下,怎么能够使程序运行后的黑框直接变成全屏显示

问题描述 VC++6.0 环境下,怎么能够使程序运行后的黑框直接变成全屏显示 VC++6.0 环境下,win32 console application下编一个什么样的程序能够使输出的黑框变成全屏?例如 #include main() { printf("你好n"); } 这个简单程序中添加上哪些代码就能够使输出的黑框直接占满整个电脑屏幕?麻烦哪位大侠给帮个忙,并把修改后的完整程序附上,小弟不胜感激!