修改PE入口点方式注入自己编写的DLL——《英雄无敌》1代小回城术修改成大回城术

news/2024/12/23 12:59:04/文章来源:https://www.cnblogs.com/dark-f/p/18450877

一、前言

《英雄无敌》每代都有回城术,而第一代的回城术则是所谓的“小回城术”,就是到达的城堡不能选择。到了《英雄无敌》2代和3代,都有大小回城术了,而大回城术可以使英雄回到己方的任意一座没有自己的英雄所在的城堡,有了这样的魔法,使得英雄能兼顾整个地图,是每个玩家都是首要得到的!可惜的是,到了《英雄无敌》四代,回城术又回到了小回城术并且限定只有会秩序类魔法的英雄才能拥有,极大地增加了完成地图的难度和时间!以前我有发过把二代里的小回城术修改成大回城术的博文:
https://www.cnblogs.com/dark-f/p/18432634
这次我将要把《英雄无敌》一代的小回城术,改成大回城术。具体方法包括:
1)设计一个DLL,就是一个由一个下拉框组成的对话框
2)把这个DLL注入到《英雄无敌》一代里
本次修改是在这2个文章基础上进行的:
https://www.cnblogs.com/dark-f/p/18316105
https://www.cnblogs.com/dark-f/p/18299637

二、下拉框对话框

这个设计很简单,语言选择的是masm32。

给DLL取名Towngate,下面直接上代码。
Towngate.asm:
`;********************************************************************
.386
.model flat, stdcall
option casemap :none
;********************************************************************
; Include 文件定义
include windows.inc
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
include gdi32.inc
includelib gdi32.lib
;********************************************************************
; Equ 等值定义
DLG_MAIN equ 1
IDC_COMBOTEXT equ 101
;********************************************************************
; 数据段
.data?
hInstance dd ?
dwCount dd ?
dwTown dd ?

		.const

pTable dd sz1,sz2,sz3,sz4,sz5,sz6,sz7,sz8,sz9,sz10,
sz11,sz12,sz13,sz14,sz15,sz16,sz17,sz18,sz19,sz20
sz1 db '1',0
sz2 db '2',0
sz3 db '3',0
sz4 db '4',0
sz5 db '5',0
sz6 db '6',0
sz7 db '7',0
sz8 db '8',0
sz9 db '9',0
sz10 db '10',0
sz11 db '11',0
sz12 db '12',0
sz13 db '13',0
sz14 db '14',0
sz15 db '15',0
sz16 db '16',0
sz17 db '17',0
sz18 db '18',0
sz19 db '19',0
sz20 db '20',0
;********************************************************************
; 代码段
.code
;********************************************************************
; dll 的入口函数
DllEntry proc _hInstance,_dwReason,_dwReserved
mov eax,_dwReason
.if eax == DLL_PROCESS_ATTACH
push _hInstance
pop hInstance
.endif
mov eax,TRUE
ret
DllEntry endp
;********************************************************************
_DlgProc proc uses ebx edi esi _hWnd,_wMsg,_wParam,_lParam
local @nCount:dword

	mov	eax,_wMsg.if	eax == WM_CLOSEinvoke	EndDialog,_hWnd,NULL.elseif	eax == WM_INITDIALOG

;********************************************************************
; 初始化组合框
mov esi,0
push dwCount
pop @nCount
dec @nCount
mov ebx, offset pTable
@@: mov edi, [ebx + esi*4]
invoke SendDlgItemMessage,_hWnd,IDC_COMBOTEXT,CB_ADDSTRING,0,edi
.if esi == @nCount
jmp @F
.endif
inc esi
jmp @B
@@: invoke SendDlgItemMessage,_hWnd,IDC_COMBOTEXT,CB_SETCURSEL,0,0
;********************************************************************
.elseif eax == WM_COMMAND
mov eax,_wParam
.if ax == IDOK
invoke EndDialog,_hWnd,NULL
;********************************************************************
; 演示处理下拉式组合框
.elseif ax == IDC_COMBOTEXT
shr eax,16
.if ax == CBN_SELENDOK
invoke SendDlgItemMessage,_hWnd,IDC_COMBOTEXT,CB_GETCURSEL,0,0
mov dwTown, eax
.endif
.endif
;********************************************************************
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret

_DlgProc endp
;********************************************************************
GetTown proc _dwCount
push _dwCount
pop dwCount
invoke DialogBoxParam,hInstance,DLG_MAIN,NULL,offset _DlgProc,NULL
mov eax, dwTown
ret
GetTown endp
;********************************************************************
End DllEntryTowngate.def:EXPORTS
GetTownTowngate.rc:/********************************************************************/

include <resource.h>

/********************************************************************/

define DLG_MAIN 1

define IDC_COMBOTEXT 101

/********************************************************************/
DLG_MAIN DIALOG 100, 100, 200, 100
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
FONT 12, "Comic Sans MS"
{
/FONT 14 "Segoe Script"/
CTEXT "TOWN GATE",-1,50,0,100,30
CTEXT "Select a town to port to",-1, 50,20,100,30
COMBOBOX IDC_COMBOTEXT, 50, 45, 100, 70, CBS_DROPDOWNLIST | WS_TABSTOP
PUSHBUTTON "OK", IDOK, 75, 70, 50, 14
}Makefile文件:NAME = Towngate
OBJS = $(NAME).obj
RES = $(NAME).res
DEF = $(NAME).def

LINK_FLAG = /subsystem:windows /Dll
ML_FLAG = /c /coff

$(NAME).dll: $(OBJS) $(RES) $(DEF)
Link $(LINK_FLAG) /Def:$(DEF) $(OBJS) $(RES)
.asm.obj:
ml $(ML_FLAG) $<
.rc.res:
rc $<`
然后就可以编译链接成dll了(为了确保dll有效,已经用应用程序测试可以加载调用)。

