也談.Net中間語言——破解Delphi2CS行數和時間限制

Mr.Johness發表於2013-08-17

  其實我一直在研究將Delphi版的傳奇2原始碼使用C#實現,不過由於我並沒有學習過Delphi。就只能說先試著用一些工具轉換程式碼。

  後來我在網上找到了一款軟體:Delphi2CS。這款軟體比較強大,雖然不支援條件編譯,但竟然能對窗體控制元件達到非常高的轉換效率!且直接生成vs.net的專案,令我十分高興,這意味著我們只需要修復很少的部分。

  不過真正嘗試轉換的時候發現有一個限制:原始檔不得超過500行。

  如下,圖片為準換完成後的報告,程式碼為轉換完成後的cs檔案:

 1 using System;
 2 using System.IO;
 3 using DCPconst;
 4 using Base64;
 5 using Sha1;
 6 
 7 namespace DCPcrypt
 8 {
 9  // Delphi2CS trial converts the .PAS file that is less than 500 lines. 
10  // DCPcrypt.pas is 1029 lines. 
11  // Please purchse the final version to avoid the limitation.
12 }

  這令我十分困擾。

  後來在網上發現部落格園的liufei同學解決了這個問題,他說明了方法並提供了可用程式。

  使用他的程式確實可以達到效果,但是另一個問題卻出現了:

    

  大概意思是說過期了。我是從官網上下載了檔案進行安裝,然後將liufei同學的破解檔案放到程式目錄下執行的(如果是直接使用liu同學的程式是可行的)。不過出現了上圖的情況。

  雖然不知道原因,不過貌似現在只能自己來破解了。

  先開啟IL,載入程式

  

  

  然後依次點選“檔案”=>“轉儲”,使用預設設定就行了

  

  然後開啟il檔案開始找,不過沒什麼挑戰性,一下就找到了

  

  上述程式碼可用.NET Reflector還原

  1 public void F()
  2 {
  3     if (!this.TB)
  4     {
  5         try
  6         {
  7             DC.Q = this;
  8             StringBuilder builder = new StringBuilder();
  9             bool flag = false;
 10             StringBuilder builder2 = new StringBuilder();
 11             bool flag2 = false;
 12             StringBuilder builder3 = new StringBuilder();
 13             StringBuilder b = null;
 14             if (this.R != null)
 15             {
 16                 AB ab = null;
 17                 foreach (string str in this.R)
 18                 {
 19                     ab = this.NB.BB(str);
 20                     if (ab != null)
 21                     {
 22                         this.U(this.LD(ab.O()), null);
 23                     }
 24                 }
 25             }
 26             if (this.OB != null)
 27             {
 28                 foreach (string str2 in this.OB)
 29                 {
 30                     if (!str2.Equals(this.O()))
 31                     {
 32                         builder.Append("using ").Append(str2).Append(";").Append("\r\n");
 33                     }
 34                 }
 35             }
 36             string str3 = SB.U();
 37             if ((str3 != null) && (str3 != ""))
 38             {
 39                 builder.Append(str3);
 40             }
 41             if (this.EB != null)
 42             {
 43                 builder.Append(this.EB.Replace("U_K_N_O_W_N ", " "));
 44             }
 45             if (QB.B != 100)
 46             {
 47                 this.ED("Delphi2CS has expired.");
 48                 builder3.Append("// Delphi2CS has expired, please purchase the final version. \r\n");
 49                 flag = true;
 50             }
 51             else if (this.GB.A > 0x1f4)
 52             {
 53                 this.ED("Delphi2CS trial converts the .pas file that is less than 500 lines.");
 54                 builder3.Append(" // Delphi2CS trial converts the .PAS file that is less than 500 lines. \r\n");
 55                 string fileName = Path.GetFileName(this.O);
 56                 builder3.Append(string.Concat(new object[] { " // ", fileName, " is ", this.GB.A, " lines. \r\n" }));
 57                 builder3.Append(" // Please purchse the final version to avoid the limitation.\r\n");
 58                 flag = true;
 59             }
 60             if (!flag)
 61             {
 62                 if ((this.MB != null) && (this.MB.Count > 0))
 63                 {
 64                     foreach (YB yb in this.MB.ToArray())
 65                     {
 66                         if (yb.L() == null)
 67                         {
 68                             continue;
 69                         }
 70                         if (SB.J())
 71                         {
 72                             if (b == null)
 73                             {
 74                                 b = new StringBuilder();
 75                             }
 76                             b.Append("namespace " + this.O() + "\r\n{").Append("\r\n");
 77                             b.Append("  partial class ").Append(yb.B.A).Append("\r\n");
 78                             b.Append("    {\r\n");
 79                             b.Append(yb.L().U());
 80                             b.Append("    }\r\n");
 81                             b.Append("}\r\n");
 82                         }
 83                         builder3.Append(yb.HB());
 84                         this.MB.Remove(yb);
 85                     }
 86                 }
 87                 if ((this.MB != null) && (this.MB.Count > 0))
 88                 {
 89                     foreach (YB yb2 in this.MB)
 90                     {
 91                         builder3.Append(yb2.HB());
 92                     }
 93                 }
 94                 if (this.U.K.W != null)
 95                 {
 96                     flag2 = true;
 97                     StringBuilder builder5 = new StringBuilder();
 98                     builder5.Append(this.U.K.W.FB());
 99                     if (builder5.Length > 0)
100                     {
101                         builder2.Append("namespace ").Append(this.O()).Append(".Units\r\n");
102                         builder2.Append("{\r\n");
103                         builder2.Append(builder5.ToString());
104                         builder2.Append("}\r\n");
105                         builder2.Append("\r\n");
106                         string str5 = this.HB();
107                         if (str5 != null)
108                         {
109                             str5 = str5.Replace("U_K_N_O_W_N ", "");
110                             builder2.Append(str5);
111                         }
112                     }
113                 }
114             }
115             string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(this.O);
116             string a = fileNameWithoutExtension + ".Designer.cs";
117             if (this.RB)
118             {
119                 string str8 = this.NB.LB(this.O);
120                 fileNameWithoutExtension = fileNameWithoutExtension + str8;
121             }
122             else
123             {
124                 fileNameWithoutExtension = fileNameWithoutExtension + ".cs";
125             }
126             string str9 = this.O.ToLower().Replace(this.NB.K, this.NB.J);
127             if (!str9.StartsWith(this.NB.J))
128             {
129                 str9 = this.NB.J(str9, this.NB.J);
130             }
131             if (!str9.StartsWith(this.NB.J))
132             {
133                 str9 = this.NB.K(str9, this.NB.J);
134             }
135             str9 = Path.Combine(Path.GetDirectoryName(str9), fileNameWithoutExtension);
136             FileInfo info = new FileInfo(str9);
137             if (info.Exists)
138             {
139                 info.Delete();
140             }
141             else
142             {
143                 info.Directory.Create();
144             }
145             this.PC();
146             FileStream stream = info.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
147             StreamWriter writer = new StreamWriter(stream, Encoding.Default);
148             writer.Write(builder.ToString());
149             if (builder3.Length > 0)
150             {
151                 writer.WriteLine("namespace " + this.O() + "\r\n{");
152                 writer.Write(builder3.ToString());
153                 writer.WriteLine("}\r\n");
154             }
155             if (flag2)
156             {
157                 writer.Write(builder2.ToString());
158             }
159             writer.Close();
160             stream.Close();
161             writer = null;
162             stream = null;
163             if ((b != null) && (b.Length > 0))
164             {
165                 this.VC(a, b);
166             }
167             builder2 = null;
168             builder3 = null;
169             builder = null;
170             this.P = str9.Replace(this.NB.J, "");
171             if (this.P.StartsWith(Path.DirectorySeparatorChar.ToString()))
172             {
173                 this.P = this.P.Substring(1);
174             }
175             this.NB.T.Add(this.P);
176             if (this.KB != null)
177             {
178                 string key = this.KB.Replace(this.NB.J, "");
179                 this.JC("Creating " + Path.GetFileName(this.KB));
180                 try
181                 {
182                     this.LB.Close();
183                     this.NB.U.Add(key, this.P);
184                 }
185                 catch
186                 {
187                 }
188             }
189         }
190         catch (Exception exception)
191         {
192             this.NB.W(exception.ToString());
193             this.ED(exception.Message);
194         }
195     }
196 }

  上面的語句其實是一個條件判斷,判斷讀取到的行數是否小於500,在第51行處。我們可以改到500000。

  行數限制似乎破解了,那麼時間限制呢?文章第一副圖片所示大概是說我們使用的是Delphi2CS評估版,而現在它過期了。

  破解過期時間很簡單,我們在il檔案中找到過期判斷語句

 1 .method private hidebysig instance void 
 2           H(object A,
 3             class [mscorlib]System.EventArgs B) cil managed
 4   {
 5     // 程式碼大小       435 (0x1b3)
 6     .maxstack  4
 7     .locals init (string V_0,
 8              string V_1,
 9              class [mscorlib]System.Threading.ThreadStart V_2,
10              class [mscorlib]System.Threading.Thread V_3)
11     IL_0000:  ldsfld     int32 QB::B
12     IL_0005:  ldc.i4.s   100
13     IL_0007:  beq.s      IL_001d
14 
15     IL_0009:  ldstr      "Delphi2CS Evaluation Version has expired, please p"
16     + "urchase the final version."
17     IL_000e:  ldstr      "Error"
18     IL_0013:  ldc.i4.0
19     IL_0014:  ldc.i4.s   16

  搜尋“Delphi2CS Evaluation”即可找到,上面程式碼第11行即判斷了QB.B和100是否相等,我們通過.NET Reflector來檢視可得下面的程式碼:

 1 private void H(object A, EventArgs B)
 2 {
 3     if (QB.B != 100)
 4     {
 5         MessageBox.Show("Delphi2CS Evaluation Version has expired, please purchase the final version.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
 6     }
 7     else
 8     {
 9         this.UB = this.O.Text;
10         if ((this.UB.Trim().Length == 0) || (this.UB == null))
11         {
12             MessageBox.Show("You must input an existed Delphi Project filename", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
13         }
14         else if (!File.Exists(this.UB))
15         {
16             MessageBox.Show("You must input an existed Delphi Project filename:\n '" + this.UB + "'", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
17         }
18         else
19         {
20             this.VB = this.J.Text;
21             if (this.VB.Trim().Length == 0)
22             {
23                 MessageBox.Show("Please specify an output path for the generated C# files", "Error", MessageBoxButtons.OK, MessageBoxIcon.Hand);
24             }
25             else
26             {
27                 if (!Directory.Exists(this.VB))
28                 {
29                     Directory.CreateDirectory(this.VB);
30                 }
31                 this.VB = Path.GetFullPath(this.VB);
32                 if (!this.VB.EndsWith(new string(Path.DirectorySeparatorChar, 1)))
33                 {
34                     this.VB = this.VB + Path.DirectorySeparatorChar;
35                 }
36                 string str = Path.GetDirectoryName(this.UB).ToLower();
37                 string str2 = Path.GetDirectoryName(this.VB).ToLower();
38                 if (str.StartsWith(str2))
39                 {
40                     MessageBox.Show("Please make sure the Output path is different from the project path.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Hand);
41                 }
42                 else
43                 {
44                     this.WB = new LD(this);
45                     ThreadStart start = new ThreadStart(this.I);
46                     Thread thread = new Thread(start);
47                     thread.Start();
48                     this.WB.ShowDialog(this);
49                     try
50                     {
51                         if (thread != null)
52                         {
53                             thread.Abort();
54                         }
55                         else
56                         {
57                             thread = null;
58                         }
59                     }
60                     catch
61                     {
62                     }
63                     this.WB = null;
64                     base.Activate();
65                 }
66             }
67         }
68     }
69 }

  其實能夠猜到:這個方法是點選瀏覽按鈕選擇了檔案後的事件處理方法。第一步就判斷了QB.B是否等於100,如果不等的話就會終端執行。我們只需把if的條件設為永遠不等即可,如下面這樣

 1 .method private hidebysig instance void 
 2           H(object A,
 3             class [mscorlib]System.EventArgs B) cil managed
 4   {
 5     // 程式碼大小       435 (0x1b3)
 6     .maxstack  4
 7     .locals init (string V_0,
 8              string V_1,
 9              class [mscorlib]System.Threading.ThreadStart V_2,
10              class [mscorlib]System.Threading.Thread V_3)
11     IL_0000:  ldc.i4.s   100
12     IL_0005:  ldc.i4.s   100
13     IL_0007:  beq.s      IL_001d
14 
15     IL_0009:  ldstr      "Delphi2CS Evaluation Version has expired, please p"
16     + "urchase the final version."
17     IL_000e:  ldstr      "Error"
18     IL_0013:  ldc.i4.0
19     IL_0014:  ldc.i4.s   16

  在IL_0000裡我把二元判斷表示式左邊的值從QB.B直接換成了100,這樣的話就不存在過期了,因為if(100 != 100)永遠為false!

  其實現在要做的就很簡單了,把上面的0x1f4(即500)改大和把這個使用了QB.B判斷並終止程式執行的地方修正(其實重要的地方有兩個,上述的KD.H方法中和BB.F中,後者的if判斷正在500行判斷前)。我更改過後後進入vs.net的命令列生成可執行檔案。

  

  

  按照liu同學的說法,delphi2cs程式需要.net framework3.5,所以vs2005的cmd無法編譯,不過我沒有嘗試。按照上圖的方法就能生成exe檔案了,使用生成的檔案替換掉安裝目錄下檔案即可。

  我把破解了的檔案上傳上來,大家可以看看。

 

 歡迎您移步我們的交流群,無聊的時候大家一起打發時間:Programmer Union

 或者通過QQ與我聯絡:點選這裡給我發訊息

 (最後編輯時間2013-08-17 01:05:38)

 

相關文章