其實我一直在研究將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檔案了,使用生成的檔案替換掉安裝目錄下檔案即可。
我把破解了的檔案上傳上來,大家可以看看。
(最後編輯時間2013-08-17 01:05:38)