yfan 发表于 2011-11-16 23:32

酱了个油~蛋疼的家伙又出现了~魔能字体文件数据结构分析

魔能是个.net游戏,而且贯彻了不加密不混淆的优良作风(不过现在已经够慢了,估计混淆以后速度只能堪比乌龟了)。
因此,这是一个很好玩的玩具(……?),很适合用来做.Net破解的练手,也有足够多的内容用来折腾。
开学以来一直很忙,无暇无地折腾新游戏。

于是在一个月黑风高的夜晚,我溜进学校的某个实验室,由于蛋疼甚矣,不知怎么的想起魔能,觉得鸾霄那帮家伙估计短时间不会填坑了,于是翻出U盘里曾扔进去的魔能的主程序加几个dll并拆了其中一个,做了如下蛋疼的记录。

话说vim真是很好用呢。

PolygonHead.dll 文件
.NET 3.5 引用 XNA 3.1
目标:字体文件格式逆向
使用工具:ILSpy

1.        先是按照当初写提取器时候的记忆,找到了Pipeline模块

2.        由于做的是字体逆向,无视掉AdditiveEddectReader和BiTreeModeReader等等其它Reader,直奔BitmapFontReader。

3.        打开后发现仅有一个Read函数,然而输入的两个参数是ContentReader iInput和BitmapFont existingInstance,猜想还不是调用的最底层,值得商榷。

4.        不管怎么说,按照线索追下去,发现:
        if (existingInstance == null)
        {
                existingInstance = new BitmapFont();
        }
        existingInstance.Read(iInput);

5.        追入BitmapFont的构造器,发现空无一物。只好继续追踪下面的Read方法。

6.        进入BitmapFont.Read,内容很多
        this.mSpacing = 0;
        this.mLineHeight = iInput.ReadInt32();
        this.mBaseLine = iInput.ReadInt32();
        this.mDefaultCharacter = iInput.ReadChar();
        这是开始的一点代码,spacing是啥?本来想要预留一段作为备用数据单元么?
        先继续看。
        先是莫名的mLineHeight,ReadInt32()读取了4个字节
        然后是mBaseLine,也是4字节
        DefaultCHaracter,读了一个char,这个有点复杂,
        观察得知里面检查了char是否占两个byte,所以可能读一个可能读俩。
        GraphicsDevice graphicsDevice = null;
        do
        {
                try
                {
                        graphicsDevice = (iInput.ContentManager.ServiceProvider.GetService(typeof(IGraphicsDeviceManager)) as GraphicsDeviceManager).GraphicsDevice;
                }
                catch
                {
                        Thread.Sleep(1);
                }
        }
        图形设备相关,无视……
        while (graphicsDevice == null);
        GraphicsDevice obj;
        Monitor.Enter(obj = graphicsDevice);
        继续无视……
        try
        {
                this.mTexture = iInput.ReadObject<Texture2D>();
        }
        finally
        {
                Monitor.Exit(obj);
        }
        好吧,这个东西……可以看到字体作为Texture2D格式被读出了,这与曾经做过的工作结论相符,当时就是以Texture2D形式成功读出图片的。

7.        追入Texture2D,发现这东西不小……好吧,到时候毫无疑问需要逆向构造一个此类型,那么可以肯定平台又是.NET没跑了。。。这有点看不懂,SetData<T>里面的<T>是什么?泛型吗?这这这……数据是哪个啊……

8.        囧。才发现Texture2D属于XNA模块啊……不用管它了。

9.        回头关心一下iInput.ReadObject,这家伙在Xna.Framework.Content.ContentReader类中,嗯,没准以后能顺藤摸瓜找个Writer之类的。先留个Flag。

