ce吧 关注:196,555贴子:3,801,614

回复:【瞎写】讲讲如何用 Modern C++ 撸一套内存读写函数 (0)

只看楼主收藏回复

说起来为什么一个图片会扯出来这么多内容,本来我是不打算将这些基础到不能再基础的概念的,但是想到如果连这些都不懂,那连图中的那第一行注释为“触发”代码都看不懂,所以便还是讲了些,大概明白点也好继续看下去。
继续要讲的是基址和偏移的本质,我是非常有想找到我N年前录得视屏,就不用再把这里废话一遍了,但是我找不到了。
基址是啥?他就是个地址,可以转换成指针来进行存取操作,那为啥不叫地址呢,因为它通常和偏移一同使用,偏移是基于此地址的,所以叫“基”址。
而且它是每次运行都不变的,因为他是个全局变量或者static变量(单例模式),在游戏开发中多为后者,这个static变量是指针,指向某个类或结构的唯一实例。
偏移是啥?他就上面最后一例代码*(int*)(i+4)中的4。
程序设计语言,不论是面向过程还是面向对象,都具备定义和操作数据结构的能力,上面的S就是最简单的数据结构,现在来把他和它的成员变一下名字:
struct soldier {
int hp;
int mp;
};
感觉是不是微妙了一些呢,这不就是游戏中常见的概念吗?一个“战士”拥有最基本的“血量”和“蓝量”两个属性,这就是一个结构,那之前对于这个结构的操作,都可以理解为WG对游戏内数值的修改行为。
一般情况下我们会通过逆向来找出“基址”,当然也可以使用别人找到共享出来的,假设我们通过调试器找到了某个游戏中角色的基址为0x1234,并且通过观察内存数据发现了偏移0处为hp,偏移4处为mp,那么我们对其hp和mp的修改就可以这样操作(注意我们是没有结构体的定义的,我们只知道基址和偏移):
*(int*)(*(int*)0x1234 + 0) = 999999;
*(int*)(*(int*)0x1234 + 4) = 999999;
是不是很像图片里的代码了,只是比图里的少了几层而已。但是跟之前的例子对比,又有些不太一样,如果按之前的例子写,那应该是 *(int*)(0x1234 + 4) = 999999 啊,为什么会多了一次*(int*)呢?这并不是写错了,而是因为我上文说的单例模式,也就是基址存在的本质原因。
何为单例模式,它是一种设计模式,作用是限制一个结构只能定义一个对象,考虑游戏中角色的概念,通常角色只有一个,就是你所操作的那一个,所以角色是非常适合使用单例模式的结构。
对于常规的结构比如前文的S,我们可以:
S s1;
S s2;
这是完全没有问题的,他们是类型相同的两个不同对象。
而对于单例结构,只允许定义(实例化)一个对象,它定义对象的方式是通过定义一个静态的实例指针,并保存在结构中,暴露接口给外部访问,而其构造函数设为私有,因此外部不可以访问其构造函数,也就无法定义一个实例(超纲,不懂啥是构造函数的自行搜索或者跳过这块)。
而所谓基址,其实就是这个结构中保存的指针的地址,这个static指针和全局对象拥有相同的特性,即他是硬编码在二进制文件中的,体现在汇编中就是一个立即数,是由程序本身的加载的基地址加上其在.data节中的rva所得到的(听不懂没关系,就知道他是不变的就行了)。
因此0x1234其实是这个指针的地址,指针保存的值才是我们角色的地址,所以要先对这个指针的地址进行一次解引用取值操作,得到角色的地址,就可以代入之前的例子了。
搞清楚了基址和偏移的本质,再来看看多级偏移,上个例子我们只有一个偏移量,要么是4要么是0,那是因为这个角色的结构太简单了,而一个好玩的游戏,人物的属性必然不会只有血和蓝,起码他还得有技能吧。
那么看这样一个结构:
struct skill {
char name[10];
int damage;
};
这是一个简单的技能结构,他有两个成员,一个是技能的名字,一个是技能的伤害。
我们来定义一个技能
skill taunt;
taunt.name = "嘲讽"; //这里不能这么用,对字符串的操作不可以直接赋值,暂且不讲,就当成可以这么用
taunt.damage = 800;
以上定义了一个名为嘲讽的技能,它对敌人造成800点伤害,但是这个技能暂时不属于任何人。
我们再改一下角色的定义:
struct soldier {
int hp;
int mp;
skill* skl;
};
然后我们做这样一个操作:
soldier s;
s.hp = 1000;
s.mp = 400;
s.skl = &taunt;
这样我们通过给角色结构的成员中加入了一个技能的指针,使我们的战士得到了一个嘲讽的技能,为什么用指针而不直接在结构中定义技能呢,这是游戏设计的问题,这里不详细讨论。
总之无论是指针还是直接定义,都有读写它的办法,多级偏移正是由于使用指针的设计方式而出现的。
接下来写代码来修改我们角色嘲讽技能的攻击力(你可以先不往下看,自己尝试写一下):
*(int*)(*(int*)(*(int*)0x1234 + 8) + 10) = 999999;
emmm...这样看起来和图片里的更像了,来看一步步分析。
*(int*)0x1234 按上文所述是拿到人物的地址,+8是跳过hp和mp成员计算出嘲讽技能指针的地址,因为计算地址要int型,所以算完后还要将其转为指针,再对其解引用,*(int*)(*(int*)0x1234 + 8) 即是嘲讽技能的地址。
此时我们拿到的是另一个结构的地址,如果你真理解了上面的内容,那你应当知道+10是何意:为的是跳过技能名字的数据,即10个char,每个char是一个字节,因此+10(此处依然不考虑对齐,有好奇心的同学自行搜索)。
跳过10个字节后 *(int*)(*(int*)0x1234 + 8) + 10 便是damage成员的地址了,damage是int型数据所以将其转换为int*进行访问,*(int*)(*(int*)(*(int*)0x1234 + 8) + 10) = 999999 自然便是修改其为999999。
我不会再写例子还原图片中的5级偏移,因为原理和2级偏移是一样的,你可以想象一下,有一个地牢、地图里有个人、人身上有个袋子、袋子里有很多小袋子、某个小袋子是装战利品的、战利品有自己的属性(名称、价值、描述、重量、是否可出售等等),那如果你要修改这个物品的价值,那你起码需要几次偏移呢?
我再结合之前的某个东西提个问题并做出解答,建议你先自己做出来再看答案:如果技能结构的伤害成员是float类型,你该怎么修改他的值呢?答案在文章末尾。


