2008-01-26

玩玩.net的ildasm與ilasm

之前有個朋友拿個vb6的程式給我.
說這是他請別人寫的,需要密碼才能開啟使用,
可是他忘了密碼是甚麼,而且沒有原始碼,
希望看看有沒有辦法"找到".

程式的功能是甚麼已經有點不記得了,因為不是重點.
重點是他的密碼並沒有在資料庫中,因為翻遍資料庫(Access)中的資料表沒有找到任何訊息.
所以猜測密碼應該是在包在程式裡了.

所以直接用十六進位編輯器(ultraedit)開啟那個執行檔.
在檔案中看到password的字樣,而就在附近看到了"疑似"密碼的字串.
然後使用那個密碼試著登入.果然真的是密碼...

不知道各位看了上面這個例子會有甚麼想法!!

回到主題.
今天要介紹的是ildasm跟ilasm的功能.
在msdn上的介紹寫著 :

MSIL 反組譯工具是 MSIL 組譯工具 (Ilasm.exe) 的附屬工具。Ildasm.exe 使用包含 Microsoft Intermediate Language (MSIL) 程式碼的可攜式執行檔 (PE),並建立可以做為 Ilasm.exe 的輸入檔的文字檔。

如果你跟我一樣看了還是不懂他是用來做甚麼的話,那就先往下看.
為了測試,我們要先寫個主控台程式(ConsoleApplication).
主要的功能如下:

讓使用者輸入帳號密碼,
然後判斷是否跟我們設定好的字串相同.
如果相同就顯示welcome,
如果不同就顯示Access Deny.

程式碼如下:

using System;

namespace LoginConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            string UserId = "ajun";
            string UserPwd = "password";

            Console.Write("Please enter your id : ");
            string uid = Console.ReadLine();
            Console.Write("Please enter your password : ");
            string pwd = Console.ReadLine();

            if ((uid == UserId) && (UserPwd == pwd))
            {
                Console.WriteLine("Welcome!");
            }
            else
            {
                Console.WriteLine("Access Deny!");
            }
        }
    }
}

寫好後編譯成exe檔.(假設我們編譯完後的執行檔的名稱為 LoginConsole.exe)
然後開啟一個dos視窗.
因為我是用vs.net 2005.
所以要將路徑切換到到
C:\Windows\Microsoft.NET\Framework\v2.0.50727
因為ildasm.exe是放在這個路徑下.
當然.如果你是透過"Visual Studio 2005命令提示字元"啟動命令視窗的話,就不用切換目錄了.

然後執行下列的指令 :

ildasm LoginConsole.exe

這時就會跳出ildasm的視窗如下 :

ildasm001

點開其中的 Main : void(string[])
就可以看到如下反組譯的視窗畫面

ildasm002

而內容如下 :

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 程式碼大小       104 (0x68)
  .maxstack  2
  .locals init ([0] string UserId,
           [1] string UserPwd,
           [2] string uid,
           [3] string pwd,
           [4] bool CS$4$0000)
  IL_0000:  nop
  IL_0001:  ldstr      "ajun"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "password"
  IL_000c:  stloc.1
  IL_000d:  ldstr      "Please enter your id : "
  IL_0012:  call       void [mscorlib]System.Console::Write(string)
  IL_0017:  nop
  IL_0018:  call       string [mscorlib]System.Console::ReadLine()
  IL_001d:  stloc.2
  IL_001e:  ldstr      "Please enter your password : "
  IL_0023:  call       void [mscorlib]System.Console::Write(string)
  IL_0028:  nop
  IL_0029:  call       string [mscorlib]System.Console::ReadLine()
  IL_002e:  stloc.3
  IL_002f:  ldloc.2
  IL_0030:  ldloc.0
  IL_0031:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_0036:  brfalse.s  IL_0044
  IL_0038:  ldloc.1
  IL_0039:  ldloc.3
  IL_003a:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                 string)
  IL_003f:  ldc.i4.0
  IL_0040:  ceq
  IL_0042:  br.s       IL_0045
  IL_0044:  ldc.i4.1
  IL_0045:  stloc.s    CS$4$0000
  IL_0047:  ldloc.s    CS$4$0000
  IL_0049:  brtrue.s   IL_005a
  IL_004b:  nop
  IL_004c:  ldstr      "Welcome!"
  IL_0051:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0056:  nop
  IL_0057:  nop
  IL_0058:  br.s       IL_0067
  IL_005a:  nop
  IL_005b:  ldstr      "Access Deny!"
  IL_0060:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0065:  nop
  IL_0066:  nop
  IL_0067:  ret
} // end of method Program::Main

而從這內容就可以很清楚的看到我們所設定的帳號密碼.
到這.我想大家應該比較清楚ildasm.exe的功能了.

再來在說一下ilasm.exe,
ildasm是用來做反組譯的,
而ilasm就是用來作組譯用的.
至於怎麼用.看一下下面的用法.

現在我們先用ildasm.exe來產生剛剛所寫的程式的PE檔.
語法如下 :

ildasm /source LoginConsole.exe /out:LoginConsoleFake.txt

其中的/source參數是用來"顯示原始程式行做為註解".
然後我們就可以得到下面這個LoginConsoleFake.txt的檔案.

//  Microsoft (R) .NET Framework IL Disassembler.  Version 2.0.50727.42
//  Copyright (c) Microsoft Corporation.  All rights reserved.

