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,處理其。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM