1. 程式人生 > >MonoTouch 二三事(一)

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

 好了,看到.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:

習慣讓我把他放入google裡搜尋了一下,結果又有意外發現

看看,這個應該是最新版的官方用來更新的地址,裡面應該永遠是最新的。沒說的,裡面三個檔案都下來,

於是乎在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,處理其。。。。