发表于 2020-6-20 23:38

申请会员ID:RURUKnight


1、申 请 I D:RURUKnight
2、个人邮箱:KnightOfRURU@msn.com
3、原创技术文章:
标题:EPIC免费游戏《SAMURAI SHODOWN NEOGEO COLLECTION》中资源的提取方法
//不知为何插入不了图片的说,此处应有游戏主界面图片
前不久EPIC提供了免费游戏SAMURAI SHODOWN NEOGEO COLLECTION,其实就是把街机侍魂搞了个模拟器,然后凑合些资源就拿来骗钱(然而免费)。其模拟器性能拙劣,唯一有点意思的也就是图片、音乐和视频了。
这些数据都被打包在安装目录下的Bundle目录下,非常庞大如图所示:
/*不知为何插入不了图片的说,以下数据来自于命令提示符:
2020/06/1123:24       637,231,110 bundleInterviews1.mbundle
2020/06/1123:26       481,307,002 bundleInterviews2.mbundle
2020/06/1123:27       335,458,964 bundleInterviews3.mbundle
2020/06/1123:32   1,077,913,072 bundleInterviews4.mbundle
2020/06/1123:36   1,037,097,669 bundleInterviews5.mbundle
2020/06/1123:38       608,174,005 bundleInterviews6.mbundle
2020/06/2008:44       362,214,865 bundleInterviews7.mbundle
2020/06/1123:40       331,334,555 bundleInterviews8.mbundle
2020/06/2008:47       946,349,680 bundleMain.mbundle
2020/06/1123:43      70,995,638 bundleMenus.mbundle
2020/06/1123:44       304,457,654 bundleSamuraiShodown.mbundle
2020/06/1123:46       499,600,436 bundleSamuraiShodown2.mbundle
2020/06/1123:48       489,450,460 bundleSamuraiShodown3.mbundle
2020/06/1123:50       489,695,096 bundleSamuraiShodown4.mbundle
2020/06/1123:52       458,020,631 bundleSamuraiShodown5.mbundle
2020/06/1123:53       314,726,247 bundleSamuraiShodown5Special.mbundle
2020/06/1123:55       416,457,053 bundleTimeline_00.mbundle
2020/06/1123:56       493,675,338 bundleTimeline_01.mbundle
2020/06/1123:57       264,045,358 bundleTimeline_02.mbundle
2020/06/1200:01       899,176,553 bundleTimeline_03.mbundle
2020/06/1200:01      94,318,522 bundleTournament1.mbundle
2020/06/1200:01      38,304,641 bundleTournament2.mbundle
2020/06/1200:02      61,389,048 bundleTournament3.mbundle
2020/06/1200:02      45,019,145 bundleTournament4.mbundle
2020/06/1200:02      17,554,393 bundleTournament5.mbundle
2020/06/1200:02      38,413,170 bundleTournament6.mbundle
2020/06/1200:02      82,317,725 bundleTournament7.mbundle
            27 个文件 10,894,698,030 字节
*/
这里的东西基本上也就占整个游戏容量的99.9%左右吧,所以我想方设法地打算提取其中的内容。
通过分析某个文件的内容(没错,就是最后上图最后一个文件:bundleTournament7.mbundle),我们用WinHex分析其中文件内容如下:
/*不知为何插入不了图片的说,以下来自于WinHex截图的模拟:
Offset      01234567   89 10 11 12 13 14 15

00000000   62 70 6C 69 73 74 30 305F 12 00 00 00 11 54 6F   bplist00_   To
00000016   75 72 6E 61 6D 65 6E 745F 37 2E 77 65 62 6D 4F   urnament_7.webmO
00000032   12 04 E8 11 49 1A 45 DFA3 9F 42 86 81 01 42 F7   ?I E撸烞? B?
00000048   81 01 42 F2 81 04 42 F381 08 42 82 84 77 65 62   B? B? B倓web
00000064   6D 42 87 81 04 42 85 8102 18 53 80 67 01 00 00   mB? B?S?g   
00000080   00 04 E8 11 19 11 4D 9B74 BC 4D BB 8B 53 AB 84   ?M泃糓粙S珓
00000096   15 49 A9 66 53 AC 81 6E4D BB 8B 53 AB 84 16 54    IゝS?nM粙S珓 T
00000112   AE 6B 53 AC 81 CB 4D BB8C 53 AB 84 1F 43 B6 75   甼S?薓粚S珓 C秛
00000128   53 AC 82 11 C0 4D BB 8E53 AB 84 1C 53 BB 6B 53   S瑐 繫粠S珓 S籯S
00000144   AC 84 04 E8 0C 7E EC AB00 00 00 00 00 00 00 00   瑒 ?~飓      
*/
毫不意外地,我们看到了这个包里包含的文件只有一个:Tournament_7.webm,稍后的字节中顺利地找到了明文的webm文件头内容,说明文件是以明文形式打包在这里的。
本打算根据这个去解包所有的文件,然而这时候我看到了几乎明文的文件存放位置记录,但是似乎文件清单的前后的数据并不是直接的偏移量和数据大小。于是我试图搜索关键字符串“Tournament_7.webm”,看是否存在单独的Index记录。结果找到了文件Manifest.plist,其中文件内容相对应的为:
<key>bundleName</key>
                                <string>bundleTournament7.mbundle</string>
                                <key>files</key>
                                <dict>
                                        <key>Tournament_7.webm</key>
                                        <dict>
                                                <key>offset</key>
                                                <integer>37</integer>
                                                <key>size</key>
                                                <integer>82317641</integer>