19楼2018-01-27 13:01
收起回复
    很厉害啊,希望这是以后的我。
    window程序设计 第三版中的内容


    IP属地:湖北20楼2018-01-27 18:09
    收起回复
      加精


      IP属地:广东来自Android客户端22楼2018-01-28 01:01
      回复
        这玩应其实就是指针,指针的内容和指针类型这三东西,让你说的挺复杂。
        要是到了数组指针和指针数组可咋整


        IP属地:北京23楼2018-01-28 20:47
        回复
          mark


          IP属地:上海24楼2018-01-31 14:33
          回复
            这是我第二次看到有人这样写C++。。。。。


            25楼2018-02-03 10:42
            回复
              还好我看得懂


              IP属地:湖北来自Android客户端26楼2018-02-22 02:16
              回复
                虽然不理解,但先收藏了。


                来自Android客户端27楼2018-08-11 13:32
                回复
                  感谢分享,收藏了~


                  IP属地:湖北28楼2018-09-04 11:02
                  回复
                    懂了,然后呢。。。。。可我还是不会查基址,不会反编译,不会过tp检测。。。。。。


                    来自手机贴吧29楼2018-09-14 11:25
                    回复
                      学习了。


                      30楼2018-10-20 05:02
                      回复
                        mark


                        来自Android客户端31楼2019-01-29 01:07
                        回复
                          mark


                          IP属地:广东32楼2019-03-06 18:20
                          回复
                            给大佬跪了,写的很详细,受教了


                            IP属地:浙江来自Android客户端33楼2019-07-01 03:07
                            回复
                              给大佬跪了,写的很详细,受教了


                              IP属地:广东34楼2022-12-12 21:59
                              回复