MonoTouch 二三事(一)

 

题外话: 最近工作室打算接个iPhone + iPad + Android Phone + Android Pad 杂志客户端的活,以前只只储备了Android的开发知识, 因为个人原因,很讨厌Apple的作风,所以从没打算做Apple的开发,也就没有储备相关知识,临时只好抱佛脚,看看能否用自己 会的知识来减少iOS开发的学习成本与时间,看了Web App的AppCan和PhoneGap,没细看,因这两个都是用html做开发,最后找到了 MonoTouch,Mono园子里的应该不陌生,我自己也鼓捣很长时间,MonoTouch是Mono被从Novell 转移到新东家xamarin后的一款商业软件, 基于Mono,以前在Novell 的时候是开源的,转给xamarin后从Mono和MonoDevelop的源码树中被移除了,成了商业软件。

对MonoTouch主要关心授权价格、性能、以及编译后生成文件的大小,第一个官网上有,个人一年399刀,第二个,根据我对其进行的 简单分析来看,其运用了AOT编译技术及llvm优化技术,生成经过裁剪的native程序,绕过ios平台上禁止jit的限制,性能应该是不错的, 但没有真机实验,第三个问题就比较难以解答了,我经过google+baidu后没找到一个对这方面进行评论的,于是决定自己操刀。

环境:Win7 x64 + VM9 + Mac OS X Lion 10.4

MonoTouch从官方网站下载的是一个web安装程序,在填写信息点击下载后下载的是 http://download.xamarin.com/Installer/MonoTouch/monotouch-eval.dmg 这个文件,免费试用版,无法编译真机程序。 下载后比较好奇,直接右键用7z打开一级一级目录找下去最后找到了

 好了,看到.NET 的程序就顺手发送到ILSPY,结果就找到了

那个啥。。我真不是故意的,完整版的下载url就出来了。。。你说我不去下载也。。。于是乎。下载后

此处省略X千字针对5.2.12版本的[处理]过程,因为我要介绍的是6.0.6版本,最新的。

话说,我把5.2.12都[处理]完了,能真机编译了然后有次启动MonoDevelop时,居然问我更新不,我更新后就变成了6.0.6版本,把我的[处理]过的文件给替换了,又不能用了。

既然他自动更新了,肯定有个临时文件,把新版的安装包弄了下来,于是乎,我在并不熟悉的Mac os x里一顿大搜索啊,其中各种google+百度一言难尽啊。。最终找到了

/Users/binsys/Library/Caches/MonoDevelop-3.0/TempDownload/4569c276-1397-4adb-9485-82a7696df22e-2060006000.pkg

/Users/binsys/Library/Caches/MonoDevelop-3.0/TempDownload/index.xml

打开xml文件你会发现一个url:

http://download.xamarin.com/priv/1edf75dc763a09195fbdfb81cf367/MonoTouch/Mac/monotouch-6.0.6.pkg

习惯让我把他放入google里搜索了一下,结果又有意外发现

http://xamarin.com/download/installer/Mac/MonoTouch/full/InstallationManifest.xml

看看,这个应该是最新版的官方用来更新的地址,里面应该永远是最新的。没说的,里面三个文件都下来,

http://download.mono-project.com/archive/2.10.9/macos-10-x86/11/MonoFramework-MDK-2.10.9_11.macos10.xamarin.x86.dmg 这个安装正常

http://download.xamarin.com/monodevelop/Mac/MonoDevelop-3.0.5.dmg 这个安装正常

http://download.xamarin.com/priv/1edf75dc763a09195fbdfb81cf367/MonoTouch/Mac/monotouch-6.0.6.pkg 这个嘛。。。安装时得激活。所以我们接下来主要手工处理它。

于是乎在windows里右键7z 打开monotouch-6.0.6.pkg解压到目录,进入解压后的目录的monotouch.pkg目录,发现4个无后缀的文件Bom,PackageInfo,Payload,Scripts,根据百度+google告诉我,这是一个用Apple的PackageMaker打包的应用程序,支持启动前脚本,运行后脚本,Payload这个文件可用7z打开里面几层都能用7z打开,里面是这个安装包的主要内容,Payload解压后最终得到

存着,备用,接下来Scripts这个也能用7z在解压了几层后打开,Scripts解压后最终得到postinstall,preinstall,两个文件,都是bash脚本,顾名思义,一个是安装前运行的,一个是安装后运行的,右键,notepad2打开preinstall,文件较大啊,根据内容是一个叫Makeself 2.1.5生成的,具体功能和在windows里把exe转成bat差不多,他把一个程序转成了sh脚本,嗯,根据本文件内容是解压后运行解压缩后的user-driver(其实就是个自解压~),我从本文件后面的二进制数据提出了取名为InstallationCheck.gz的文件,好吧,伟大的7z再次。然后得到client,driver,user-driver,libMonoPosixHelper.dylib,打开user-driver一看,是段sh脚本调用了client这个程序,也就是说这个程序就是我们要[处理]的文件,7z打开之。。