经验证,和事实一致……于是事情变得简单。于是我写了个小程序来处理这件事。使用的VS2017,控制台应用程序,C#编写。理论上不存在任何危险操作。以下为原代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace GetNaKoRuRu
{
    class Program
    {
      static void Main(string[] args)
      {
            if (System.IO.File.Exists("Manifest.plist"))
            {
                Console.WriteLine("列表文件已经找到,正在读取列表文件!");
                XmlDocument xd = new XmlDocument();
                xd.Load("Manifest.plist");
                var root = xd.DocumentElement;
                var loop = root.GetEnumerator();
                while (loop.MoveNext())
                {
                  var item = loop.Current as XmlNode;
                  checknode(item);
                }
            }
            else
            {
                Console.WriteLine("请把本程序复制到正确目录下运行!");


            }
            string lastpack = null;
            System.IO.FileStream fs = null;
            int c = 0;
            foreach (var x in results)
            {
                try
                {
                  c++;
                  if (lastpack != x.pack)
                  {
                        if (fs != null)
                            fs.Dispose();
                        if (System.IO.File.Exists(x.pack))
                            fs = new System.IO.FileStream(x.pack, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                        else
                        {
                            Console.WriteLine("NoSuchFile" + x.pack);
                            break;
                        }
                  }
                  byte[] buffer = new byte;
                  fs.Seek(x.offset, System.IO.SeekOrigin.Begin);
                  fs.Read(buffer, 0, buffer.Length);
                  if (System.IO.File.Exists(x.name)) continue;
                  using (var w = System.IO.File.Create(x.name))
                        w.Write(buffer, 0, buffer.Length);
                  Console.Title = c + "/" + results.Count;
                  Console.Write('.');
                }
                catch(Exception ex)
                {
                  Console.WriteLine(ex.Message);
                  Console.WriteLine(ex.StackTrace);
                  Console.WriteLine("{0} in {1}(offset:{2} size:{3}", x.name, x.pack, x.offset, x.filesize);
                  Console.ReadKey();
                }
            }
            Console.WriteLine("Done!");
            Console.ReadKey();
      }
      struct fileinfo
      {
            public string pack;
            public string name;
            public long offset;
            public int filesize;
      }

      static string pickname = null;
      static fileinfo lastone;
      static List<fileinfo> results = new List<fileinfo>();
      private static void checknode(XmlNode item, int Level = 0)
      {
            if (item.Value != null)
            {
                if (Level == 4 && item.Value.EndsWith(".mbundle"))
                { pickname = item.Value; }
                if (Level == 5 && !(item.Value.Contains("offset") && item.Value.Contains("size")))
                { lastone = new fileinfo() { pack = pickname, name = item.Value }; }
                if (Level == 6 && long.TryParse(item.Value, out long t))
                {
                  if (item.ParentNode.PreviousSibling.InnerText == "offset")
                        lastone.offset = t;
                  else
                  {
                        lastone.filesize = int.Parse(item.Value);
                        results.Add(lastone);
                  }
                }
            }
            if (item.HasChildNodes) foreach (XmlNode i in item.ChildNodes)
                {
                  checknode(i, Level + 1);
                }

      }
    }
}

为了帮助没有编译环境的同志,我将相应文件压缩打包改后缀名传上来(传不上来……),压缩包大小3K,程序本身大小为7K左右,放置到数据包所在文件夹下运行即可,运行后不会影响到这款游戏的运行!运行时可能要1.5G左右内存用于转移个别大文件。运行结果会将所有文件解包到当前目录,请自行整理和剪切走需要的文件!

Hmily 发表于 2020-6-22 16:01

抱歉,未能达到申请要求,申请不通过,可以关注论坛官方微信(吾爱破解论坛),等待开放注册通知。

分析过程有些简单。
页: [1]
查看完整版本: 申请会员ID:RURUKnight