10.        回到Read方法。又有新动作。
        int num = iInput.ReadInt32();
        this.mGlyphs = new Dictionary<char, Glyph>(num);
        this.mCharacters = new Dictionary<char, Character>(num);
        num读取了4个字符。
        话说上面读了一个object怎么知道的有多长啊?莫非用了飘柔就是这么自信?
        看来Xna有自己的方式啊。
        或者,那个类中只有保存好的相关信息,而不存在数据!
        那个Dictionary属于System.Collections.Generic,好吧无视掉。我们继续。
        for (int i = 0; i < num; i++)
        {
                Glyph glyph = Glyph.Read(iInput);
                this.mGlyphs.Add(glyph.Character, glyph);
                Character value = Character.FromGlyph(glyph, Vector4.One, this.mTexture.Width, (float)this.mTexture.Height);
                this.mCharacters.Add(glyph.Character, value);
        }
        大量的读数据!num注目!
        Glyph是一个不长的类,但是读了很多东西,全粘出来好了。

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using System;
namespace PolygonHead
{
        public struct Glyph
        {
                public char Character;
                public Point Origin;
                public Point Size;
                public int AdvanceWidth;
                public int LeftSideBearing;
                public bool ForceWhite;
                public static Glyph Read(ContentReader iInput)
                {
                        Glyph result;
                        result.Character = iInput.ReadChar();
                        result.Origin = default(Point);
                        result.Origin.X = iInput.ReadInt32();
                        result.Origin.Y = iInput.ReadInt32();
                        result.Size = default(Point);
                        result.Size.X = iInput.ReadInt32();
                        result.Size.Y = iInput.ReadInt32();
                        result.AdvanceWidth = iInput.ReadInt32();
                        result.LeftSideBearing = iInput.ReadInt32();
                        result.ForceWhite = iInput.ReadBoolean();
                        return result;
                }
        }
}
        懒得算多长了……写的时候再看吧。
        int num2 = iInput.ReadInt32();
        哦哦,又来了。
        this.mKerning = new Dictionary<char, Dictionary<char, int>>();
        for (int j = 0; j < num2; j++)
        {
                char key = iInput.ReadChar();
                char key2 = iInput.ReadChar();
                int value2 = iInput.ReadInt32();
                Dictionary<char, int> dictionary;
                if (!this.mKerning.TryGetValue(key, out dictionary))
                {
                        dictionary = new Dictionary<char, int>();
                        this.mKerning.Add(key, dictionary);
                }
                dictionary.Add(key2, value2);
        }
        看来是很简单的数据结构嘛。回头用UE看一下,收工!

11.        最后,整理下流程:

<1>        mLineHeight                        4                                int32
<2>        mBaseLine                        4                                int32
<3>        mDefaultCharacter        1 or 2                        char
<4>        mTexture                        不定                        object,Texture2D
<5>        num                                        4                                int32
<6>        Characters                        num*(26 or 27)        struct
<7>        num2                                4                                int32
<8>        Kernings                        num2*(6 or 8)        struct

<end>

注:Kerning
        n. 1. 【印】(铅字面之)上下的突出部分
        vt. 1. 使(铅字面)上下突出 2. 将(铅字上下突出部分)做平

完成
2011.11.16 22:15

yfan 发表于 2011-11-16 23:43

后记:
看来最后一部分的格式乱掉了。
嗯毕竟是手机发帖不能强求太多。

这文件格式的关键就是里面那个隶属于xna的Texture2D
既然支持很多格式,还有压缩大小的空间吧(大概)?

这里不禁又要吐槽了,你魔能在启动的把所有东西都加进内存是要闹哪样?

便便藏tt 发表于 2011-11-17 11:33

魔能是个.net游戏,而且贯彻了不加密不混淆的优良作风(不过现在已经够慢了,估计混淆以后速度只能堪比乌龟 ...
yfan 发表于 2011-11-16 23:32 http://bbs.blacksheepgame.com/images/common/back.gif


    虽然不太明白老y在这月黑风高的日子里到底在偷偷摸摸搞了些啥,不过……总之……嘛,辛苦了

另外魔能占内存大的缘故就是因为不管三七二十一全都启动时塞进内存了么……啊……%(!@&(¥

yfan 发表于 2011-11-17 15:31

回复 3# 便便藏tt


   喂喂,全塞进内存这不是当年汉化时候就发现了么我只是二次吐槽啊……天然了吧你
页: [1]
查看完整版本: 酱了个油~蛋疼的家伙又出现了~魔能字体文件数据结构分析