三、修改PE入口点方式注入DLL

这种注入方式的优点非常明显:
1)这种注入方式,不怕dll升级与修改,即修改dll无需再去修改PE
2)无需繁琐的导入表重定位操作与计算
注入方法只是很简单的三步:
第一步:在pe文件的空白处输入dll文件名称字符串,并push 地址
第二步:call LoadLibraryA
第三步:跳回原pe入口点
最后修改PE的入口点为上面第一步push所在命令行地址即可。下面以《英雄无敌》1代注入Towngate.dll看具体操作:
用OD打开程序就会停在程序入口点,这个点也可以使用PE类程序查看的,本贴使用的是Explorer Suite(也就是CFF Explorer)

在程序的尾部有很多空白,选定0048B0C0处作为新的入口点

先在0047B0A4的地方输入字符串Towngate.dll,这是dll的文件名

再在0047B0B1的地方输入GetTown,这是dll中的输出函数名

好,下面就可以按照上面的三步注入dll。在0048B0C0处,依次输入上面三步,结果如下图

保存修改

用CFF Explorer打开刚修改后的程序,可以看到此时程序的入口点是在826C0处(即4826C0)

将其修改为8B0C0,并保存

运行一下游戏,看看是否可以正常运行。可以正常运行,说明入口点的修改没有问题,下面就是修改程序代码的过程了。

四、回城术代码研读