好了,伟大的7z告诉我们这是个Mac OS X平台的可执行文件,Mach0类型,其和exe文件在windows上是一个意义。嗯这下不是.NET的了,ILSPY不管用了,好吧,拿出我的静态反汇编神器-IDA6.1,加载本文件,等待IDA分析完毕,

看见我选中那个函数名没?我老亲切了,因为我以前阅读过mono打包、绿化、移植相关的代码。。知道这个程序是使用mono的一个附属工具mkbundle生成的,开源的,看其源代码,发现就是把mono写的.NET程序以及其依赖项打包到一个mac os x的可执行文件,打包是压缩了那些被打包的东西,具体可以自己看代码

咱关注的是mkbundle源码template_z.c这个文件里的mono_mkbundle_init函数,用于解压并加载应用程序程序集并建立程序域在内存,

ptr = (CompressedAssembly **) compressed;

这句中的compressed指向压缩程序集的表,根据mkbundle.cs内容来看就是那些程序集被打包后存的位置,好了我们要在client这个程序里找到这个表位置。

在左侧函数列表里双击,IDA的由此反汇编窗口就显示这函数的汇编代码,嗯,轻轻地摁下F5,执行hex-rays的反编译插件,这时我们看见了上图的代码。。。明显的是标黄的

双击进去,位置找到了,那他是什么结构呢?

typedef struct {
    const char *name;
    const unsigned char *data;
    const unsigned int size;
} MonoBundledAssembly;


typedef struct _compressed_data {
    MonoBundledAssembly assembly;
    int compressed_size;
} CompressedAssembly;

经过查看程序以及其头文件,我们发现了此结构,把他保存成.h文件,打开ida的菜单:File -> Load file -> Parse C header file,找到保存的头文件,打开它,然后ida提示解析成功,IDA菜单:View -> Open subviews->Local types,可以看到我们刚才解析的头文件的结构体

选中MonoBundledAssembly右键选择Synchronize to idb,CompressedAssembly也同样,然后回到双击_compressed后的反汇编位置

因为是结构体指针的指针,所以这里应该是一堆指针组成的数组,也就是4字节为一个指针,N个4字节,每个指向某个数据区域,是个偏移值,在_compressed上选中,然后快捷键ctrl+o,将其转成偏移,

下一个__data:002C0884也快速转成offset,知道4字节都是0,表索引结束。看见ida自动生成的offset名称,_assembly_bundle_client_exe,双击,跳到

嗯,有点结构体的样子了,我们在下图里黄色处点击右键

看见那个结构体名称没,选中,下边一堆类似的如_data:002C08EC _assembly_bundle_mscorlib_dll,也这么操作,看结果,

有点儿多,哈,熟悉不?写段代码来吧,根据mkbundle把过程逆向,提取出一堆托管程序集

View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;

namespace unmkbundle
{
    class Program
    {


        public class MonoBundledAssembly
        {
            public UInt32 name_pos;
            public string name;

            public UInt32 data_compressed_pos;
            public byte[] data_compressed;

            public byte[] data;

            public UInt32 size;
        }

        public class CompressedAssembly
        {
            public MonoBundledAssembly assembly;
            public UInt32 compressed_size;
        }


        public class FileItem
        {
            public string FileName;
            public UInt32 Pos;
        }

        static FileItem[] Files = new FileItem[] 
        {
            //new FileItem()
            //{
            //    FileName = "client",
            //    Pos = 0x00396a00
            //},
            //new FileItem()
            //{
            //    FileName = "driver",
            //    Pos = 0x00396a00
            //},
            //new FileItem()
            //{
            //    FileName = "mtouch",
            //    Pos = 0x003a4a80
            //},
            //new FileItem()
            //{
            //    FileName = "mmp",
            //    Pos = 0x0039850c
            //},
            new FileItem()
            {
                FileName = "client6",
                Pos = 0x002c0880
            },
            new FileItem()
            {
                FileName = "driver6",
                Pos = 0x002c0880
            },
            new FileItem()
            {
                FileName = "mtouch6",
                Pos = 0x003aa8c0
            },
        };

        static int base_offset = 0x1000;



        static List<UInt32> CompressedAssemblyItemPosList = new List<UInt32>();
        static List<CompressedAssembly> CompressedAssemblyList = new List<CompressedAssembly>();
        static byte[] Bytes_client;

        static string currentTime = DateTime.Now.ToString("yyyyMMddHHmmss");