// Metadata version: v2.0.50727
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 2:0:0:0
}
.assembly LoginConsole
{
  .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 )             // ...1.0.0.0..
  .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 65 30 31 65 31 37 32 31 2D 37 32 62 63   // ..$e01e1721-72bc
                                                                                                  2D 34 33 61 62 2D 38 65 62 62 2D 34 66 66 38 34   // -43ab-8ebb-4ff84
                                                                                                  31 62 65 34 65 37 36 00 00 )                      // 1be4e76..
  .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 13 43 6F 70 79 72 69 67 68 74 20 28 43 29   // ...Copyright (C)
                                                                                                  20 20 32 30 30 38 00 00 )                         //   2008..
  .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0C 4C 6F 67 69 6E 43 6F 6E 73 6F 6C 65 00   // ...LoginConsole.
                                                                                                00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 )
  .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0C 4C 6F 67 69 6E 43 6F 6E 73 6F 6C 65 00   // ...LoginConsole.
                                                                                              00 )

  // --- 以下為自動加入的自訂屬性,請勿取消註解 -------
  //  .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 )

  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 )
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 1:0:0:0
}
.module LoginConsole.exe
// MVID: {71069530-5126-4026-B70A-03F69CFFD29A}
.imagebase 0x00400000
.file alignment 0x00001000
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x009C0000

// =============== CLASS MEMBERS DECLARATION ===================

.class private auto ansi beforefieldinit LoginConsole.Program
       extends [mscorlib]System.Object
{
  .method private hidebysig static void  Main(string[] args) cil managed
  {
    .entrypoint
    // 程式碼大小       104 (0x68)
    .maxstack  2
    .locals init ([0] string UserId,
             [1] string UserPwd,
             [2] string uid,
             [3] string pwd,
             [4] bool CS$4$0000)
    .language '{3F5162F8-07C6-11D3-9053-00C04FA302A1}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
// Source File 'D:\Project\LoginConsole\LoginConsole\Program.cs'
//000010:         {
    IL_0000:  nop
//000011:             string UserId = "ajun";
    IL_0001:  ldstr      "ajun"
    IL_0006:  stloc.0
//000012:             string UserPwd = "password";
    IL_0007:  ldstr      "password"
    IL_000c:  stloc.1
//000013:
//000014:             Console.Write("Please enter your id : ");
    IL_000d:  ldstr      "Please enter your id : "
    IL_0012:  call       void [mscorlib]System.Console::Write(string)
    IL_0017:  nop
//000015:             string uid = Console.ReadLine();
    IL_0018:  call       string [mscorlib]System.Console::ReadLine()
    IL_001d:  stloc.2
//000016:             Console.Write("Please enter your password : ");
    IL_001e:  ldstr      "Please enter your password : "
    IL_0023:  call       void [mscorlib]System.Console::Write(string)
    IL_0028:  nop
//000017:             string pwd = Console.ReadLine();
    IL_0029:  call       string [mscorlib]System.Console::ReadLine()
    IL_002e:  stloc.3
//000018:
//000019:             if ((uid == UserId) && (UserPwd == pwd))
    IL_002f:  ldloc.2
    IL_0030:  ldloc.0
    IL_0031:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                   string)
    IL_0036:  brfalse.s  IL_0044

    IL_0038:  ldloc.1
    IL_0039:  ldloc.3
    IL_003a:  call       bool [mscorlib]System.String::op_Equality(string,
                                                                   string)
    IL_003f:  ldc.i4.0
    IL_0040:  ceq
    IL_0042:  br.s       IL_0045

    IL_0044:  ldc.i4.1
    IL_0045:  stloc.s    CS$4$0000
    IL_0047:  ldloc.s    CS$4$0000
    IL_0049:  brtrue.s   IL_005a

//000020:             {
    IL_004b:  nop
//000021:                 Console.WriteLine("Welcome!");
    IL_004c:  ldstr      "Welcome!"
    IL_0051:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0056:  nop
//000022:             }
    IL_0057:  nop
    IL_0058:  br.s       IL_0067

//000023:             else
//000024:             {
    IL_005a:  nop
//000025:                 Console.WriteLine("Access Deny!");
    IL_005b:  ldstr      "Access Deny!"
    IL_0060:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0065:  nop
//000026:             }
    IL_0066:  nop
//000027:         }
    IL_0067:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname
          instance void  .ctor() cil managed
  {
    // 程式碼大小       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class LoginConsole.Program

// =============================================================

// *********** 反組譯作業完成 ***********************
// 警告: 已建立 Win32 資源檔 LoginConsoleFake.res

再來我們將這個檔案做些小調整.
從檔案中我們可以很容易看出哪個是帳號密碼的設定.
所以我們直接改一下密碼.將他改為1234.

IL_0007:  ldstr      "password"
改為
IL_0007:  ldstr      "1234"

存檔後.在dos的視窗輸入下列指令

ilasm LoginConsoleFake.txt

當執行完成後,它會產生一個LoginConsoleFake.exe的檔案.
這時你就可以執行它.然後輸入新的密碼看看!!

我想這時你應該對於ildasm跟ilasm有初步的認識了...
雖然這並不是一個很正面的例子.
但是,我想應該是可以讓人比較有興趣...

其實,ildasm有更正面的用法,
像是黑暗執行緒寫的這篇,

或是你想要看看對於sqlconnection使用using跟使用close()與Dispose(),
在編譯後所進行的動作有甚麼差異.
也都是可以透過這個工具來進行.

最後,你或許會覺得.net的程式就這麼"透明",這麼不安全嗎??
其實有攻就有防.
下個主題或許可以來說一下.net的"混淆器"(obfuscator).

張貼留言

Google以後可以查祖譜嗎?

今天看到一篇INDISE的文章 : 比爾蓋茲和賈伯斯,他們的孩子都是 14 歲才能用手機 想說比爾蓋茲不是有點年紀了嗎? 他的小孩應該也都不小了. 他們14歲時應該還沒有甚麼智慧手機吧… 說不定還是在BB call的年代. 所以就好奇的用google查一下有沒...