.net下程式的暴力修改

看雪資料發表於2004-09-23

小弟最近一直在做.net下序列通訊方面的程式,.net下沒有自帶的串列埠控制元件,使用以前的com元件又不甘心,所以在網上找到了sax.net元件(破解)版本,使用的時候,比較滿意,但是在使用的過程中,出現了

System.ArgumentOutOfRangeException: 長度不能小於 0。

引數名: newlength

   at System.Text.StringBuilder.set_Length(Int32 value)

   at Sax.Communications.SerialConnection.a(UInt32 A_0)

   at Sax.Communications.SerialConnection.a(IntPtr A_0, c& A_1)

   at Sax.Communications.SerialConnection.get_Available()

   at Sax.Communications.SerialConnection.b()

 

異常

尤其是使用ISP線與微控制器通訊的時候,幾乎每次插拔ISP線都會出現異常。異常後,控制元件的串列埠資料監視執行緒退出以後再也不能收發資料,只能重新啟動軟體。經過仔細除錯,發現
異常出現在插拔資料線的時候,有資料收到,產生了DataAvailable事件中

private void serialConnection_DataAvailable(object sender, EventArgs e) //受到資料時的處理函式
  {
   //在這之前出現了異常
   int length = serialConnection.Available;
//   Debug.WriteLine(length.ToString());
   if(length<=0) return;
   
   Byte[] buffer = new byte[length]; //輸入緩衝區有多少資料?
   serialConnection.Read(buffer,0,length);
   
   //分析資料
   if( length > 0 )
   {   
    HandleDataReceive(buffer);
   } 
  }


就是在發生事件之後,在int length = serialConnection.Available;之前發生了異常。根據VS.net
的提示資訊,初步確定是在給一個StringBuilder物件builder設定其Length的時候,傳入的引數<0導
致的。

好了,使用.net的反編譯工具Reflector(我用的版本4.0.18)開啟控制元件,找到SerialConnection類;由於此控制元件經過混淆,所以在反編譯後出現了大量的比如a,或者b這樣的看不出作者意圖的函式名。不過沒有關係,因為異常出現在serialConnection.Available附近,直接找到Available的實現程式碼

public override int get_Available()

{

      z.c c1;

      this.c();

      c1 = new z.c();

      this.a(this.a, ref c1);

      if (this.b != -1)

      {

            return ((int) (c1.b + 1));

      }

      return ((int) c1.b);

}
 

在裡面尋找出錯的函式
找到this.a(this.a, ref c1);這個

private string a(IntPtr A_0, ref z.c A_1)

{

      uint num1 = 0;

      z.ClearCommError(A_0, ref num1, ref A_1);

      if (num1 != 0)

      {

            return this.a(num1);

      }

      return null;

}
 

有追蹤到return this.a(num1);

private string a(uint A_0)

{

      StringBuilder builder1 = new StringBuilder("UART Error:", 60);

      if (builder1.Length <= 0)

      {

            goto Label_005F;

      }

      goto Label_00D0;

Label_001B:

      builder1 = builder1.Append(" Parity,");

      goto Label_00B1;

Label_002C:

      builder1 = builder1.Append(" Receive Overflow,");

      goto Label_0069;

Label_003D:

      builder1 = builder1.Append(" Framing,");

      goto Label_0095;

Label_004E:

      builder1 = builder1.Append(" Transmit Overflow,");

      goto Label_00A3;

Label_005F:

      if ((A_0 & 8) == 0)

      {

            goto Label_0095;

      }

      goto Label_003D;

Label_0069:

      if ((A_0 & 4) == 0)

      {

            goto Label_00B1;

      }

      goto Label_001B;

Label_0073:

      builder1 = builder1.Append(" <Unknown>,");

      goto Label_00FA;

Label_0084:

      builder1 = builder1.Append(" Overrun,");

      goto Label_00ED;

Label_0095:

      if ((A_0 & 1024) == 0)

      {

            goto Label_00E3;

      }

      goto Label_00BF;

Label_00A3:

      if ((A_0 & 1287) == 0)

      {

            goto Label_00FA;

      }

      goto Label_0073;

Label_00B1:

      if ((A_0 & 256) == 0)

      {

            goto Label_00A3;

      }

      goto Label_004E;

Label_00BF:

      builder1 = builder1.Append(" IO,");

      goto Label_00E3;

Label_00D0:

      builder1.Remove(0, builder1.Length);

      goto Label_005F;

Label_00E3:

      if ((A_0 & 2) != 0)

      {

            goto Label_0084;

      }

Label_00ED:

      if ((A_0 & 1) == 0)

      {

            goto Label_0069;

      }

      goto Label_002C;

Label_00FA:

      builder1.Length -= 1;//!!!!!!!!!!!!!!!!!!錯誤的來源

      return builder1.ToString();

}

我靠,也沒有想細讀為什麼要將builder1.Length 減去1,節省記憶體,沒有必要把?!!
反正是這句出錯了,將後builder1.Length 小於0了

檢視前面的程式碼,沒有對builder1.Length 賦值的語句,看來將這句改掉就行了,

檢視il程式碼,
最後的幾句為
L_00fa: ldloc.0 
L_00fb: ldloc.0 
L_00fc: callvirt StringBuilder.get_Length
L_0101: ldc.i4.1 //裝載常數1--將此處改為裝載常數0,即可
L_0102: sub 
L_0103: callvirt StringBuilder.set_Length
L_0108: ldloc.0 
L_0109: callvirt StringBuilder.ToString
L_010e: ret 

查詢il的彙編指令
發現(格式指令 ---十六進位制數) 
ldloc.0 ----06
ldloc.0 ----06
callvirt StringBuilder.get_Length ---- 6F X X X X (X不知道具體的值,可以透過模糊匹配來確定要修改的指令在檔案的位置)
ldc.i4.1 ---- 17 



sub ----  59
L_0103: callvirt  ---- StringBuilder.set_Length 6F X X X X
L_0108: ldloc.0  ---- 06
L_0109: callvirt  ---- StringBuilder.ToString 6F X X X X
L_010e: ret ----  28


使用WinHEx開啟檔案,使用16進位制搜尋,對於上面的X 使用模糊匹配的原則
找到偏移0x34B0處,在0x34BD處的將0x17 改為0x16(IL指令ldc.i4.0 ,就是將Length-0等於不減)
存檔退出,將修改好的dll檔案覆蓋原來的dll,執行軟體,怎麼插拔ISP線都沒有問題出現了!


參考資料
Inside Microsoft.net IL Assesmbler

相關文章