昨天使用古老的按键精灵写个脚本,用来给老叶的海盗王挂船熟,每隔一个小时都要补一次船实在太麻烦了。
好吧,古老的游戏,和古老的工具。一般很少上游戏了,也不想打装备,不想升级装备,没有人玩船,所以我就升升船。
使用湾湾工具Game Master获得坐标字符串的内存地址,就可以自动跑船了。但是很快又发现了一个大问题,每次运行Game.exe,当前位置坐标和船只补给的内存地址都会变?!真是防到家了。
脚本贴出来(VB风格):
1 [General] 2 Description=碎冰船熟 3 BeginHotkey=120 4 BeginHotkeyMod=2 5 PauseHotkey=0 6 PauseHotkeyMod=0 7 StopHotkey=123 8 StopHotkeyMod=0 9 RunOnce=1 10 EnableWindow= 11 Enable=1 12 AutoRun=0 13 [Repeat] 14 Type=0 15 Number=1 16 [Relative] 17 SetupOCXFile= 18 [Comment] 19 Content= 20 21 [Script] 22 VBS Dim curShape,comShape,attShape,nowayShape,peopleShape,npcShape,skillShape,pickupShape 23 VBS Dim Addr1,Addr2,M1,M2,M3 24 VBS Dim BasescrX,BasescrY,CenterX,CenterY 25 VBS Dim M,N,zbd0x,zbd0y,zbd_1x,zbd_1y,zbd1x,zbd1y,zbd2x,zbd2y,zbd3x,zbd3y 26 VBS Dim curSupply,protectSupply 27 VBS Dim r,i,kstep,basekstep,span,x0,y0,x,y,k 28 //Addr1 位置坐标的内存地址,Addr2 补给的内存地址 29 Addr1 = &H00696C48:Addr2 = &H0D4BD20C 30 //Addr1 = &H00626C48:Addr2 = &H0E57413C 31 M1=1:M2="":M3=Addr1 32 BasescrX=0:BasescrY=0 33 CenterX=474:CenterY=352 34 zbd0x=2255:zbd0y=1085 35 zbd_1x=2257:zbd_1y=1094 36 zbd1x=2272:zbd1y=1070 37 zbd2x=2306:zbd2y=1075 38 zbd3x=2301:zbd3y=1100 39 zbd4x=2299:zbd4y=1117 40 xzb=0:yzb=0 41 curSupply=100:protectSupply=50 42 comShape = 528446959 43 attShape = 1848294001 44 nowayShape = 1458522284 45 peopleShape = 921605627 46 npcShape = 1482209781 47 skillShape = 1157927434 48 pickupShape = 119364435 49 J=1:basekstep=0.25:span=12 50 Gosub getWinBasePos 51 M=zbd0x:N=zbd0y 52 Gosub MoveToMN 53 For 1000 54 Plugin File.WriteINI("Command", "Val1", "进入主循环:J=" & J, ".\qqnc_debugtext.ini") 55 Gosub ZDGongji 56 Gosub getSupply 57 Gosub 返回挂点 58 Plugin File.WriteINI("Command", "Val1", "主循环完成一轮:J=" & J, ".\qqnc_debugtext.ini") 59 Delay 1000 60 J=J+1 61 EndFor 62 Plugin File.WriteINI("Command", "Val1", "脚本停止运行!", ".\qqnc_debugtext.ini") 63 Plugin File.WriteINI("Command", "Val2", "", ".\qqnc_debugtext.ini") 64 Plugin File.WriteINI("Command", "Val3", "", ".\qqnc_debugtext.ini") 65 Plugin File.WriteINI("Command", "Val4", "", ".\qqnc_debugtext.ini") 66 Plugin File.WriteINI("Command", "Val5", "", ".\qqnc_debugtext.ini") 67 EndScript 68 Sub getInMem 69 M2="" 70 VBSCall ReadMemory(M3,0,M1) 71 While M1 > 0 72 M2 = M2 & Chr(M1) 73 M3 = M3 + 1 74 VBSCall ReadMemory(M3,0,M1) 75 EndWhile 76 Return getInMem 77 Sub Czb 78 tmpStr1="":tmpStr2="" 79 VBS ipo=InStr(M2,",") 80 VBS tmpStr1=Left(M2,ipo-1) 81 VBS tmpStr2=Replace(M2,tmpStr1 & ",","") 82 VBS xzb=Clng(tmpStr1):yzb=Clng(tmpStr2) 83 //xzb & "," & yzb & ";" 84 Return Czb 85 Sub Csp 86 tmpStr1="":tmpStr2="" 87 VBS ipo=InStr(M2,"/") 88 VBS tmpStr1=Left(M2,ipo-1) 89 VBS tmpStr2=Replace(M2,tmpStr1 & "/","") 90 VBS curSupply=Clng(tmpStr1) 91 Return Csp 92 Sub getXYzb 93 M3=Addr1 94 Gosub getInMem 95 Gosub Czb 96 Return getXYzb 97 Sub getWinBasePos 98 dim hwnd,wndClientPos,tmpstr1,tmpstr2,tmpstr3,iwin 99 Plugin hwnd=Window.Foreground() 100 Plugin wndClientPos=Window.GetClientRect(hwnd) 101 VBS iwin=InStr(wndClientPos,"|") 102 VBS tmpstr1=Left(wndClientPos,iwin-1) 103 VBS tmpstr3=Replace(wndClientPos,tmpStr1 & "|","") 104 VBS iwin=InStr(tmpstr3,"|") 105 VBS tmpstr2=Left(tmpstr3,iwin-1) 106 VBS BasescrX=Clng(tmpStr1):BasescrY=Clng(tmpStr2) 107 CenterX=BasescrX+466:CenterY=BasescrY+322 108 Return getWinBasePos 109 Sub MoveToMN 110 Plugin File.WriteINI("Command", "Val5", "MoveToMN,M=" & M & ",N=" & N & ",M2:" & M2, ".\qqnc_debugtext.ini") 111 MoveTo CenterX,CenterY 112 Delay 200 113 VBSCall Dim xzb,yzb,u,v,w,cxpos,cypos 114 Gosub getXYzb 115 u=M-xzb:v=N-yzb 116 w=Sqr(u*u+v*v) 117 If abs(u)>=5 Or abs(v)>=5 118 cxpos=CenterX+u*180/w 119 cypos=CenterY+v*180/w 120 MoveTo cxpos,cypos 121 Delay 1000 122 LeftClick 1 123 Delay 100 124 LeftClick 1 125 Delay 1000 126 Gosub MoveToMN 127 ElseIf Abs(u)>=2 Or Abs(v)>=2 128 Delay 3500 129 MoveR u*23,v*23 130 Delay 200 131 LeftClick 1 132 Delay 4500 133 Gosub MoveToMN 134 EndIf 135 Plugin File.WriteINI("Command", "Val5", "完成MoveToMN:" & M & "," & N ";", ".\qqnc_debugtext.ini") 136 Return MoveToMN 137 Sub MoveAboutMN 138 Plugin File.WriteINI("Command", "Val5", "MoveAboutMN,M=" & M & ",N=" & N & ",M2:" & M2, ".\qqnc_debugtext.ini") 139 MoveTo CenterX,CenterY 140 Delay 200 141 VBSCall Dim xzb,yzb,u,v,w,cxpos,cypos 142 Gosub getXYzb 143 u=M-xzb:v=N-yzb 144 w=Sqr(u*u+v*v) 145 If abs(u)>=8 Or abs(v)>=8 146 cxpos=CenterX+u*180/w 147 cypos=CenterY+v*180/w 148 MoveTo cxpos,cypos 149 Delay 1000 150 LeftClick 1 151 Delay 100 152 LeftClick 1 153 Delay 200 154 Gosub MoveAboutMN 155 EndIf 156 Plugin File.WriteINI("Command", "Val5", "完成MoveAboutMN:" & M & "," & N ";", ".\qqnc_debugtext.ini") 157 Return MoveAboutMN 158 Sub ZDGongji 159 MoveTo CenterX,CenterY 160 u=zbd_1x-zbd0x:v=zbd_1y-zbd0y 161 MoveR u*22,v*22 162 KeyDown 17,1 163 KeyDown 16,1 164 Delay 200 165 LeftClick 1 166 Delay 200 167 LeftClick 1 168 Delay 200 169 LeftClick 1 170 KeyUp 16,1 171 KeyUp 17,1 172 Return ZDGongji 173 Sub getSupply 174 Plugin File.WriteINI("Command", "Val1", "进入getSuppply:J=" & J, ".\qqnc_debugtext.ini") 175 curSupply=0:M3=Addr2 176 Gosub getInMem 177 Gosub Csp 178 Plugin File.WriteINI("Command", "Val2", "当前坐标:xzb=" & xzb & ",yzb=" & yzb, ".\qqnc_debugtext.ini") 179 Plugin File.WriteINI("Command", "Val3", "curSupply:" & curSupply, ".\qqnc_debugtext.ini") 180 Plugin File.WriteINI("Command", "Val5", "", ".\qqnc_debugtext.ini") 181 If curSupply >protectSupply 182 wsnd=CInt(5.0*(curSupply-protectSupply)/3.0) 183 Plugin File.WriteINI("Command", "Val4", "等待时间wsnd:" & wsnd, ".\qqnc_debugtext.ini") 184 Delay wsnd*1000 185 EndIf 186 Gosub CancleSkill 187 M=zbd1x:N=zbd1y 188 Gosub MoveAboutMN 189 M=zbd2x:N=zbd2y 190 Gosub MoveAboutMN 191 M=zbd3x:N=zbd3y 192 Gosub MoveAboutMN 193 M=zbd4x:N=zbd4y 194 Gosub MoveToMN 195 Gosub 补给修理 196 Plugin File.WriteINI("Command", "Val1", "退出getSuppply:J=" & J, ".\qqnc_debugtext.ini") 197 Return getSupply 198 Sub 补给修理 199 Plugin File.WriteINI("Command", "Val2", "进入补给修理:J=" & J, ".\qqnc_debugtext.ini") 200 curSupply=0:M3=Addr2 201 Gosub getInMem 202 Gosub Csp 203 Plugin File.WriteINI("Command", "Val3", "死循环关键参数curSupply:" & curSupply, ".\qqnc_debugtext.ini") 204 While curSupply < protectSupply 205 Gosub FindNPC 206 curSupply=0:M3=Addr2 207 Gosub getInMem 208 Gosub Csp 209 Plugin File.WriteINI("Command", "Val3", "死循环关键参数curSupply:" & curSupply, ".\qqnc_debugtext.ini") 210 EndWhile 211 Plugin File.WriteINI("Command", "Val2", "退出补给修理:J=" & J, ".\qqnc_debugtext.ini") 212 Return 补给修理 213 Sub 返回挂点 214 Plugin File.WriteINI("Command", "Val1", "进入返回挂点:J=" & J, ".\qqnc_debugtext.ini") 215 Plugin File.WriteINI("Command", "Val2", "", ".\qqnc_debugtext.ini") 216 Plugin File.WriteINI("Command", "Val3", "M2=" & M2, ".\qqnc_debugtext.ini") 217 Plugin File.WriteINI("Command", "Val4", "", ".\qqnc_debugtext.ini") 218 Plugin File.WriteINI("Command", "Val5", "", ".\qqnc_debugtext.ini") 219 M=zbd3x:N=zbd3y 220 Gosub MoveAboutMN 221 M=zbd2x:N=zbd2y 222 Gosub MoveAboutMN 223 M=zbd1x:N=zbd1y 224 Gosub MoveAboutMN 225 M=zbd0x:N=zbd0y 226 Gosub MoveToMN 227 Plugin File.WriteINI("Command", "Val1", "退出返回挂点:J=" & J, ".\qqnc_debugtext.ini") 228 Return 返回挂点 229 Sub CancleSkill 230 //VBSCall CurShape=GetCursorShape(0) 231 //While curShape=skillShape 232 SaveMousePos 233 MoveTo BasescrX-8+32,BasescrY-30+90 234 Delay 100 235 RightClick 1 236 Delay 100 237 RightClick 1 238 Delay 100 239 RightClick 1 240 Delay 100 241 RestoreMousePos 242 Delay 300 243 //VBSCall CurShape=GetCursorShape(0) 244 //EndWhile 245 Return CancleSkill 246 Sub FindNPC 247 r=0:i=0:kstep=basekstep 248 While r<78 249 r=25+i*span 250 Gosub 画圆 251 i=i+1 252 EndWhile 253 Return FindNPC 254 Sub 画圆 255 x0=CenterX:y0=CenterY:x=0:y=0:k=0 256 While k<3.1415926*2 257 x=x0+r*1.4*cos(k) 258 y=y0-r*sin(k) 259 MoveTo x,y 260 Gosub att 261 Delay 90 262 k=k+kstep 263 EndWhile 264 Return 画圆 265 Sub att 266 VBSCall CurShape=GetCursorShape(0) 267 If CurShape=npcShape 268 LeftClick 1 269 Delay 800 270 VBSCall FindPic(BasescrX-8+80,BasescrY-30+170,BasescrX-8+800,BasescrY-30+550,"船只补给.bmp",0.9,intX,intY) 271 If intX+intY > 0 272 MoveTo intX+30,intY+6 273 Delay 600 274 LeftClick 1 275 Delay 600 276 k=6.3:r=78 277 EndIf 278 EndIf 279 Return att
在这个过程中,发现使用Game Master(湾湾工具)搜索值可以选字符串,以前只知道可以搜索整数类型的数据,所以就想到了其它数据类型,本着多了解一些的想法,在网上找到了不知道转载了几手的这文章,还不错,文章也是湾湾人写的?
http://www.360doc.com/content/13/1130/14/9200790_333409537.shtml
嗯?防复制?先来个浏览器脚本吧……greasefork.org打不开??上知乎(https://zhuanlan.zhihu.com/p/513275228)找一个资源地址……啊,找到了,资源在这里(https://greasyfork.org/zh-CN/scripts/405130-%E6%96%87%E6%9C%AC%E9%80%89%E4%B8%AD%E5%A4%8D%E5%88%B6)
好吧,现在可以复制了。
[转]关于内存地址的个人理解和操作原理1(按键精灵)
2010-04-09 11:10:19| 分类: 默认分类 |字号 订阅
转自:http://hi.baidu.com/cxhphoto/blog/item/d56797efa037cb17fdfa3cab.html
关于内存地址的个人理解和操作原理
最近看见太多人询问内存地址的作用和使用方法,我来做个大概的解释吧 内存地址,其实电脑的每个操作都会在内存中先存储,然后经CPU处理后返回内存最终显现出来,而内存里有个内存地址,是为了区分各种不同数据的,而每个地址则相对应一个数据。 网络游戏中,数据也会先存放到内存中,然后进行处理,包括坐标、血量、MANA等,其实所有信息都是在内存中一一存放并等待处理。处理完毕后结果将会返回原来的地址中(当然也有返回到其他的)。 通过以上的解释,大家可以明白一个道理,其实任何数据都是会下载到本地电脑上然后经过处理后再返回的(至于返回到网络和返回到本地就看数据的重要性了)包括网页的浏览等,任何在你电脑上可以看见的东西必定先存放到本地内存中或硬盘中等待处理后才会显现出来的。 内存和外挂: 说到这里当然要给大家举个例子了,就拿网络游戏来说吧。以前的网络游戏很多数据都是在本地电脑上进行处理和比对的,例如曾经的精灵,它的游戏币是在本地电 脑上进行处理的,处理后的结果再返回服务器,这样就导致了当时的刷钱外挂,因为大家可以修改本地电脑上的内存地址中的数值,然后再返回给服务器。从以上可 以知道单机游戏锁血锁蓝是多么简单的事了吧。因为所有的数据操作都是在本地运行计算的。 当然,还有的外挂是通过改变封包数据来达到效果的。例如魔兽世界加速外挂,比如我现在人物的坐标是0 0然后按照正常速度走的话下一秒坐标是1 1。 因为魔兽世界是在本地对人物坐标进行处理,如果只是单一的改变本地游戏坐标的位置,或许你在自己电脑上会看见自己一下跑到 3 3的位置了,但实际游戏服务器上的人物还在1 1这个位置,那就毫无意义。我们可以截取发送坐标的封包,修改我们下一秒跨度的数值,向服务器发送 我下一秒会在3 3的位置,这时服务器处理后你的人物就在3 3的位置了。当然,整个过程只是改变封包内的数据值,并没有向服务器多发送封包,所以有 的人说是加速外挂给游戏带来了压力的说法是错误的。当然,运营商也可以通过检查你的日常数据来判断你是否使用外挂,所以是没有查不出的外挂,只有不愿意查 的外挂。 而现在的网络游戏血、经验、等级、金钱等重要数据都是由服务器计算然后返回给客户端(就是我们),而且每次存放血、蓝等信息的内存地址在每次游戏的启动都 会变化,在现在来说,修改其数值已经没有任何意义了,因为那只能让你在本地读取的数值不同而已,而真正的数据被存储在了服务器上,你无法修改。但是我们可 以通过读取内存地址中的数值来精确我们脚本的计算。 脚本与读取内存地址: 例如我要做一个脚本,需要在血或蓝少于一个具体的值的时候加血加蓝,这时候我们就可以采用内存地址的读取,因为这种做法可以让脚本以最精确的方式去加血加蓝,而不是去看血条蓝条(其实血条蓝条这时候已经根本不重要)。在按键精灵里 有这么一个脚本。 VBSCall ReadMemory(内存地址:整数(16进制),类型:0-字节 1-整数 2-长整数,输出值-所读取的内容:长整型变量) 实际应用(中间的内存地址为假设): VBSCall ReadMemory( &H400000,2,xue) 这句脚本的意思为,&H400000为读取内存地址400000中数值;2为读取类型,为长整数;xue则把读取出来的数值保存到xue这个变量中去。 如果这个400000的地址是用来储存血量的。那我们就可以对xue这个变量来进行判断计算。实际应用如下 If xue<500 //放入加血的脚本 EndIf 也就是当血量少于500的时候我们开始加血。这个500你可以随意改变 实际应用2挂机打怪,然后人物回到挂机点(中间的内存地址为假设): vbs dim x,y,a,b //申明变量(其实在按键精灵里不用这么做) VBSCall ReadMemory( &H400000,2,x) //读取地址中的纵坐标保存到x VBSCall ReadMemory( &H800000,2,y) //读取地址中的横坐标保存到y vbs a=100 vbs b=100 //也就是原始坐标或者是你想定点的坐标 if x>a and y>b moveto 300 500 //以上坐标为虚拟,根据游戏不同自己更改,以下放入这个游戏里移动的移动方式的脚本 endif //如果两个坐标都大于我们希望保存的地方,我们就开始向另一个方向移动 由此我们可以生成 if x<a and y<b if x>a and y<b if x<a and y>b if x=a and y=b 一共5种不同的处理方法,这样让人物回到原来的点继续开始打怪就可以轻松制作原点挂机的脚本 说了那么多,一定会产生一个很大的问题,如何查找游戏内的内存地址。 内存地址的查找 我一般用金山游侠 例如我要找红,先在自己满血的时候输入上限,然后搜索,这时候会搜索出很多地址,一些是代表你当前的红,和你的上限,还有一些正好是搜索时碰巧一样的。 OK,在结果里再搜索一次,会去掉一些,这样搜索3次基本上结果已经很少了,但还不准确。 现在,你要做的是就是让自己死了,不同游戏可能不同,死的时候血是0,那就去自杀吧(如果掉经验用小号)。 在结果里用0搜索一次。 别以为这样就结束了,这次要让自己不死,那就让自己少点血,我一般采用脱掉加血上限的装备,让自己的血减少。 依照以上方法反复尝试,最后确定一个地址。 地址出现以后别高兴太早,现在很多游戏内存地址是变动的(至少每次开游戏时就变动),所以可能这次管用,到下次还要重新搜索。 以上这些都是我个人的经验之谈,可能有不精确之处,但我想基本上可以解决大部分问题,如果还有不懂的下面跟贴。 PS:这里的人看贴不回的习惯太不好了 以下发文观点皆以 "您已经会使用金山游侠、FPE2000、GameMaster等辅助软体来找寻资料" 相信不少玩家都经歷过那段记忆体只有 1M 时代,常常為為了挤那1,2k出来 跟OS争的你死我,再看看现在的电脑规格...,科技真一日千里 XD~ 切入我们的主题,找到某游戏的内存位址数值后该怎麼用。 第一先从按精提供的函数来看 VBSCall ReadMemory(v1,v2,v3) v1:内存地址:整数(16进制) v2:类型:0-字节 1-整数 2-长整数 v3:输出值-所读取的内容:长整型变量 我想很多人一开始就算你找到了数值,却卡在这边不知道要输入什麼,包括我第一次使用也 遇到这个问题,所以我觉得有必要就这个部份说明一下。 VBSCall ReadMemory(&HFAC8EA,1,HP) 上面这行您一定常用 &HFAC8EA 是内存地址 , 1 是读取一个整数然再后再存进 HP 变量裡面 不管您用过几次,下面这三个参数说明您一定要看完。 ------------------------------------------------------------------------------------------- ReadMemory()第一个参数 内存地址:整数(16进制) ------------------------------------------------------------------------------------------- &HFAC8EA 是什麼东西,為什麼可以代表一个内存地址? 来测试一下,在按精脚本输入 [Copy to clipboard] CODE: VBSCall MessageBox(a) VBSCall MessageBox(typename(a)) 结果: 第一次显示 16435434 第二次显示 Long 没错,&HFAC8EA 是一个长整数,&H就是用来将一个10进制的数字转成16进制的表示法, 他的反函数是HEX(),按精测试 VBSCall MessageBox(HEX(16435434))会显示 FAC8EA。 *因為我没有 ReadMemory() 这个函数的原形,所以我并不知道為什麼说明文件上面写整数(16进位) 也许是很早之前的按精版本确实只能使用整数,只是说明文件没改,也许是他确实是传入一个整数 ,只是我误会他的意思,有时间我会把函数dump出来看看再上来加註,或是总版老大来龙去脉可以 说明一下。 不过他是什麼型态无所谓,至少用到现在没有出错过(不过还是有必要瞭解一下),我们只要确定一点 a = &HFAC8EA,变量 a 的型态确实是一个长整数!! 就算你把他强制转形,他还是一个长整数。且看 以下。 [Copy to clipboard] CODE: VBSCall MessageBox(typename(a)) //强制转形為整数 a = CINT(a) VBSCall MessageBox(typename(a)) 嗯,二次都是显示 Long,这是因為VBS的特性会根据变量内容来决定他是什麼型态如同以下程式 [Copy to clipboard] CODE: b = 3 VBSCall MessageBox(a + b) 得到 8,因為VBS变数宣告的不定型特性,相对VBS也调整了一些运算子的自动转换,这样是好还是不好, 好处就是乱乱写也通,坏处就是在脚本裡做判断时很容易出错,因為你不知道他什麼时候偷偷帮你转形, 或是你以為他有帮你转形,事实上并没有,下就这段就不能执行了。 [Copy to clipboard] CODE: b = 3 VBSCall MessageBox(a + b) 这种情况其实常常发生从文字档裡读出使用者界面参数做判断时,有时应该要HP=30自动吃补药,但是没吃, 不是按精有问题,而是你的判断式不正确,避免这种情况发生,读取文字档裡的变数出来后一律强制转形一 次(一个被变数型态搞到失眠的人留),微软出了一个VBS不知道该说造大眾还是害死一堆入门设计师。 偏主题有点久了,这部份详细请参考拙作(其实是写给我自已看的)。 按键精灵与 VBscrīpt 的基础观念(一)/viewth ... &extra=page%3D2 按键精灵与 VBscrīpt 的基础观念(二)/viewth ... &extra=page%3D2 ------------------------------------------------------------------------------------------- ReadMemory()第二个参数 类型:0-字节 1-整数 2-长整数 ------------------------------------------------------------------------------------------- 為什麼我这麼在意变量是整数还是长整数,原因就在这,第二个参数型态直接就影响到输出的结果, 字节跟整数跟长整数有什麼不同,最大就差别就在范围值 字节 = 1 Byte (-127 ~ 128) 或不带正负号 (0 ~ 255) = 256 整数 = 2 Byte (-32,768 ~ 32,767) 或 (0 ~ 65535) = 65536(256 ^ 2) 长整数 = 4 Byte (-2,147,483,648 ~ 2,147,483,647) 或 (0 ~ 4294967296) = 256 ^ 4 上面的范围很重要在,超过原本型态的范围就会產生溢位(overflow)的错误。 整数一定是 2Byte ? 不一定,要看你用的是哪一套程式语言,像 .NET Framework 中的整数就是 4个 Byte,Long 是 8 个 Byte,有一种叫 短整数 才是 2 Byte。那你怎麼知道按精是什麼语言开 发的,对!我不知道...,那你怎麼知道上面字节要用1 Byte 整数要用 2 Byte,对!我猜的 =.= 呵呵,好啦,不要玩文字游戏了,没有特别的情况下一定是上面那种范围,我实测过的确第二个参数 是这个范围值没错,只是按精应该在指令说明上应该註明一下范围(ps.我的印像中好像 按精有一个地方有特别註明这个参数佔的Byte数,因為我看了5.9x简体版的说明文件还有繁体版的说明 文件还有官网上的按精宝典,都没有特别说明,我一时也想不起来在哪看过的,还是哪位高人po的帖子 有提及过,有版友可以提供我官方文件说明文件连结我就会回来编帖註解一下。) 这个跟我游戏中 血量是285 昇级经验值是 20000 有什麼关係? 所以重点就是如果你的血量是在 0~255内,你就要设 1(字节),0~65535就要设2(整数) 有时你现在的血量是124第二个参数你设1(字节),但可能你会昇级血量会超过255(字节的范围),读出来 的值最大就到255而己,所以你要先确定游戏中血量到底佔了几个Byte。 ------------------------------------------------------------------------------------------- ReadMemory()第三个参数 输出值-所读取的内容:长整型变量 ------------------------------------------------------------------------------------------- 我们来看一张图 图片附件: Memory1.gif (2006-8-18 07:32 AM, 7.97 K) 红框的部份就是从 &H000EA99A 读取 1 个byte放进HP变量 VBSCall ReadMemory(&H000EA99A,0,HP) //绿框的部份就是从 &H000EA9BA 读取 2 个byte放进HP变量 VBSCall ReadMemory(&H000EA9BA,1,HP) //蓝框的部份就是从 &H000EA9DA 读取 4 个byte放进HP变量 VBSCall ReadMemory(&H000EA9DA,2,HP) 如果要连续取12个 4 Byte要怎麼做? 好问题,什麼情况会连续读取内存,读中文和图形档! 稍后介绍 内存偏移设定:从一个位置就知道其它位置,动态计算位址。 ------------------------------------------------------------------------------------------- 对於记忆体实际系统运作,不在本篇叙述内,我们只要知道在按精如何灵活运即可。 内存位址的偏移(加加减减)观念上与按精中的阵列(数组)运作方式是一样的,且看以下 [Copy to clipboard] CODE: VBS Dim Memory(3) //Assign Value Memory(0) = &H6B Memory(1) = &H00 Memory(2) = &H4B Memory(3) = &H00 类似上面的阵列用法您一定常用 您可以把内存位址(如:&HFAC8EA) ,当做是阵列中的索引值(Memory(0)、Memory(1) 阵列中的数值 Memory(0) = &H6B、Memory(1) = &H00 当做是内存位址实际储存的数值,也就是您实际读取到的HP、MP值。 看一下这张图你就一目了然了。 图片附件: Memory2.gif (2006-8-18 09:40 PM, 6.28 K) 以上举例说明只是让您更瞭解内储位址的意义,只要知道一般我们在按精裡使用的变数、阵列是10进制,而内存不管 是索引还是存放的数字都是以16进制来表示。 假设我们用金山游侠之类的软体找到了游戏中的数值 目前血量值(HP):1575 目前魔法值(MP):1604 最大血量(TOP_HP):17414 最大魔法(TOP_MP):9990 图片附件: Memory3.gif (2006-8-18 09:40 PM, 12.47 K) 那麼按精中你会这样写 [Copy to clipboard] CODE: VBSCall ReadMemory(&H001E3242,1,MP) VBSCall ReadMemory(&H001E31B9,1,TOP_HP) VBSCall ReadMemory(&H001E31BB,1,TOP_MP) //转成百分比 My_HP = Round(HP / TOP_HP , 2)*100 My_MP = Round(MP / TOP_MP , 2)*100 //HP低於60%吃HP补药 If My_HP <= 60 Gosub 吃HP补药 End If //MP低於30%吃MP补药 If My_MP <= 30 Gosub 吃MP补药 End If 注意一个地方!! 由上图 TOP_HP (&H001E31B9)内存位址(最上面那个红色框)得知 第1个 Byte 06 第2个 Byte 44 如果你在按精测试 VBSCall MessageBox(&H0644) 得到结果会是 1604 ,而不是我上面写的 17414 怎麼会这样? 不要担心,因為记忆体高低位元排列的关係,要测试的值要倒过来打, VBSCall MessageBox(&H4406) ,才能显示你抓到的数值 17414。 这部份原理我就不赘述,因為按精ReadMemory函数已经帮你处理掉,输出长整数变数一定跟记忆体中存的数值没错, 用ReadMemory读出来也是10进位,而不是16进位,你的目的只是要读取内存资讯而己,所以不要想太多 上面这个范例是按精中典形的用法,但是如果如果你需要使用大量的内存位址,万一游戏改版或其它因素导致内存位址改变了 那啟不是又要重头全部找一次? 所以有经验的玩家会使用相对位址来储存,只记录内存位址偏移(偏了几个Byte)。 通常有3,4个内存位址,我们会挑位址最前面(位址索引值最低)那个来储存当做是计算偏移起点,以上例来说就是 &H001E31B9(TOP_HP)。 &H001E31B9 (TOP_HP) < &H001E31BB (TOP_MP) < &H001E3240 (HP) < &H001E3242 (MP) 再来就是执行小算盘->检视->切换成工程型,用来计算偏移位址 (你要用手算也可以啦 ~_~) 图片附件: Memory4.gif (2006-8-18 09:40 PM, 16.34 K) 求各个内存位址 到 &H001E31B9(TOP_HP) 的偏移(距离) HP - TOP_HP (目前血量 HP 的内存偏移) 小算盘输入(前置0不用打) 1E3240 - 1E31B9 = 87 MP - TOP_HP (目前魔法 MP 的内存偏移) 小算盘输入(前置0不用打) 1E3242 - 1E31B9 = 89 TOP_MP - TOP_HP (最大魔法值 TOP_MP 的内存偏移) 小算盘输入(前置0不用打) 1E31BB - 1E31B9 = 2 注意! 红色数字的偏移量是16进位 按精中实际脚本语法 [Copy to clipboard] CODE: //目前血量内存位址 HP_Addr = TOP_HP_Addr + &H87 //目前魔法内存位址 MP_Addr = TOP_HP_Addr + &H89 //最大魔法内存位址 TOP_MP_Addr = TOP_HP_Addr + &H2 //将内存位址以变数传入 VBSCall ReadMemory(HP_Addr,1,HP) VBSCall ReadMemory(MP_Addr,1,MP) VBSCall ReadMemory(TOP_HP_Addr,1,TOP_HP) VBSCall ReadMemory(TOP_MP_Addr,1,TOP_MP) 注意一个地方,如果您使用档案存取方式将起始偏移量储存起来,那麼要用 CLng 强制转形一次 因為预设读出来的值都是字串(字串和数值相加会错误,第一篇有提到这个问题)。 "&H001E31B9" TOP_HP_Addr = CLng(TOP_HP_Addr) 或者是您只存了 "1E31B9" TOP_HP_Addr = "1E31B9" TOP_HP_Addr = CLng("&H" & TOP_HP_Addr) *结论: 用这种只存起始位址和记录偏移量的好处就是,就算游戏改版改变起始位址,也只要找一次,不用每个都重找内存位址。 当然,碰上游戏大改版,在多了一些栏位(譬如多了什麼精神力、威望值等等),有可能重新排列过记忆体位址,就要重 新全部找一次了。(总不可能游戏天天在改版吧 / _ \) 记忆偏移设定教学就到这边。 内存中找中文这篇我延了一段时间才补上来,不是我偷懒,而是我在想该怎麼詮释 中文字在内存的值,我试著写了一些解释(word档二页),写到后面实在有点无力, (没看没事,看完连我自己都开始疑问一大堆),於是我往现成网路资料找,也找到 一些相当不错的网站,请版友慢慢享用。 谈谈Unicode编码 http://fmddlmyy.home4u.china.com/text6.html Unicode /w/index. ... e&variant=zh-cn Big5 /w/index. ... C&variant=zh-tw GB 2312 /w/index. ... 2&variant=zh-cn GB 18030 /w/index. ... 0&variant=zh-cn ASCII 是的,中文在内存中的值取决於他是什麼编码类型,上面列表是比较常用的。 原则上,一个英文字 1 个 Byte ,一个中文字 2 个 Byte(台湾以前还有一个中文字3个Byte 的编码方式),Unicode 编码也很重要,详细请看上表,在以下教学中避免误导,不介绍此 编码方式。 我们先从比较没问题的英文字在内存看起,英文字编码是属於ASCII,建议您把上表中连 结开啟对照。(按精中有个指令叫 ASC 您应该有用过,就是ASCII的缩写,下例会用到) 假如某位玩家对您说 LOVE,经由辅助软体Game Master我们找 到 "LOVE" 在内存的位址 = &H001C19D8 图片附件: Memory5.gif (2006-8-20 06:58 PM, 3.46 K) 按精中读取LOVE字串内存的方法 //Love字串内存起始位址 LoveAddr = &H001C19D8 //读取 4 Byte(一个英文字一个Byte,这边我不用Len("Love")或是设定為变数计算的方式,稍后说明。) iByte = 4 //从第 1 Byte读取 i = 1 //读取内存值存放变数 ReadString = "" iStr = "" //fix iString rename iStr (0821) //连续读取 4 个Byte内存资料(还记得上篇教的位址偏移量的使用吗 ) 一定要先看完前二篇哦,不然看到这会很吃力 while i <= iByte VBSCall ReadMemory(LoveAddr + CLng("&H" & i-1) , 0 , iStr) iStr = CHR(iStr) ReadString = ReadString & iStr i = i + 1 //fix 0823 EndWhile //男/女朋友密你,自动回话给他 If ReadString = "LOVE" SayString "baby~ i love you too" EndIf 说明: * LOVE 跟 love 大小写不同内码值也不同,上面这个例子是用 ASC 的反函数 CHR 自动转换读取到 Ascii码所对应的字元。 * 不用 Len("Love") 或是 s = "Love" : Len(s) 这样的方式是因為Len是VBS函数,VBS属於作业系统一部份,Len会随著 作业系统的不同而有不同的值(Windows XP/2000作业系统本身就是Unicode系统),得到的值 是字串的字元长度,而不是字串的 Byte数,(你又开始在抽像了...), 作个实验就知道,在按精输入 VBSCall MessageBox(Len("abc中文")),得到的答案是 5 , 怎会这样? 你不是说一个英文1个byte,一个中文二个byte,Len("abc中文") 不是应该要 3+2+2 = 7 吗? 答案就是从这段*号地方开始再看一次! 事实上 "abc中文" 在XP裡面是10个Byte(5x2), VBS 另外有一个计算字元的Byte数的函数,在按精输入VBSCall MessageBox(LenB("abc中文")) ,显示 10 。 写到这边我都还没正式介绍内存读取中文哦,所以读取英文的情况下照我上面的范例不会错。 再说也许可能是要读取不同男/女朋友的英文名字,再判断是第几号情人再回话,万一回错了就好笑了,所以还是照这样用。 不过话又说回来,我不敢保证游戏中一定是用1个byte在存一个英文字,有的游戏设计有考虑到发行 到不语言的国家,很有可能会使用2个byte来存英文字,现我在测试的游戏英文是以1个Byte储存,实际上使用,您 还是要多观察内存值的变化,再正确的设定ReadMemory参数,这就是我為什麼要写了二页的word档之后又砍掉的原因了, 既然要使用内存来读取游戏数值,就要细心一点。 接下来继续介绍中文内存读取,原理跟英文字串差不多略作修改,假如我们要对应游戏中某张地图名称叫 "火山湖" 也找到内存位址起点。 图片附件: Memory7.gif (2006-8-20 06:58 PM, 4.58 K) 脚本命名:FindStrInMemory.txt //要找的字串 FindStr = "火山湖" //这个字串的内存起始位址 FindStr_Addr = &H00169380 //转换实际内存中字串佔的Byte数 (中文=2个Byte,英文字1个Byte) VBSCall FindStrByte = StrLength(FindStr)@<StanleyUtl.vbs> //从第1个Byte开始读取 i = 1 //内存中字串的16进制码 MemoryHexStr = "" //迴圈开始读取 While i <= FindStrByte VBSCall ReadMemory(FindStr_Addr + CLng("&H" & i - 1) , 0 , bWord) MemoryHexStr = MemoryHexStr & hex(bWord) i=i+1 EndWhile //将您要找的中文字串转成跟内存一样的格式 VBSCall returnStr = Str2Hex(FindStr)@<StanleyUtl.vbs> //这裡写找到内存中文字串之后值判断式 If returnStr = MemoryHexStr GoSub 地图区域处理副程式 EndIf VBS自订函数档命名: StanleyUtl.vbs (跟上面这个脚本放同一目录) Function Str2Hex(varstr) Str2Hex="" For i=1 To Len(varstr) varchar=Mid(varstr,i,1) varasc = Asc(varchar) If varasc<0 Then varasc = varasc + 65535 End If If varasc>255 Then varlow = Left(Hex(Asc(varchar)),2) varhigh = right(Hex(Asc(varchar)),2) Str2Hex = Str2Hex & varlow & varhigh Else Str2Hex = Str2Hex & Hex(ASC(varchar)) End If Next End Function '计算byte(中文 2 Byte,英文 1byte) Function StrLength(S) n = 0 For i = 1 To Len(S) C = Asc(Mid(S, I, 1)) If C >= 0 And C <128 Then N = N + 1 Else N = N + 2 End If Next StrLength = N End Function 说明: 1.请注意我在脚本中 VBSCall ReadMemory(FindStr_Addr + CLng("&H" & i - 1) , 0 , bWord) 这行是以 0 (字节:1个BYTE) 方式读取,原因是因為实际上您在游戏中可能要比对的字串中英文混合 "火山湖ab12有美女" ,所以我在 StanleyUtl.vbs 提供了一个字串转成内存实际佔用Byte的函数。 2.实际你只要比对内存字串值,不用刻意对中文转码,什麼咧Unicode转ansi,简体转繁体,转来转去头都晕了,当然啦, 上列脚本只是简单应用,也许演算法写得不很好,如果有更好的写法也欢迎您po上来讨论。 3.一句老话,使用内存多观察内存值变化情况,确定你要找个这个中(英)文字串的内存起始位址,才不会找错 中文字在内存的应用介绍就到这边~ XD 很不好意思要这样跳来跳去看~ 全部写完会委请总版帮忙连在一起 |
标签:Gosub,VBSCall,HP,VBS,按键精灵,内存,碎冰,Byte,内存地址 From: https://www.cnblogs.com/argent/p/17035203.html