下面是关键部分,也就是回城术是怎么选择城堡的
004351C5 |. E8 67C80100 call HEROES.00451A31 ;dark-f: it's failed 004351CA |. 83C4 24 add esp,24 004351CD |. E9 2C020000 jmp HEROES.004353FE 004351D2 |> C745 EC 00000000 mov [local.5],0 ;dark-f: initial 004351D9 |. E9 03000000 jmp HEROES.004351E1 ;===>E9 F25E0500-->0048B0D0 004351DE |> FF45 EC /inc [local.5] ;[ebp-14],loop=2,loc.5=1;2 004351E1 |> A1 B0244C00 mov eax,dword ptr ds:[4C24B0] ;dark-f: 026D0254 ASCII "Player 1" 004351E6 |. 0FBE40 56 |movsx eax,byte ptr ds:[eax+56] ;dark-f: town number 004351EA |. 3B45 EC |cmp eax,[local.5] 004351ED |. 0F8E 96000000 |jle HEROES.00435289 ;dark-f: end loop, when scaning town No. = town No. 004351F3 |. 8B45 EC |mov eax,[local.5] ;dark-f: loop No.,loop=2,eax=1;2 004351F6 |. 8B0D B0244C00 |mov ecx,dword ptr ds:[4C24B0] ;026D0254 ASCII "Player 1" 004351FC |. 0FBE4408 59 |movsx eax,byte ptr ds:[eax+ecx+59] ;town ID: 03,0C;0B;02;0A;01;08;0;06;05;09;04;07 00435201 |. 8BC8 |mov ecx,eax 00435203 |. 8D0480 |lea eax,dword ptr ds:[eax+eax*4] ;ID*5 00435206 |. 8D0441 |lea eax,dword ptr ds:[ecx+eax*2] ;ID+ID*5*2 00435209 |. 8D0480 |lea eax,dword ptr ds:[eax+eax*4] ;(ID+ID*5*2)*5 0043520C |. 8B0D 486D4C00 |mov ecx,dword ptr ds:[4C6D48] ;[4C6D48]=XXXX0048 00435212 |. 0FBE8408 A5210100 |movsx eax,byte ptr ds:[eax+ecx+121A5] ;dark-f: town X-coordination 13;29;42 0043521A |. 8B4D FC |mov ecx,[local.1] ;hero's address 0043521D |. 0FBE49 1E |movsx ecx,byte ptr ds:[ecx+1E] ;[026E 3107]=FFFF0E32,hero X-coordination,ecx=32(12) 00435221 |. 2BC1 |sub eax,ecx ;dark-f: for calculating distance delta? eax=13-32=E1;F7;10 00435223 |. 50 |push eax 00435224 |. E8 67C60400 |call HEROES.00481890 ;get absolute value, eax=1F, edx=FFFFFFFF;eax=09;10 00435229 |. 83C4 04 |add esp,4 0043522C |. 8BD8 |mov ebx,eax ;dark-f: keep eax into ebx(delta X), ebx=1F;09;10 0043522E |. 8B45 EC |mov eax,[local.5] ;0;1;2 00435231 |. 8B0D B0244C00 |mov ecx,dword ptr ds:[4C24B0] 00435237 |. 0FBE4408 59 |movsx eax,byte ptr ds:[eax+ecx+59] ;town ID: 03;0C;0B 0043523C |. 8BC8 |mov ecx,eax 0043523E |. 8D0480 |lea eax,dword ptr ds:[eax+eax*4] 00435241 |. 8D0441 |lea eax,dword ptr ds:[ecx+eax*2] 00435244 |. 8D0480 |lea eax,dword ptr ds:[eax+eax*4] 00435247 |. 8B0D 486D4C00 |mov ecx,dword ptr ds:[4C6D48] 0043524D |. 0FBE8408 A6210100 |movsx eax,byte ptr ds:[eax+ecx+121A6] ;town Y-coordination 3E;45;3A 00435255 |. 8B4D FC |mov ecx,[local.1] 00435258 |. 0FBE49 1F |movsx ecx,byte ptr ds:[ecx+1F] ;hero Y-coordination 0E(40) 0043525C |. 2BC1 |sub eax,ecx ;3E-0E=30;45-0E=37;3A-0E=2C 0043525E |. 50 |push eax 0043525F |. E8 2CC60400 |call HEROES.00481890 ;eax=30;37;2C 00435264 |. 83C4 04 |add esp,4 00435267 |. 03D8 |add ebx,eax ;distance=delta X + delta Y, ebx=4F;40;3C 00435269 |. 895D F8 |mov [local.2],ebx ;loc.2 keeps the result.;40;3C 0043526C |. 8B45 F0 |mov eax,[local.4] ;loc.4=3E4, 3E4=996 is a limit?;4F;40 0043526F |. 3945 F8 |cmp [local.2],eax ;keep min distance, ebx=4F<loc.4=3E8;40<4F;3C<40 00435272 |. 0F8D 0C000000 |jge HEROES.00435284 00435278 |. 8B45 F8 |mov eax,[local.2] ;eax=4F;40;3C 0043527B |. 8945 F0 |mov [local.4],eax ;if loc.2 < 3E4, loc.4=loc.2, loc.4(min distance)=4F;40;3C 0043527E |. 8B45 EC |mov eax,[local.5] ;loc.5=0;1;2 00435281 |. 8945 F4 |mov [local.3],eax ;loc.3 min distance town ID , if loc.2<3E4, loc.3=loc.5, loc.3=0;1;2 00435284 |>^ E9 55FFFFFF \jmp HEROES.004351DE ;dark-f: this procedure gets the nearest town, loop all towns 00435289 |> 837D F4 FF cmp [local.3],-1 ;loc.3=-1, no available town (07) 0043528D |. 0F85 1D000000 jnz HEROES.004352B0 ;dark-f: loc.3 != -1, has a town to portal
可以看出,上面的小回城术是根据英雄所在位置坐标,和己方所有城堡坐标作距离计算,得到最小的那个距离,就是英雄要回到的城堡。其中关键的数据有3个:player1的首地址,是存在[4C24B0],而己方城堡数量,是存在上面那个地址偏移56的地方[eax+56],再就是选中的城堡是存在[local.3](即[ebp-C])里的。
搞清楚了回城术的应用流程,修改就简单了,只要在跳入对每个城堡循环前,就将程序跳转到自己的代码那里,然后把所要去的城堡付给[local.3]即可。
因此,将程序中的这个跳转,改到跳到修改代码所在地址即可

五、修改代码

再看看修改的代码怎么写(下图红框为入口点的三行代码,再下面的就是)?

大意就是:得到dll的句柄-->检查句柄是否为NULL,如果是NULL,让程序回到原有的小回城术流程上去-->得到导出函数入口地址-->检查入口地址是否为NULL,若是同样返回原有程序的小回城术流程-->得到己方城堡数量,作为调用导出函数的参数,调用导出函数-->将导出函数的返回值传递给[local.3]-->最后跳转到原路径的00435289处。
修改后的程序代码在X32dbg中的样子(红框中三行是改变入口点的):