        static void Main(string[] args)
        {
            foreach (FileItem fi in Files)
            {
                CompressedAssemblyItemPosList = new List<uint>();
                CompressedAssemblyList = new List<CompressedAssembly>();
                Bytes_client = null;

                string curr_dir = fi.FileName + "_" + currentTime;
                Bytes_client = File.ReadAllBytes(fi.FileName);
                using (MemoryStream ms = new MemoryStream(Bytes_client))
                {
                    using (BinaryReader br = new BinaryReader(ms))
                    {

                        br.BaseStream.Seek(fi.Pos - base_offset, SeekOrigin.Begin);
                        UInt32 pos = 0;
                        do
                        {
                            pos = br.ReadUInt32();

                            CompressedAssemblyItemPosList.Add(pos);
                        }
                        while (pos != 0);
                        br.Close();
                    }
                    ms.Close();
                }


                foreach (UInt32 CompressedAssemblyItemPos in CompressedAssemblyItemPosList)
                {
                    if (CompressedAssemblyItemPos == 0) continue;
                    using (MemoryStream ms = new MemoryStream(Bytes_client))
                    {
                        ms.Seek(CompressedAssemblyItemPos - base_offset, SeekOrigin.Begin);
                        using (BinaryReader br = new BinaryReader(ms))
                        {
                            UInt32 name_pos = br.ReadUInt32();
                            UInt32 data_pos = br.ReadUInt32();
                            UInt32 size = br.ReadUInt32();
                            UInt32 compressed_size = br.ReadUInt32();

                            MonoBundledAssembly mba = new MonoBundledAssembly()
                            {
                                name_pos = name_pos,
                                data_compressed_pos = data_pos,
                                size = size,
                                name = ReadName(name_pos),
                                data_compressed = ReadData(data_pos, compressed_size)
                            };

                            Console.WriteLine(string.Format("从 [{0}] 解压 [{1}] 到 [{2}]", fi.FileName, mba.name, curr_dir));
                            mba.data = DeCompress(mba.data_compressed);


                            if (!Directory.Exists(curr_dir))
                            {
                                Directory.CreateDirectory(curr_dir);
                            }

                            File.WriteAllBytes(Path.Combine(curr_dir, mba.name), mba.data);

                            //SaveMonoBundledAssembly(mba);

                            CompressedAssembly ca = new CompressedAssembly()
                            {
                                assembly = mba,
                                compressed_size = compressed_size
                            };

                            CompressedAssemblyList.Add(ca);
                            br.Close();
                        }
                        ms.Close();
                    }
                }
            }

            Console.ReadKey(true);
        }

        static string ReadName(UInt32 pos)
        {
            string a = string.Empty;
            using (MemoryStream ms = new MemoryStream(Bytes_client))
            {
                using (BinaryReader br = new BinaryReader(ms))
                {
                    br.BaseStream.Seek((long)pos - base_offset, SeekOrigin.Begin);
                    //a = br.reads

                    byte d = 0;
                    int len = 0;
                    while ((d = br.ReadByte()) != 0)
                    {
                        len++;
                    }
                    br.BaseStream.Seek((long)pos - base_offset, SeekOrigin.Begin);


                    byte[] namebytes = br.ReadBytes(len);

                    a = Encoding.ASCII.GetString(namebytes);


                    br.Close();
                }
                ms.Close();
            }
            return a;
        }

        static byte[] ReadData(UInt32 pos, UInt32 compressed_size)
        {
            byte[] ret;
            using (MemoryStream ms = new MemoryStream(Bytes_client))
            {
                using (BinaryReader br = new BinaryReader(ms))
                {
                    br.BaseStream.Seek((long)pos - base_offset, SeekOrigin.Begin);
                    ret = br.ReadBytes((int)compressed_size);
                    br.Close();
                }
                ms.Close();
            }
            return ret;
        }

        public static byte[] DeCompress(byte[] pBytes)
        {
            ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream mStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(new MemoryStream(pBytes));

            MemoryStream mMemory = new MemoryStream();
            Int32 mSize;

            byte[] mWriteData = new byte[4096];

            while (true)
            {
                mSize = mStream.Read(mWriteData, 0, mWriteData.Length);
                if (mSize > 0)
                {
                    mMemory.Write(mWriteData, 0, mSize);
                }
                else
                {
                    break;
                }
            }

            mStream.Close();
            return mMemory.ToArray();
        }

    }
}

结果就是

代码里那个Pos是__data:002C0880 _compressed 的002C0880,用这段代码能解压很多MonoTouch里的被打包的程序,

尽情的把client扔到ilspy里吧。。。

有点儿晚了。下篇继续拔MonoTouch,处理其。。。。

posted @ 2012-11-29 00:44 BinSys 阅读(...) 评论(...) 编辑 收藏