武林外传怪物血量,坐标读取(注入)附部分源代码
type
{定义一个结构,也就是说长跳转的机器代码,即JMP xxxxxxxx,跳转的机器代码是E9(操作码)+跳转的偏移量,偏移量是这样定义的,目的地址-(源地址+5),并不是直接的目标地址,这个结构定义,JmpCode,跳转的操作码,也就是E9,JmpAddr,偏移量(4字节),JmpNop,空操作的代码,即90,为什么要加JmpNop,因为一般的变址寻址传送指令是6字节的,长跳转是5字节的,在这加入空操作补齐。定义结构,代码比较简洁,计算偏移量也比较方便。这里要特别注意的是 Packed record,如果不加Packed关键字,那么Delphi 会自动优化默认4字节对齐(和编译开关有关),这样,这个结构就是3*4=12字节长了,不是我们想要的6个字节长了。shortInt (1字节)+Dword(4字节)+shortint(1字节)正好是6字节}
TLongJump = packed record JmpCode: shortInt; JmpAddr: Dword; JmpNop: shortInt; //加入nop,跳转是5字节,move是6字节 end; {传送机器码的结构,moveCode,操作码(2字节),moveAddr,传送的地址(4字节)} TmoveCode =Packed record MoveCode: Word; MoveAddr: DWord; end;
{这里不用说了吧,hp,怪物血,maxhp,怪物最大血,x.y怪物坐标}
TMonStat = record //怪物属性 Hp:integer; MaxHp:integer; x:integer; y:integer; end; ... const Maddr : dword =$549140; {$549140,是注入的地址,这里应该是一条指令 mov [eax+130],edx eax即所选怪物的基址!!! 这个数值一般每更新都会变,每次更新都要改变这个数,这也是注入的缺点吧,$549140对应的是51版的,新的我没记下来是多少,这里说下查找方法,找个5级发下的小号,不拿装备(这样打一下怪减少的血是固定的),选中一个抱抱兔,我们知道抱抱兔的血是79,然后用CE搜79,打下怪,停下,记下打了怪多少血,搜索79-怪物掉血值,肯定能搜到唯一一个数,这就是你当前选的抱抱兔在内存中的地址,然后用find what write this address,再打下抱抱兔,CE会显示出一个地址,对应的指令就是Mov [eax+130],edx 记下这个地址,把这里改成这个地址,就是咱们要用到的注入点}
code : string=chr($89)+chr($90)+chr($30)+chr($01)+chr(0)+chr(0); //mov [eax+130],edx的机器代码 899030010000
//注入部分 var pid,code:dword; JumpCode:TlongJump; MoveCode:TMoveCode; begin hw:=findwindow(nil,'Element Client'); if hw=0 then begin messagebox(0,'请确认游戏已经运行!','未找到游戏', 0); application.Terminate; end; GetWindowThreadProcessId(hw, @pid); h := OpenProcess(PROCESS_ALL_ACCESS, false, pid); if h=0 then begin messagebox(0,'请确认游戏已经运行!','未找到游戏', 0); application.Terminate; end; {上面的不用说了吧} ... //在游戏中申请一块128字节的地址 ThreadAdd := VirtualAllocEx(h, nil, 128, MEM_COMMIT, PAGE_EXECUTE_READWRITE); {我习惯在游戏中申请内存地址,而不用固定地址,这样虽然注入的程序麻烦些,但不会出现内存冲突而导致错误,128字节的地址应该够用了,threadAdd就是申请到的地址指针} writeProcessMemory(h,threadadd,@code,length(code),tmp); {code对应的就是move [eax+130],edx ,因为我们把原来的这个代码改成JMP xxxxx了,所以这里要恢复} MoveCode.MoveCode:=word($0589); {mov [xxxxxxxx],eax的操作码,因为eax里放的是怪物的基址,我们要将这个基址放到我们能知道的地方,这也是我们注入的目 的} MoveCode.Moveaddr:=longint(ThreadAdd)+$30; //从$30开始保存eax {moveaddr 就是mov [xxxxxxxx],eax中的xxxxxxx,这里我们设的是我们申请的地址+$30,也就是在这里保存eax即怪物基址,这一步以后,movecode的内容就是0589xxxxxxx,也就是mov [xxxxxxx],eax 的机器码了} writeProcessMemory(h,pointer(longint(threadadd)+6),@MoveCode,6,tmp); //写入 move [addr+$30],eax的机器码,pointer(longint(threadadd)+6),为什么要加6?因为我们前面恢复了mov [eax+130],edx,这个指令是6个字节,所以要加上6,才能保证接着写
JumpCode.JmpCode:=shortint($e9); //e9是jmp的机器码 JumpCode.JmpAddr:=maddr+6-(longint(ThreadAdd)+12+5); JumpCode.JmpNop:= shortint($90); //nop,补齐nop,其实也没必要
{嘿嘿,地址指针已经存到我们要的地方了,应该干什么?跳转回原来的地址呀,e9是jmp的机器码,这里说一下偏移量的计算,我们不能再跳回到我们的注入点了,这样是死循环,我们要跳转到我们注入点以后的下一条指令,也就是游戏原来mov [eax+130],edx后的下一条指令开始执行,而mov [eax+130],edx 是6个字节,所以目标地址就是maddr+6;源地址呢?因为我们在我们申请的地址里已经写了两条指令了,两条指令的长度是12字节,所以源地址就是longint(ThreadAdd)+12,为什么要+5,看前面去}
writeProcessMemory(h,pointer(longint(threadadd)+12),@JumpCode,6,tmp); {写入跳回原地址的指令,这样我们在我们申请的地址里写入的代码就是这样的 mov [eax+130],edx mov [threadadd+30],eax ;将eax保存在我们申请的地址+$30处 jmp maddr+6 //跳回去接着执行}
jmpCode.JmpAddr:=longint(ThreadAdd)-(maddr+5) ; //计算 jmp threadAdd的偏移量 writeProcessMemory(h,pointer(maddr),@JumpCode,6,tmp); {上次忘写这两行了,在这里表示歉意,这两行在我们的注入点写上代码jmp Threadadd,也就是跳到我们申请的地址开始执行,也就是把游戏原来的mov [eax+130],edx 替换为jmp threadAdd} //注入结束 .... function ReadMon():TMonstat; //读怪物状态 var TempAddr:Dword; tempxy:single; begin with Result do begin ReadProcessMemory(h, pointer(longint(ThreadAdd)+$30), @TempAddr, 4, tmp);//怪基址读回,我们把怪物的指针放在threadadd+$30的位置,我们从这读的就是怪物的基址,有的朋友说找不到所选怪物的基址,这就是了!!!! ReadProcessMemory(h, pointer(TempAddr+$130),@hp,4,tmp); //读怪血, ReadProcessMemory(h, pointer(TempAddr+$130+24),@maxhp,4,tmp); //读怪最大血 if (hp<0) or (hp>500000) then hp:=0; if (maxhp<0) or (maxhp>500000) then maxhp:=0; ReadProcessMemory(h, pointer(TempAddr+$1b8),@tempxy,4,tmp); try x:=trunc(tempxy); except x:=0; end; {加入try,防止换线时出现浮点错误} ReadProcessMemory(h, pointer(TempAddr+$1c0),@tempxy,4,tmp); try y:=trunc(tempxy); except y:=0; end; end; end;
[怪物指针+130]=怪物血 [怪物指针+148]=怪物最大血 [怪物指针+1b8]=怪物X坐标,浮点数 [怪物指针+1c0]=怪物Y坐标,浮点数 最后说一点,好象不用找怪物名字把,大家研究怪物ID没,80xxabcd,我感觉xx应该是怪物的编码,把这个分出来,就能对应怪物名,abcd应该是这个怪物在这个地图上的编号,这个没太多研究,大家可以验证一下!
|
请发表评论