本帖最后由 公孙秒秒 于 2019-4-2 18:10 编辑
前言:
-------------------------------------------------------------------------------------
从自学python到现在碰到过蛮多问题,很多问题其实在百度上很难去找到答案,python的相关教程实在太难找了,这里分享一个自己研究了蛮久的64位程序内存的读与取的教程吧
视频版地址:
-------------------------------------------------------------------------------------
https://www.bilibili.com/video/av47453086
需要用到的软件:
-------------------------------------------------------------------------------------
CE , X64DBG,notepad++(下面简称记事本本),鬼泣五哈哈哈
*这里讨论的python是32位的,如果使用64位python的情况下其实可以直接通过kernel32.dll中的相关函数进行操作的,这里主要是打破32程序与64位程序的壁垒,很多插件都是32位的,所以你懂的,使用64位python调用会产生一些莫名其妙的问题,可根据需求自行考量
需要的python的库
-------------------------------------------------------------------------------------
请大家提前安装好:pywin32
步入正题
-------------------------------------------------------------------------------------
首先我们肯定是要找基址的,这里64位程序的找基址与32位大同小异:
首先打开游戏,进入到游戏的商店界面
游戏商店
然后打开CE,并打开游戏进程
打开进程
选中游戏进程
进行首次扫描,图片里1对应的文本框填入需要搜索的数值,HEX勾选的话就代表搜索的内容是16进制,这里我们不用勾选,填上我们的魂石数量就好了
2指向的扫描类型,这个应该不用我说大家都懂的
3的话数据类型其实正常都是4字节整型,当然不确定的时候其实可以考虑所有类型,现在的硬件搜起来也是很快的
好了过后点击首次扫描
进行首次搜索
这里其实我们发现搜索结果里面的数据非常少,我们很快就可以锁定到魂石的地址了,如果非常多的话重复下面的步骤就好了
去游戏里买个东西,让魂石改变,这里我买了一根胳膊,是的,胳膊,尼禄限量版胳膊,500魂石,可以看到魂石减少了
OKK,我们现在回到CE里,准备进行再次扫描:
准备再次扫描
这里其实打开CE我们发现有两个地址中的数据时红色的,说明他们已经改变了,这两个其实就是我们要的地址了,可以直接进行找基址了,不过也不排除有时候数据会非常多,还需要再次扫描
再搜索框里填上改变后的魂石的数量,点击再次搜索
这里的结果其实就是上图中改变的那两个啦,不过假如数据很多大家重复上面两个步骤,排除到数据很少的时候也就OJBK了,就可以愉快的进行我们的下一步了
下一步是什么呢,当然就是传说中的找出是什么改写了这个地址,这里的两个地址其实无所谓啦,随便在哪个上面右击,选择找出是什么改写了这个地址
找出是什么改写了这个地址
同意附加:
然后我们的调试器窗口就出现了,当然一开始如下图,他一贫如洗,我们需要再去游戏里shoping一下,让我们的小钱钱改变一下
调试器窗口
女人为什么爱购物,因为购物能有愉悦感啊,开开心心买完东西过后返回我们的CE,看,有东西了,是不是很惊喜很开心:
好了,飞鸟尽,良弓藏,狡兔死,走狗烹,我不会告诉你们这句话我是去现搜的,记住这个伟大的时刻,我们把这句代码复制出来,贴到记事本里,然后就可以关掉CE了
对,CE他现在就这样被我们榨干过后一脚踢开了,当然不是我们绝情,假如你不关了CE的话,后面的步骤你可能就不能愉快的进行了
打开X64DBG,并选择附加,你也可以按快捷键的,不说了,都在酒里,呸,都在图里
:
然后附加上游戏,乱花渐欲迷人眼,小浪蹄子太多了,万一找不到进程你可以按个排序,一下子就找到了,双击它,也可以单击他,然后点附加:
附加鬼泣五
这个时候我们看费客户去,也就是程序的最上面,找到模块,会发现我们现在所处的位置不是鬼泣五他们家,所以我们需要打开我们的导航,然后传送过去找我们的数据:
看模块
如何打开导航呢,其实就是我们的模块界面,可以看图
打开模块界面
也可以按快捷键,快捷键是什么呢,我们也看图我才不会给你们打出来:
找到鬼泣五
找到鬼泣五,我们发现这里的小浪蹄子比刚才还多,点个排序,很快找到,双击666,当然这里没有666,我们会传送到鬼泣五的老家:
转到表达式
转到后如图在汇编代码区右击,转到,表达式,你也可以按快捷键的,还记得我们刚才从CE里复制出来的那段代码吗,是时候登场了:
7FF7E77E88E4 - 44 29 76 60 - sub [rsi+60],r14d
最前面的其实就是地址,64位的地址,中间是机器码,后面试汇编指令,这个可能大家都知道我就是话痨一下,我们把地址复制出来,粘贴进X64DBG里的框框里然后你可以猛烈敲打你的回车挥着轻轻用鼠标点击确定按钮
下面就是见证平常的时刻,你转到了你复制的那句代码的地方,别说话,往上拉,找到函数头,怎么知道到了函数头呢,找RET或者int 3,有他们两个准没跑这里我们看到了int 3我们知道我们该刹车了,按住shift然后点击int 3下面的一句,选中他们过后复制出来
怎么复制?按CTRL+C啊!!!!
然后粘贴进你的记事本本里,查找RSI,为什么要查找RSI呢,因为从我们一开始获取的那一段代码我们可以知道
7FF7E77E88E4 - 44 29 76 60 - sub [rsi+60],r14d
即这条最后面的汇编指令部分,他的意思是将 [rsi+60]里的值减去r14d里存放的值,rsi,r14d是什么呢,是一个寄存器,你可以想象成一个变量,他们是会变得,所以我们要追根溯源,知道谁把货放进了rsi里
汇编中呢,[rsi+60]的意思呢就是先将rsi这个寄存器里面的值加上60,注意这里是十六进制,然后再以得到的结果为地址,去取出这个地址中的数据,这个数据其实就是我们的红魂石的数据了,而rsi+0x60,就是地址,就是装货的车
我们找到车没用啊,我们的找到谁装的货我们才能每次都拿到货对不对,所以我们要跟踪RSI这辆车,找到发货人,所以我们在记事本本里按CTRL+F,弹出查找页面,选择标记,查找rsi
查找rsi
看到木有,见证奇迹的时刻,rsi被染色了!!是的,被染色了,其实你也可以在x64dbg里直接找,不过我找不到x64dbg让对应寄存器变色的插件,所以我就复制出来到了记事本本里找,是不是换个思路过后瞬间清晰明了
然后我们向上找,要找什么呢,其实就是找对rsi进行mov,lea这类的赋值指令,或者是ADD,SUB这样的运算指令等等,我们很快锁定了我们的现已目标,也就是我框中的这一句,我们出动警力先把他抓回来;复制出来跟一开始那句代码放一起
[Asm] 纯文本查看 复制代码
7FF7E77E88E4 - 44 29 76 60 - sub [rsi+60],r14d
00007FF7E77E8833 | 48:8BF2 | mov rsi,rdx
这个时候我们知道了,rsi其实是由rdx赋值的,也就是说,rsi的货是rdx给他的,但是rdx也是一个寄存器,也就是赚差价的中间商,他不是我们要找的幕后黑手,我们继续向上找,可以标记所有的RDX,进行全网搜捕,但是我们发现(其实不搜索也发现了),上面已经没几句代码了
我们的线索戛然而止,我们找不到rdx进行秘密交易的现场了,怎么办,没事我们网上找,找出是哪里调用了我们复制出来的这段函数,肮脏的交易必然落入我们正义之网。到刚才复制那一大段代码的第一句,也就是函数头那里,去设下路障,呸,下个断点,怎么设呢,看图
函数头
设好路障过后,我们再让他们继续进行交易,我们才能寻找到更多的线索。也就是去游戏里继续买条胳膊,然后我们的路障就能顺利的拦截住他们,断点成功断下了,找到队长窗口,也就是右下角的框框
看第一句,返回到XXXXXXXXXX了吗,右击,选择在反汇编窗口中跟随,也可以大力的敲击你的回车键,我们就可以摸索到上层交易的现场附近了,也就是调用了这个函数的地方
跟踪到函数调用的
跟第一次一样,别说话,往上拉,找到函数头,复制出来,粘贴进记事本本
然后寻找RDX,看我偷拍到的交易现场就知道了,我们找到了,幕后黑手就是他
[Asm] 纯文本查看 复制代码 00007FF7E787E288 | 48:8B15 89D2B106 | mov rdx,qword ptr ds:[7FF7EE39B518] |
为什么我们知道就是他呢,因为这里出现了一个常量,八九不离十就是他了,复制出来,跟一开始的两句关到一起,为什么不用关test rdx,rdx这一句呢,因为这一句其实并不改变rdx的值,所以我们可以略过他
这样我们就一共关押了三位嫌疑犯,我们把他们放在一起:
[Asm] 纯文本查看 复制代码 7FF7E77E88E4 - 44 29 76 60 - sub [rsi+60],r14d
00007FF7E77E8833 | 48:8BF2 | mov rsi,rdx
00007FF7E787E288 | 48:8B15 89D2B106 | mov rdx,qword ptr ds:[7FF7EE39B518]
不难看出,我们要找到的幕后黑手藏身所在,其实就是[[7FF7EE39B518]+0x60],也就是我们的基址偏离了,大家可以用CE验证一下的,这里就不验证了,进入我们的主题,怎么用python读取,我的天我干了什么,我截了这么多图才到python么
彳亍口巴!!
打开我们的python编译器
首先我们导入函数:
[Python] 纯文本查看 复制代码 from win32gui import FindWindow #获取窗口句柄
from win32api import OpenProcess, CloseHandle #创建进程句柄与关闭进程句柄
from win32con import PROCESS_VM_READ, PROCESS_VM_WRITE #win32con里面放的是一些pywin32中的常量,这两个分别是进程的读取权限和写入权限
from win32process import GetWindowThreadProcessId #通过窗口句柄获取进程ID
from ctypes import WinDLL, c_ulonglong, byref
"""
WinDLL这个函数是导入系统的动态链接库,因为pywin32中并没有直接可以对内存进行读取和写入操作的函数,对于32位的程序,我们可以用kernel32.dll中的readmemor这些,
但是到了64位程序我们发现没有用了,这也是主要难点,所以需要通过ntdll.dll中的某些未公开的api来达到小青龙们不可告人的秘密
64位地址的长度是8字节,python中调用api时默认的int只有4个字节,所以用ctypes中的c_ulonglong类型来存放我们的数据,避免精度的丢失
byref python本身是没有指针的概念的,所以我们需要通过这个参数来传递指针
"""
导入包结束,首先我们需要定义两个函数,一个函数用来读取内存,一个函数用来写入内存
读取的函数:
[Python] 纯文本查看 复制代码 def ReadVirtualMemory64(hProcess,addr, n=8):
# 这里定义一个函数来读取,传入三个参数,第一个是进程句柄,第二个是我们要读取的地址,第三个是要读取的长度
ntdll = WinDLL("ntdll.dll")
addr = c_ulonglong(addr)
ret = c_ulonglong()
BufferLength = c_ulonglong(n)
ntdll.NtWow64ReadVirtualMemory64(int(hProcess), addr, byref(ret), BufferLength, 0)
"""
这个函数并不是一个公开的API,找了很多文献才研究出来怎么用python去调用它,他一共有五个参数
第一个参数是我们通过OpenProcess获取的进程句柄,在python中要记得把这个句柄转换成int类型,默认其实是个句柄类型,不
转换会出错,
第二个参数其实就是我们要读取的地址,我们辛苦找到的基址和便宜终于有了用武之地
第三个参数是一个指针,我们通过ctypes中的byref方法可以将一个指针传进去,函数会把读取到的参数放进这个指针指向的地方,
在这里也就是我们的ret中
第四个参数是我们需要读取的长度
第五个参数也是一个指针,存放实际读取的长度,需要的话可以传一个参数,这里我偷懒填的0
"""
return ret.value # c_ulonglong的类型中,他的数值是放在他的属性value中的,所以返回的时候我们只需要获取value中存放的数值就好了
写入的函数:
[Python] 纯文本查看 复制代码 def WriteVirtualMemory64(hProcess,addr, s, n=8): # 这个函数与读取的其实是一样的,区别只是一个是读一个写,不作介绍了,参考读取的函数,s参数是我们要写入的数据,原谅我懒,你若看懂,便是晴天
ntdll = WinDLL("ntdll.dll")
addr = c_ulonglong(addr)
ret = c_ulonglong(s)
BufferLength = c_ulonglong(n)
ntdll.NtWow64WriteVirtualMemory64(int(hProcess), addr, byref(ret), BufferLength, 0)
其实真实操作的时候,大家可以把这两个函数封装成一个类哈哈哈哈,这样的很多东西就不用定义两遍了,ntdll也可以定义成全局的,省事
然后就是对游戏的操作了,我们要成功操作游戏,就必须获取他的窗口句柄,进程id,最后创建线程句柄去操作,如何获取这些呢,首先我们用一些小助手什么的获取游戏窗口的类以及窗口标题,这个应该不用我说了吧,我相信大家不是问题
#窗口类名 via
#窗口标题 Devil May Cry 5
获取到鬼泣五的窗口类名以及窗口标题,然后下面就是基址利用与修改的实例了
[Python] 纯文本查看 复制代码 if __name__ == '__main__':
hwnd = FindWindow("via", "Devil May Cry 5") # 获取窗口句柄
pid = GetWindowThreadProcessId(hwnd) # 获取线程id
hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE, False, pid[1])
""" 这个函数有三个参数,第一个参数是你需要的权限,我不是很喜欢这种直接获取全部权限,因为有的时候权限太高并不是好事情
容易被检测GG,当然这里是单机无所谓可以随便来
第二个参数是是否继承,一般都是选False,第三个参数是线程的ID,python里一定要记得取第1位的值,也就是第二个值,因为python里
GetWindowThreadProcessId这个函数返回的值有两个"""
#记得我们刚才得到的基址吗,先去地址0x7FF7EE39B518中的值,加上0x60,也就是我们魂石的地址
ret = ReadVirtualMemory64(hProcess,0x7FF7EE39B518, 8)
addr = ret + 0x60
#然后我们调用写入的函数,就可以对魂石随意修改了,想修改成什么大家也可以随便玩,例子里我选择了123456
WriteVirtualMemory64(hProcess,addr, 123456, 4)#这里千万记得,我们的修改的长度是4,4个字节,改多了会有什么后果我也不知道
CloseHandle(hProcess) # 一定一定记得释放资源
然后我们运行一下我们的程序,看看游戏里面魂石的数量变成了123456,成功
运行后界面
当然你们想封装成一个类或者做个界面出来或者弄个多线程都是可以的
okk本期到此结束,附上自己录制的视频版,老实说,其实我是个严肃的人,我不说骚话的,真的
https://www.bilibili.com/video/av47453086
|