作为编程者,你却不会写内存修改器?咸鱼一条!

伊始

  本文主要介绍基础修改器的制作,其中包含实现原理,以及实现步骤。
  不知道小伙伴们有没有遇到过,对游戏人物属性不满?有没有遇到过因为服务器不能停止的运行,但其中一部分数据出错而需要立刻修正?哎,又是需求嘛,有需求总要实现的嘛。接下来我们就上干货了哦!
笑脸

主体

  有需求就要有需求分析嘛,需求分析完毕要做什么呢,当然是初步解决方案啦。这叫按需而做,不做多余的哦!
  这里我们就直接开始,罗列我们要做的事!
可能用到的API如下:

序号 API &博客
1 OpenProcess
2 ReadProcessMemory
3 WriteProcessMemory
4 DLL注入与安全

  那我们就开始吧!
  第一步:也就是我们加快搜索速度的一步:确定空间范围。首先应用程序的“起始位置我们事要知道的”,还有就是在这个空间中那一部分?是exe中?亦或具体的dll中?,如果知道的话,那就缩小范围了。

打开进程
HANDLE WINAPI OpenProcess(
    _In_ DWORD dwDesiredAccess,
    _In_ BOOL bInheritHandle,
    _In_ DWORD dwProcessId
    );

  如果需要获取固定dll的空间可以采用“DLL注入与安全中的方案” 获取目标空间具体的范围。以缩小范围,加快定位。(注:如果你不知道,请忽略)
  第二步:第一步确定了空间,接下来当然是根据空间范围搜索啦!
  搜索前,你需要知道的一部分知识:

  1. 内存空间都是机器码,(这里我们采用32位cpu,可表示地址范围0x00000000-0xffffffff,数据最小范围char 8位,十六进制数对应0x00)。
      就是下面这样的图:在这里插入图片描述
      如果你是游戏爱好者,或者是开发者,你是知道,你要找的“数据类型“什么。如果你不知道的话,下面由几种推荐的:
      a. 第一种char型,8位对应十六进制0x00,也就是上面图片中”69“的整体。当然啦,是区分有符号和无符号的啦,这个别忘记啦!
      b.第二种short int型,16位对应十六进制的0x0000,也就是上面图片中2个69”的大小。和char型一样也是有符号和无符号之分。
      c.第三种,int型,32位对应十六进制0x00000000,也就是上面图片中4个"69"的大小,和char型一样也是有符号和无符号之分。
      d.第四种,lont lont int型,64位对应十六进制0x0000000000000000,也就是上面图片中8个"69"的大小,和char型一样也是有符号和无符号之分。
      e.第五种,float型,32位,对应十六进制的0x00000000,也就是上面图片中4个"69"的大小,但由于float采用的是科学计数法方式计数的,所以和上面还是有一些区别的。
      f.第六种,double型,64位,对应十六进制的0x0000000000000000,也就是上面图片中的8个”69”的大小,但由于double采用的是科学计数法方式计数的,所以和float是类似的。
      特别的,还有字符串类型数组类型自定义类类型比如类似于DECIMAL,要注意!推荐每4k作为一页读,毕竟内存4k对齐,当然也可以不是,只不过会查找熟读变慢罢了。

接下来我们就开始搜索吧!
根据所需要的类型读存操作。

读内存
BOOL WINAPI ReadProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPCVOID lpBaseAddress,
    _Out_writes_bytes_to_(nSize, *lpNumberOfBytesRead) LPVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T * lpNumberOfBytesRead
    );

  由于空间范围越大,我们根据我们设定好的数据类型,搜索到的东西越多。
  对于游戏来说,我们可以采用以下方法。
  1. 试探法:在第一次的基础上,人为增加数值搜索增加多少数值-精确、增加到多少-精确、数值增加了的-模糊。同理,减少也这样 。也可以搜索,不动的数值亦或变动的数值
  2.程序反编译法:这个这里不做过多的介绍,涉及到反汇编定位。
  对于业务程序来说,有时候试探法是不可取的,你懂的!!!。
  对于这种如果是核心的,请找专业人士来进行内存定位,否则一失足成千古恨!切记切记。

  第三步:当我们第二步,找到了内存地址后,修改成我们想要的数值即可。

BOOL WINAPI WriteProcessMemory(
    _In_ HANDLE hProcess,
    _In_ LPVOID lpBaseAddress,
    _In_reads_bytes_(nSize) LPCVOID lpBuffer,
    _In_ SIZE_T nSize,
    _Out_opt_ SIZE_T * lpNumberOfBytesWritten
    );

  思考
  上面思考的仅仅是基本数据类型,要知道内存中,不仅仅可以存储基本数据,也是可以存储地址的。假如每次生成的数值都是,动态申请的怎么办,前面我们修改的地址内容有可能无效了呢,因此,这就涉及到了,指针寻址,找到最初的指针地址,或许是一级指针、二级、甚至是多级指针
  多级指针的寻找方法。假如,虽然存在多级指针,但是指针不存在频繁的动态申请空间和释放的情况下。我们在上面找到的数据,的地址链表中,备份一次,再次对改变数值时,进行,判断是哪个地址,被访问了?(当然被修改也是可以的,主要是实现不同。)被访问,在汇编语言中,也就是该内存地址存在数据传送指令,因此找到即可,这里不做详细描述。我们只要找到每次被访问指针的地址,就可以找到初级的指针。

  假如程序采用的是系统调用动态申请空间,为了不加重系统的负担,不可能非常频繁的申请和释放,因此我们只需要使用(动态规划方法)找到最初的指针地址即可,然后根据指针的偏移获取指向的数据。
  假如程序采用的是内存池。好家伙,不同的内存池又自己的规则,这是,有可能采用频繁的向内存池中申请。这时,我们每次就需要扫描,整个内存池,来定位初级指针的地址了。

结束语

  每一项技术都有存在的意义,知道了如何读写内存,那么从中就可以了解到如何防止被读内存,技术无好坏,好坏在于用的人!。

发布了5 篇原创文章 · 获赞 10 · 访问量 74

猜你喜欢

转载自blog.csdn.net/itsaoght/article/details/105425539