哈希算法原理解析,如何利用哈希函数预测博彩走势浅谈UE4的序列化

2025-07-17

  哈希算法,SHA256,哈希函数,加密哈希,哈希预测/哈希算法是博彩游戏公平性的核心,本文详细解析 SHA256 哈希函数的运作原理,并提供如何通过哈希技术进行博彩预测的方法!【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!

  这是侑虎科技第1465篇文章,感谢作者佐味供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。()

  我写文章,不爱一上来就讲道理、贴代码,而是喜欢先提需求、提问题,然后围绕这个需求的实现再一步步挖掘源码。

  我们的需求是要对游戏中某些核心逻辑的代码做一个快照,这个快照可以保存到磁盘,可以上传到服务器;拿到生成的快照还可以快速地恢复现场。

  针对序列化,其实UE4本身就写了一套。老祖宗UObject身上就有一个Serialize()方法,这个方法负责对整个类里面的「某些信息」做序列化。其中,被UPROPERTY()宏标记的属性,一般都是会被序列化的。

  序列化到磁盘之后,UE4是将序列化的「二进制」数据以.uasset后缀的文件保存起来。使用LoadObject可以重新将uasset文件反序列化成UObject。

  Pacakge的概念这里可以先等同于一个uasset文件在内存里的表示。

  做完后再调用 UPackage::Save() 方法。这是一个静态方法,第一个参数是刚才创建的Package,第二个参数就是要被序列化保存下来的对象,第四个参数是要保存的路径,所以最后代码就大概是这样:

  包的路径。包的路径一般用的是/Game/xxx这种形式,表示要将文件保存在你项目的Content/目录下,包是xxx。

  包的「保存路径」。这里的保存路径指的是「绝对路径」或者「相对路径」,也就是uasset文件最终保存的地方,要对应于包的路径,如果对应上面一条,那么这个路径可能就是 cpp ../../../你的项目名/Content/xxx.uasset 。这里注意两点:文件的后缀名必须是uasset,虽说不是uasset也能保存成功,但是后面反序列化LoadObject时会有问题;保存路径要和包路径对应。

  一波操作之后确实能够保存一个uasset文件到指定路径,大小大概是1KB。虽然总觉得不对,但也没法看清楚里面的内容,也查不了,所以先假定它没问题,继续走下一步反序列化!

  上一步确实能够保存了个xxx.uasset文件下来。 保存完之后再尝试反序列化一波,然后发现根本反序列化不进来。

  这里要记住上面这个LoadObject的路径,它对后面的剧情将起到关键性作用。

  当然试验不会那么容易成功,这里LoadObject出来的结果是个nullptr,证明了反序列化成实例失败了。

  二、反序列化是肯定失败了,那么为何失败了呢,是找不到文件、还是UObject反序列化失败、还是单纯的路径错误?

  如果你查看官方文档( ),FArchive有各式各样数量繁多的派生类。其中负责把UObject序列化到磁盘和从磁盘反序列化生成UObject实例的分别是:

  在开始讲Linker之前,当然需要先简单地带过一下UObject::Serialize()方法。这个方法的作用恰如其名,负责序列化。

  首先是Linker最顶层的基类,叫做FLinkerTable,这个结构体可以参考文章:

  可以得知FLinkerTable的结构与uasset文件的内容是一一对应的。也就是说我们可以猜测,当uasset被加载到内存里的时候,查看FLinkerTable的内容就能知道uasset里面究竟是什么内容。

  首先我们需要知道我们要加载的包的路径,然后调用LoadObject来载入。

  稍微在网上搜过一点点「UE4 加载文件」或者「UE4 序列化」的读者们一定知道,LoadObject有一层层的包装,大概调用流程是这样的:

  这就是我们常常知道的故事的前半截,加载Object会一路调用下去,最终的目的是调用LoadPackage加载一个包。

  可以看到,Tick其实就是在一点点加载uasset的内容进来。具体内容同学们可以自己摸索,主要就是把上面贴的那个图里面的每个部分都读取到Linker中。

  根据网上的资料以及各种断点调试,可以确切地知道一般负责序列化的FArchive的类型为FArchiveSaveTagExports。看它的名字就知道它是负责将UObject中被UPROPERTY宏打上标签的数据序列化的。

  上一小节讲序列化的过程的时候已经讲清楚了序列化的流程,那么现在所需要做的就是在每一个部分打断点,看有没有进来。

  可以看到在目前的情况下,D是true。字面意义上就是Obj不在Outer这个包里面。

  那么Outer是什么呢?Outer就是我们调用UPackage::Save()时传入的第一个参数,也就是要被保存的包。 也就是说,只要我们要保存的UObject所在的包不是我们要保存的那个包,那直接就不能进行序列化了。

  那么UObject怎么指定自己所属的包呢?答案是在NewObject的时候就应该把Package作为Outer传入。

  再运行一次程序,可以看到导出来的uasset文件成功地从1KB变成了2KB!

  经历过上面的摸索和修正,可以先假设序列化这一步是没问题了。接下来要做的是寻找一下反序列化会失败的原因。

  首先怀疑路径是不是错了。于是跟着网上的文章《UE4:四种加载资源的方式》( )改了很多个版本的路径,还是不对。

  前文讲基础知识的时候,讲到第一次加载文件时,最终都是会调用LoadPackageInternal,它又会创建一个FLinkerLoad来负责加载资源。

  于是点开Linker的内容,这个时候可以看到基类的ExportMap的内容。我这里可以清楚地看到,之前序列化的UMyClass是可以正确地被生成的!

  既然Package已经被成功加载,对应的UObject也被正确地序列化,那为什么最后返回的是nullptr呢?

  既然加载进了Package,那么下一步就是从Package中取出数据了。

  为了从庞大的所有UObject中找到我们要的那个,需要使用UObject的哈希值来找。看上面这段代码的作用,就是算出UObject的哈希值,然后根据这个哈希值得到一个迭代器,最后再从迭代器中筛选出目标Object。

  既然目标Object是存在的,而使用算出来的哈希值又找不到它,那么可以推算出哈希值本身是有问题的。

  ObjName也就是传进来的ObjName的名字。ObjName的类型是一个FName,FName有自己的一套计算哈希值的公式。

  Outer的地址在这个场景中,Outer就是前面加载完毕的P ackage本身。

  前面已经验证过加载完的Package基本是没问题的,那么要怀疑的就是ObjName这个参数了。

  1.7 如何才能在Package里面找到对应的Object(以及包的路径的具体意思)

  各位有没有想过,为什么路径里面,最后那个包名要写两次,一定要写 xxx .xxx 呢?

  其实逗点前面的那部分是包名,而后面那部分是「你要加载的Object的名字」。

  回忆起之前断点调试的时候点开的FLinkerTable::ExportMap中,加载进来的内容。

  那么你怎么可能够用MyClass作为ObjName把目标对象正确的搜索到呢。

  结论:LoadObject的路径包括两个部分,逗点前的是包名,逗点后的是对象名。

  由于创建实例的时候,默认的名字是{你的类名}_{序号},所以最终被序列化到uasset文件中的对象也是这个名字。当你用包名.包名来获取对象的时候,就会获取失败。

  1. 在序列化保存对象之前,用UObject::Rename()来把对象名称改成你想要的那个名字,LoadObject的时候,逗点后的对象名就用你指定的名字。

  2. 在每次创建完要被写话的对象之后,记录下它的名字到服务器或者磁盘,下次LoadObject的时候,使用这个记录的名字来读取对应的uasset。

  第一,你要有一个问题,带着问题去看代码,比如在这里我的问题就是如何在Runtime中进行UObject的序列化。

  第二,搜寻网上所有能找到的文章,这一步的目的是让我们最起码对各个类和模块有一个基础的概念。

  第三,一边看代码,一边自己画点UML图把你所知道的类关系组合起来,让自己心里有个概念。

  所以这就是一个大弯,在你时间不会特别紧的情况下,尽量先做好调研,再解决问题,会比你直接撞到问题上提高很多效率。

  虽然UObjects能够正常被序列化,也能够成功地被反序列化成实例,但是目前为止还有一个缺陷,就是它无法被资源浏览器识别和显示出来。

  使用《UE4新增Asset类型》( )文章中提到的方法,可以自己创建一个与自定义资源同类型的资源,但是对于我们自己创建的快照却无法显示。

  为了能够在资源浏览器显示这个快照,需要我们自己去翻对应的源码,找到它不被显示出来的原因。

  写过UI的同学应该对于这段代码理解会很快,列表的数据来源明显就是上面写的 FilteredAssetItems 。

  有了入口,接下来的往回推就简单了,不断地使用IDE的「查找引用」的功能,最终找出数据的来源。

  对于每一个数据来源,在其遍历中都打印出资源的名字来,用来判断我们的自定义资源是在哪一步被过滤掉了。

  这部分UI的代码太过于繁琐,我就直接给答案了。自定义快照资源的过滤不是在资源管理器这个模块中发生的,而是在上流被过滤的。

  资源浏览器的上流便是文件系统,这其中的关系又太过于繁杂,我也没有深入去了解。

  我们理一下思路。在UE4中,每一个资源应该对应一个uasset文件。在上次试验中,我们证明了这个uasset文件是能被读取的。那么有几个疑问:

  这时候就要祭出全局搜索,全局搜素「.uasset」关键字,可以找到引用的地方在FPackageName类。这个类有几个方法和uasset后缀名有关系,其中包括:

  分别查找其引用,最终可以找到类FAssetDataDiscovery。这个类的作用便是搜集所有的Asset信息。

  其中有几句代码,它从DiskCachedAssetDataMap中取出信息,而后读取字段AssetDataList,如果这个字段非空,那么将其加入到LocalAssetResults列表(这个列表就是数据源)。

  讲道理,一个字段既然能被读出来,就肯定有被写入的地方,由于ObjectCount这个名字实在是太泛了,用它来全局搜索实在是不靠谱。这个时候我注意到它上一个返序列化的字段的代码:

  在第一节中我们验证出FLinker::ExportMap不是空的,那么肯定就是在上面这个if中被筛选掉了。

  从进入UObject::IsAsset()方法中,看见UE4对于是否是资源的判断很简单,要符合几个条件:

  首先是看源码。资源部分的代码真的是非常大块,如果想一点一点地啃,不是不行,但是很费时间,而且烧脑。从问题出发确实可以给自己提供一个非常好的入口。

  直接搜索你觉得有用的一些关键字,如上面提到的 .uasset ,直接从uasset的读取开始看。

  第二种方法其实稍微需要一些基础,在搜索之前你要对这部分的设计思路有一个大概的猜测,比如说你要知道:资源读取,必然是从文件的搜索开始,文件搜索之后便是要读取其文件信息,然后进行某些筛选,最后将通过筛选的资源添加到最终的列表。

  顺着自己猜测的思路去看,一一对应上来,在搜索代码和理解代码上就会轻松许多。

  第二个是写了UE4一定时间了,要反过来去深入了解一些基础函数、基础模块。

  比如NewObject()函数,之前我们用的时候都是直接无参数调用。现在踩坑了才知道NewObject()里面其实每一个参数都非常重要,有时候甚至会导致一些莫名其妙的问题。所以对于常用的函数,最好完全读懂它所有的参数,比如NewObject的Outer和Flags参数。

  在前两章中我们已经成功地进行了「一次」序列化和反序列化,这一切都看起来很好很妙。 直到我需要进行第二次序列化的时候,问题就出来了。

  第一次序列化能够非常完美地序列化出对象来,能够正常使用。 然而当我在Editor中重新点击「Play」按钮,再一次要求反序列化的时候,返回的是空。

  从IDE启动Editor之后,可以正常地打开和编辑n次(0 = 0 = +无限)序列化资源(指的就是我们上面序列化的那个文件)。然而只要有一次在没有打开这个资源的情况下,点击Play打开了一次游戏,那么停止游戏之后任你再怎么双击这个文件,都会发现没办法再打开。

  不管是在游戏里面调用LoadObject,还是在资源浏览器中双击一个资源(读者朋友可以自行打断点调试),最终都会调用到LoadObject()函数。

  前面章节中说过我们通过给UObject设置RF_Public的Flags来达到让编辑器能够识别和编辑资源的效果。这个Flags应该是一个很重要的特性。

  回过头来想,为啥Editor创建的资源就可以正常地多次被反序列化呢,它的序列化参数究竟和我们的有什么不同?

  随便打开一个已存在的其他资源,然后在UPackage::Save()函数上打断点,然后在Editor下点击Save。

  想要资源能够被多次序列化,能够在Runtime运行之后还能在Editor下编辑文件,就必须设置这个flag。

  那么为什么设置了RF_Standalone就可以避免Object被置空呢?这就需要我们先全局搜一下这个flag的引用,找到可疑的地方深入去看。

  文末,再次感谢 佐味 的分享,作者主页:,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。()

  特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

  网易直击黄仁勋见面会,他说了这28句线亿美元买个安心:苹果押注美国本土稀土

  AMD 推出最低端锐龙 AI 300 处理器 Ryzen AI 5 330,具完整 NPU

  《赛博朋克2077》游戏Mac版配置要求公布:最低M1芯片+16GB内存

  《编码物候》展览开幕 北京时代美术馆以科学艺术解读数字与生物交织的宇宙节律

地址:广东省广州市天河区88号 客服热线:400-123-4567 传真:+86-123-4567 QQ:1234567890

Copyright © 2012-2025 哈希游戏推荐 版权所有 非商用版本