六、检验效果

游戏中,回城术的使用方法是:
1 己方城堡从第一个开始,依次1、2、3、4下去,记住英雄要去的城堡是第几个?

2 在下拉框中就选中第几个(注意:即使是列表中的第一个,也要用鼠标选中一次)

3 点击OK就行了,下面看看实测结果

七、程序包(dll和已经修改的运行程序)下载

https://wwzd.lanzoup.com/ixvLM2bwucqj

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/809717.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Architecture 1001: x86-64 Assembly 汇编

编程语言心法参考:http://www.yinwang.org/blog-cn/2017/07/06/master-pl 英语阅读速成:http://www.yinwang.org/blog-cn/2018/11/23/grammar 前置条件 必须熟悉 C 编程。 https://www.learn-c.org/ https://www.edx.org/certificates/professional-certificate/dartmouth-im…

《机器学习》 学习记录 - 第二章

好多看不懂的高数内容。。。 第2章 模型评估与选择 2.1 经验误差与过拟合错误率(error rate):分类错误的样本数占样本总数的比例。 若在m个样本中有a个样本分类错误,则错误率\(E=a/m\); 而常说的 精度 则等于\(1-a/m\),即 “精度=1-错误率” ,常写为百分比形式。训练误差(…

git push代码失败,鉴权失败

github 无法push 代码 1、确保设置了用户名和邮箱 git config --global user.name "mars" git config --global user.email "mars3603@163.com" 2、修改 .git/config 中的URL,将https的方式修改为 ssh https方式:url = https://github.com/Mars3603/grpc…

织物图像的配准和拼接算法的MATLAB仿真,对比SIFT,SURF以及KAZE

1.算法运行效果图预览 (完整程序运行后无水印)SIFT: surf:kaze: 2.算法运行软件版本 MATLAB2022a3.部分核心程序 (完整版代码包含注释和操作步骤视频)img1 = imread(Images\F1.jpg); img2 = imread(Images\F2.jpg); figure; subplot(121); imshow(img1); title(原始图片1)…

06.OpenFeign接口调用

1.提问 1.1 已经有RestTemplate + LoadBalancer的方式进行服务的调用,为什么还要有OpenFeign? 因为OpenFeign的功能更强大,和使用更便携。 1.2 使用那个? 推荐使用OpenFeign 2.OpenFeign是什么 2.1 官网翻译 https://docs.spring.io/spring-cloud-openfeign/reference/spri…

Hadoop单机模式

1.安装JDK 1.1 下载解压 tar zxf jdk-8u151-linux-x64.tar.gz -C /usr/local/src mv jdk-8u151-linux-x64 java1.2 添加环境变量 export JAVA_HOME=/usr/local/src/java export PATH=$PATH:$JAVA_HOME/binsource /etc/prifile java -version2.安装Hadoop 1.1 下载解压 tar zxf …

mysql读写分离的最佳实践

一. 传统的读写分离方式 在 MySQL 中实现读写分离可以通过以下几种方式来达到目的: 1. 主从复制 使用主从复制(Master-Slave Replication)是实现读写分离的常见方式。主库:处理所有的写入操作(INSERT、UPDATE、DELETE)。 从库:负责处理读操作(SELECT)。步骤:设置主从…

《机器学习初步》笔记 第一章

第一章 绪论 1.1 引言 机器学习的经典定义:利用经验(数据)改善系统自身的性能 经典的机器学习过程:机器学习最重要的理论模型:PAC(概览近似正确)1.2 基本术语 数据集:一组记录的集合 学习/训练:通过执行某个学习算法,得到模型,学的的模型对应数据的某种潜在规律 示例…

XTimer定时微服务项目

定时微服务项目,分布式架构下的定时器Xtimer定时微服务 项目背景 在学校社团中,有给社团成员发送活动通知的任务需求 有定期执行某项任务的需求,比如每周末举办一次线下活动,每个月举行一次团建 再比如,我有一个任务需要设置定时发布 定时微服务调研对比方案 不足点JavaTi…

集合竞价如何看强弱?

买股票前先把集合竞价搞懂 一、集合竞价介绍 1. 什么是集合竞价 所谓集合竞价是指在股票每个交易日上午9:15—9:25,由投资者按照自己所能接受的心理价格自由地进行买卖申请。详情可参考下图:注:严谨的集合竞价时间为9:15-9:25,下文在聊到集合竞价时候我会把9:25-9:30时间…

ECS云服务器搭建Mysql,并提供公网连接

搭建mysql8.0参考:https://blog.csdn.net/ShockChen7/article/details/126965940 搭建完成之后,使用navicat发现连接不上,发现是因为安全组没有设置:安全组需要开放3306端口。 百度参考: 然后我改了下安全组就可以供外部连接了。