Assembly #3: Shared Assembly

     ในบทที่แล้ว เราได้เรียนรู้การสร้าง Assembly อย่างง่ายแล้ว บทนี้เรามาดูกันต่อครับ ยากขึ้นมาอีกระดับหนึ่ง นั่นก็คือ การสร้าง Assembly ที่สามารถ Shared ระหว่างโปรแกรม ซึ่งเรื่องนี้คงไม่ใช่แนวคิดที่แปลกอะไรนัก ใน Windows ทั่วไป ก็มี .dll ประเภทนี้อยู่จำนวนมาก ที่ฝังตัวอยู่ใน System Directory ของ Windows แต่ก็อย่างที่กล่าวไว้ในบทความก่อนๆ ว่า มันเกิดปัญหา Dll Hell วันนี้เรามาดูกันครับ ว่า Fusion 2.0 มันเป็นอย่างไรกันแน่ ถึงคุยว่าสามารถแก้ปัญหา Dll Hell ได้

    เนื้อหาในบทนี้ มีความสัมพันธ์กับ เรื่องการเข้ารหัส ถ้าใครไม่มีพื้นฐานเรื่องนี้ กรุณาไปย้อนไปอ่าน  การเข้ารหัสฉบับ ก. ไก่ ก่อนครับ

    ผมเริ่มเนื้อหาตรงที่สถานที่เก็บกันก่อนครับ Shared Assembly แบบแรก จะอยู่ใน Directory ใดก็ได้ที่เรากำหนด โดยปกติอาจจะตั้งชื่อ Directory ว่า Shared หรือ Common ซึ่ง Assembly เหล่านี้จะใช้ได้กับหลาย Applications ครับ แต่เป็น Applications ของที่เราเขียนขึ้นมาเองเท่านั้น เพราะคนอื่นไม่รู้ว่ามี Assembly ซ่อนอยู่นั่นเอง Assembly แบบนี้ไม่ใช่แบบที่เราจะคุยกันวันนี้ครับ

    Shared Assembly ในวันนี้เป็นเรื่องของกลางจริงๆ ที่โปรแกรมทุกโปรแกรมสามารถเรียกใช้ได้ และถ้าเวลา Install แล้วพบว่า เครื่องที่กำลังลงอยู่นั้น ไม่มี Assembly ดังกล่าวอยู่ มันก็จะทำการ Install ลง ที่ Directory ตัวนี้ครับ Directory ที่ว่านี้คือ  Winnt\Assembly\GAC  นั่นเอง   เราลองไปแอบดูหน่อยว่ามีอะไรบ้าง

DOS Prompt

C:\WINNT\assembly\GAC>dir/w
Volume in drive C has no label.
Volume Serial Number is DC04-5ACF

Directory of C:\WINNT\assembly\GAC

[.]
[..]
[Accessibility]
[ADODB]
[CRVsPackageLib]
[CrystalDecisions.CrystalReports.Engine]
[CrystalDecisions.ReportSource]
[CrystalDecisions.Shared]
[CrystalDecisions.Web]
[CrystalDecisions.Windows.Forms]
[CrystalEnterpriseLib]
[CrystalInfoStoreLib]
[CrystalKeyCodeLib]
[CrystalPluginMgrLib]
[CrystalReportPluginLib]
[cscompmgd]
[CustomMarshalers]
[EnvDTE]
[Extensibility]
[IEExecRemote]
[IEHost]
[IIEHost]
[Infragistics.License]
[Infragistics.Shared]
[Infragistics.Win]
[Infragistics.Win.UltraWinGrid]
[Infragistics.Win.UltraWinGrid.Design]
[Infragistics.Win.UltraWinListBar]
[Infragistics.Win.UltraWinMaskedEdit]
[Infragistics.Win.UltraWinSchedule]
[Infragistics.Win.UltraWinTree]
[ISymWrapper]
[Microsoft.JScript]
[Microsoft.mshtml]
[Microsoft.StdFormat]
[Microsoft.VisualBasic]
[Microsoft.VisualBasic.Compatibility]
[Microsoft.VisualBasic.Compatibility.Data]
[Microsoft.VisualBasic.Vsa]
[Microsoft.VisualC]
[Microsoft.VisualStudio.VCCodeModel]
[Microsoft.VisualStudio.VCProject]
[Microsoft.VisualStudio.VCProjectEngine]
[Microsoft.VisualStudio.VSHelp]
[Microsoft.Vsa]
[Microsoft.Vsa.Vb.CodeDOMProcessor]
[Microsoft_VsaVb]
[msatinterop]
[mscorcfg]
[MSDATASRC]
[MSDDSLMP]
[MSDDSP]
[Office]
[Regcode]
[SoapSudsCode]
[stdole]
[System]
[System.Configuration.Install]
[System.Data]
[System.Design]
[System.DirectoryServices]
[System.Drawing]
[System.Drawing.Design]
[System.EnterpriseServices]
[System.Management]
[System.Messaging]
[System.Runtime.Remoting]
[System.Runtime.Serialization.Formatters.Soap]
[System.Security]
[System.ServiceProcess]
[System.Web]
[System.Web.RegularExpressions]
[System.Web.Services]
[System.Windows.Forms]
[System.Xml]
[TlbExpCode]
[TlbImpCode]
[VSLangProj]
0 File(s) 0 bytes
78 Dir(s) 26,354,585,600 bytes free

C:\WINNT\assembly\GAC>

     ลองสังเกตดูนะครับ ใน Directory นี้ ยังไม่มี Assembly ที่เป็น .DLL  พวกนั้นมันจะอยู่ระดับ Directory ที่ลึกกว่าครับ เรามาลองดูชั้นนี้กันก่อน เราจะเห็นได้ว่า Assembly หนึ่งชื่อนั้น จะเก็บอยู่ใน 1 Directory ครับ เช่น Directory System.Web จะเก็บ System.Web.dll ไว้ภายใน สาเหตุที่ต้องสร้างเป็น Directory นั้น เดี๋ยวเราจะเข้าใจเมื่อเราเข้าไปดูระดับที่ลึกกว่า แต่ตอนนี้ผมตั้งใจ ไม่ย่อเนื้อหาใน Dos Prompt นี้ก็เพราะต้องการให้เห็นว่า มันแบ่งออกเป็นกลุ่มหลักๆ ได้ 4 กลุ่ม ดังนี้ กลุ่มที่ขึ้นด้วย Microsoft และ System เราก็รู้ๆ กันอยู่ว่าเป็นของ Microsoft ครับ กลุ่มที่ ขึ้นด้วย Crystal ซึ่งก็คือ Crystal Report เป็นของบริษัท Crystal Decisions ซึ่งเป็นบริษัทลูกของ Seagate Software และอีกตัวก็คือ Infragistics ซึ่งผม Installed ตัว UltraWinSuite

    Microsoft แนะนำหลักการตั้งชื่อ Assembly ไว้ก็คือ ให้ขึ้นด้วยชื่อบริษัทครับ จะสังเกตได้จาก Infragistics และ Microsoft (ส่วน Crystal Decisions ตั้งชื่อ Assembly ไม่ค่อยดีครับ) ด้วยวิธีนี้มันก็สามารถแก้ปัญหา Dll Hell ไปได้ในระดับหนึ่งแล้วครับ สองบริษัทใดๆ ถึงแม้ว่าจะมีชื่อ Assembly ซ้ำกัน ก็ไม่ทับกันครับ เพราะหน้าชื่อนำด้วยชื่อบริษัท เลี่ยงการทับกันได้ค่อนข้างดีครับ โอกาสที่จะเกิดปัญหามีน้อยลงอย่างเห็นได้ชัด

    เพื่อความเข้าใจ เรามาดูการสร้าง Share Assembly ทีละขั้นกันเลยดีกว่า

  

ตัวอย่าง

    ผมมีตัวอย่าง Assembly อย่างง่ายมาให้ดูกันครับ

sample.cs

namespace TwoGuru.Sample {
	using System;
	public class Sample1
	{
		public void ShowVersion()
		{
			Console.WriteLine("Here is Version 1");
		}
	}
}

แล้วผมก็สร้างตัวเรียกดังนี้ครับ

caller.cs

using TwoGuru.Sample;
class Caller
{
	public static void Main()
	{
		Sample1 s = new Sample1();
		s.ShowVersion();
	}
}

เมื่อคอมไพล์แล้วจะได้ดังนี้ครับ

DOS Prompt

C:\cs>csc /t:library sample1.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.
C:\cs>csc /t:exe /r:sample1.dll caller.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.
C:\cs>caller
Here is Version 1
C:\cs>dir
 Volume in drive C is Stronghold
 Volume Serial Number is 34DD-80DA
 Directory of C:\cs
02/28/2002  01:26 PM    <DIR>          .
02/28/2002  01:26 PM    <DIR>          ..
02/28/2002  01:25 PM               168 sample1.cs
02/28/2002  01:40 PM             3,072 sample1.dll
02/28/2002  12:24 PM               130 caller.cs
02/28/2002  01:40 PM             3,072 caller.exe    
C:\cs>

 ทีนี้เรามาลองแกล้งมันดูครับ แอบลบ sample1.dll แล้วลองรันดู จะได้ผลดังนี้ครับ

DOS Prompt

C:\cs>del sample1.dll

C:\cs>caller

Unhandled Exception: System.IO.FileNotFoundException: File or assembly name sam
le1, or one of its dependencies, was not found.
File name: "sample1"
   at Caller.Main()
Fusion log follows:
=== Pre-bind state information ===
LOG: DisplayName = sample1, Version=0.0.0.0, Culture=neutral, PublicKeyToken=nu
l
 (Fully-specified)
LOG: Appbase = C:\cs\
LOG: Initial PrivatePath = NULL
Calling assembly : caller, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
===
LOG: Application configuration file does not exist.
LOG: Policy not being applied to reference at this time (private, custom, parti
l, or location-based assembly bind).
LOG: Post-policy reference: sample1, Version=0.0.0.0, Culture=neutral, PublicKe
Token=null
LOG: Attempting download of new URL file:///C:/cs/sample1.DLL.
LOG: Attempting download of new URL file:///C:/cs/sample1.DLL.
LOG: Attempting download of new URL file:///C:/cs/sample1.EXE.
LOG: Attempting download of new URL file:///C:/cs/sample1.EXE.
C:\cs>

    ไม่มีอะไรมากครับ เพียงแค่ต้องการยืนยันว่า โปรแกรม caller.exe ไปเรียกใช้ sample1.dll เวลาคอมไพล์และ link มันไม่ได้เอาเนื้อหาในส่วนของ sample1.dll ไปใส่ไว้ใน caller.exe

    สมมุติว่าเราไม่ได้ลบแฟ้ม .dll นี้ เราจะลองบรรจุ assembly นี้ลงไปที่ Directory ส่วนกลางหรือ GAC ดูโดยใช้คำสั่ง Gacutil ดังนี้ครับ

DOS Prompt

C:\cs>undelete sample1.dll

C:\cs>gacutil /i sample1.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Failure adding assembly to the cache: Attempt to install an assembly without a strong name
C:\cs>

    คำสั่ง gacutil /i เป็นคำสั่งที่ install sample1.dll ลงใน GAC ครับ แต่มันไม่สำเร็จ มันบอกว่า เราไม่สามารถจะ install แฟ้มนี้ได้ ถ้าแฟ้มนี้ไม่มี Strong Name เอ! แล้ว Strong Name มันหน้าตาเป็นยังไง?

Strong Name

    เราได้เรียนรู้มาข้างต้นแล้วว่า Microsoft ใช้ธรรมเนียมปฏิบัติ โดยให้ตั้ง ชื่อบริษัทนำหน้า เพื่อหลีกเลี่ยงกันชนกัน แต่มันก็ไม่สามารถแก้ปัญหาได้ 100% เพราะถ้าเจอคนแหกกฎ hacker หรือถึงแม้ว่าจะเป็น Assembly ของเราก็ตาม แต่ต่าง Version รุ่นใหม่ยังทับรุ่นเก่าอยู่ดี ทาง Microsoft จึงออกมาตรการในการตั้งชื่อให้มันปลอดภัยยิ่งขึ้น โดยที่ Microsoft เรียกมันว่า Strong Name

    หลักการของ Strong Name นั้นก็คือ การสร้างบรรจุ เอา Version และ รายละเอียดต่างใส่เข้าไปใน Assembly โดยที่ Strong ตรงที่ว่า ผู้อื่นพยายามมาแก้ไข ก็ไม่สามารถทำได้ครับ แต่จะทำได้อย่างไร เราต้องมาดูกัน

    ขั้นแรกเรามาดูส่วนประกอบแรกของ Strong Name นั่นก็คือ Version และเลข Random (จริงๆ แล้วมี Culture อีกอย่างหนึ่งแต่ไม่ค่อยมีคนใช้)

DOS Prompt

C:\WINNT\assembly\GAC\System.Web>dir System.Web
 
02/27/2002  01:59 PM    <DIR>          .
02/27/2002  01:59 PM    <DIR>          ..
02/27/2002  01:59 PM    <DIR>          1.0.3300.0__b03f5f7f11d50a3a
C:\WINNT\assembly\GAC>dir System.Xml

02/27/2002  01:59 PM    <DIR>          .
02/27/2002  01:59 PM    <DIR>          ..
02/27/2002  01:59 PM    <DIR>          1.0.3300.0__b77a5c561934e089

C:\WINNT\assembly\GAC>cd System.Security

02/27/2002  01:59 PM    <DIR>          .
02/27/2002  01:59 PM    <DIR>          ..
02/27/2002  01:59 PM    <DIR>          1.0.3300.0__b03f5f7f11d50a3a

C:\WINNT\assembly\GAC\> _

    จากตัวอย่างข้างบนนี้จะเห็นว่า System.Web และ System.Security นั้นมาจากหน่วยงานเดียวกัน ส่วน System.XML นั้นมาจากคนละหน่วยงานครับ การใช้เลข Random แบบนี้ โอกาสบังเอิญสร้างได้เลย Random เดียวกัน เกือบเป็น 0 ครับ เพราะ ตัว Random นี้เป็นเลขขนาด 8 bytes หรือ 2256 ถ้าคิดเป็นเลขที่เราใช้อยู่ก็เกือบ 80 หลักครับ

    ดังนั้น ต่อให้ 2 บริษัทใช้ Assembly ซ้ำกัน มันก็จะถูกเก็บลงใน Directory เดียวกัน แต่คนละ Directory ย่อย ซึ่ง Directory ย่อยนี้ มีเลข Random ของหน่วยงาน ทำให้มันไม่ทับกันแน่นอน และถ้ามาจากหน่วยงานเดียวกัน แต่คนละ Version Directory ตัวนี้ก็จะเป็นตัวแยกให้ครับ

    จะเห็นได้ว่า มันคงไม่บังเอิญซ้ำแล้วครับ แต่ยังปัญหายังไม่หมด ยังมีปัญหาจาก Hacker อีก ในยุคสมัย COM นับได้ว่าเป็นสวรรค์ของ Hacker เลยครับ จะเล่นตุกติกอะไร ดูมันง่ายไปหมด มีคดีกันบ่อยๆ ครับว่า มี Hacker แอบเอา .dll ที่เป็น Trojan Horse ไปทดแทน .dll จริงในเครื่อง ทำให้แอบขโมยข้อมูลต่างๆ จนเป็นเรื่องราวใหญ่โต มาในยุค .NET ทาง Microsoft ก็พยายามหาวิธีป้องกัน นั่นก็คือ ตัวเลข Random นี้ เราต้องปลอมไม่ได้ ซึ่ง ทาง Microsoft ได้นำเอาแนวคิดของการเข้ารหัสมาใช้ เรามาลองดูกันครับว่า Microsoft จัดการเรื่องนี้อย่างไรบ้าง 

DOS Prompt

C:\cs>sn -k twoguru.snk
Microsoft (R) .NET Framework Strong Name Utility  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Key pair written to twoguru.snk

C:\cs>dir twoguru.snk
02/28/2002  02:41 PM               596 twoguru.snk
C:\CS>

    เรามาเริ่มสร้าง Strong Name กัน ขั้นแรก เราต้องสร้าง key ในระบบ Public key Cryptographic ตัวหนึ่งเป็น Public Key อีกตัวหนึ่งเป็น Private Key โดยใช้คำสั่ง sn -k ผลลัพธ์ที่ได้ เราเก็บไว้ในแฟ้มข้อมูล twoguru.snk ครับ

    ขั้นต่อมา เราแอบแกะเอาเฉพาะส่วน Public Key มาดู โดยใช้คำสั่งนี้ครับ

DOS Prompt

DC:\cs>sn -p twoguru.snk twoguru.pbk
Microsoft (R) .NET Framework Strong Name Utility  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Public key written to twoguru.pbk

C:\cs>sn -tp twoguru.pbk
Microsoft (R) .NET Framework Strong Name Utility  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Public key is
00240000048000009400000006020000002400005253413100040000010001009bdc8330d37fa6
8652c372a73f987230f13d6ef93b2402e2098dbc237ea2d474d331af5a18a5d0582e2b36abbb58
f2fb7bef44b746d5d4f71780f48ca6a71a2af4a8911cf339a2e6f9ff199597dcfa131312773304
9a9f96407552bb74d0399f702e1fca69d2581ed45d8b7d11ee03ae2517fe63fa5bb7b6f37ef8b4
b09483d0

Public key token is 261638c06cedae17
C:\cs>

    คำสั่งแรก เป็นคำสั่งที่แกะเอาเฉพาะ Public Key ออกมา เก็บเอาไว้ที่ twoguru.pbk จากนั้นคำสั่งที่ 2 เป็นคำสั่งที่ใช้แสดง ตัว key ออกมาเป็นเลขฐาน 16 ซึ่งนี่ก็คือ Public Key ของเรา และสิ่งที่แสดงต่อมาคือ Public Key Token ซึ่งใช้แทนค่าเลข Random ที่ผมกล่าวไว้ข้างบน ในรุ่น Beta2 นั้น Microsoft ใช้วิธีตัด เอา 8 bytes ท้ายของ Public Key ออกมาเป็น Token แต่มาในรุ่น 1.0 เล่นเอาผมงงเหมือนกันว่ามาสร้างมาจากไหน ผมยังหาที่มาไม่ได้ คาดเอาเองว่าน่าจะเป็นผลลัพธ์ที่เอา ตัว Public Key ไปเข้า Message Digest ครับ แต่ที่มาก็คงไม่สำคัญเท่าไหร่ครับ เรารู้เพียงว่า ตัวเลขนี้จะนำไปใช้ในส่วนของชื่อ Directory ย่อยครับ

Register Strong Name ลงใน Assembly ที่เราสร้าง

    เรื่องนี้ทำเอาผมงงไปหลายตลบเหมือนกัน ใน Beta 2 ผมใช้ sn.exe เพื่อ Register ได้ แต่พอมารุ่น 1.0 มันไม่ได้แล้ว นั่งอ่าน Document อยู่ตั้งนาน ถึงพบว่าเราต้องใช้ al.exe เพื่อ Register  ผมเลยต้อง คอมไพล์ sample1.cs ใหม่ ให้เป็น netmodule (ถ้าใครถนัดภาษา C มันก็คือ .obj) จากนั้นค่อยใช้ al.exe มาสร้างรวมเป็น .dll (สำหรับภาษา C คือ .lib) ลองดูดังนี้ครับ

DOS Prompt

C:\cs>del sample1.dll
C:\cs\>csc /target:module sample1.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.

C:\cs>dir *.netmodule

03/01/2002  10:15 AM             2,048 sample1.netmodule
C:\cs> _ample1>

จากนั้นผมจะสร้าง Assembly จาก .netmodule แล้วทำการ Register Strong Name ไปด้วยในตัวเลย

DOS Prompt

DC:\cs>al /out:Twoguru.Sample.sample1.dll /v:1 /keyfile:twoguru.snk sample1.netmodule
Microsoft (R) Assembly Linker version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.
D:\cs\sample1>dir *.dll

03/01/2002  10:20 AM             3,584 Twoguru.Sample.sample1.dll
C:\cs>

    เราระบุ สามารถระบุรายละเอียดต่างๆ ได้มากกว่านี้อีกมาก ลองไปศึกษาคำสั่ง al.exe ดูครับ ในที่นี้ผมระบุ version และ ตัวของ keyfile ที่เอาไปทำ Strong Name และที่ผมเปลี่ยนชื่อ .dll ก็เพราะว่าเวลาที่เราเอาไปเก็บไว้ใน GAC แล้ว จะได้ชื่อเป็นมาตรฐานหน่อย

Install Assembly ลงใน GAC

    ส่วนนี้ก็ไม่มีอะไรยากครับ ตรงไปตรงมา ใช้ gacutil.exe ดังนี้ครับ

DOS Prompt

C:\cs>gacutil /i Twoguru.Sample.sample1.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Assembly successfully added to the cache

C:\cs>c:

    คราวนี้มันไม่บ่นแล้วครับว่าไม่มี Strong Name ก็เราทำทุกอย่างเรียบร้อยแล้ว มันก็ลงได้อย่างสบายครับ คราวนี้เรามาแอบดูใน GAC ดูครับว่ามีอะไรเพิ่มเติม

DOS Prompt

C:\WINNT\assembly\GAC>dir Two*
 
03/01/2002  10:26 AM    <DIR>          Twoguru.Sample.sample1
C:\WINNT\assembly\GAC>cd Twoguru.Sample.sample1
C:\WINNT\assembly\GAC\Twoguru.Sample.sample1>dir
03/01/2002  10:26 AM    <DIR>          1.0.0.0__261638c06cedae17
C:\WINNT\assembly\GAC\Twoguru.Sample.sample1> _

    จะเห็นได้ว่าชื่อ Directory ที่เก็บนั้น เป็นชื่อ Assembly ของเรา ส่วน Version นั้น เราก็เป็นคนกำหนด และที่สำคัญก็คือ มันได้เอา Public Key Token มาเป็นส่วนของ Directory ด้วย

    เรามาดูกันใน Directory ชั้นล่างสุดเลยครับ

DOS Prompt

C:\WINNT\assembly\GAC\Twoguru.Sample.sample1\1.0.0.0__261638c06cedae17>dir
03/01/2002  10:26 AM             2,048 sample1.netmodule
03/01/2002  10:26 AM             3,584 Twoguru.Sample.sample1.dll
03/01/2002  10:26 AM               212 __AssemblyInfo__.ini
C:\WINNT\assembly\GAC\Twoguru.Sample.sample1\1.0.0.0__261638c06cedae17> _

    มันก็เก็บ .dll นั่นแหละครับ

Security ของ Assembly

    Assembly ปลอดภัยจาก Hacker ไม่ให้ถูกแอบเปลี่ยนเป็น Trojan ได้อย่างไร สิ่งนี้มันขึ้นอยู่กับการเข้ารหัสครับ เมื่อเวลาเรา Register Strong Name ลงใน Assembly สิ่งที่ al.exe ทำก็คือ มันจะทำการหา Message Digest ของ .Net Modules จากนั้นมันรวมกับ Public Key Token เพื่อเอา Private Key ของหน่วยงานห่อ ผลลัพธ์จะถูกเก็บไว้ในส่วน Manifest  ใน Manifest ก็จะเก็บ Public Key หน่วยงานไว้ด้วยครับ

    เมื่อเวลาเรียกใช้ Assembly ครั้งแรก .NET Runtime (CLR) จะทำการเอา Public Key ไปแกะเอา Message Digest ของ .Net Modules ถ้าใครแอบแก้อะไรเพียง byte เดียว ผลลัพธ์ของ Message Digest จะเพี้ยนไป โปรแกรมจะไม่ยอมรันครับ ดังนั้นถ้าแอบปลอมก็ต้องเล่นปลอมกันทั้งชุดครับ โดยที่ปลอม Key Public Key และ Private key ใหม่ รวมทั้ง Message Digest ทุกตัว ซึ่งก็ไม่ใช่เป็นเรื่องที่ทำไม่ได้ครับ แต่ Microsoft มีกันอีกชั้นหนึ่งครับ กันในส่วนตัวเรียกอีก เรามาลองดู caller.exe กันครับ

DOS Prompt

C:\cs>csc /r:Twoguru.Sample.sample1.dll caller.cs
Microsoft (R) Visual C# .NET Compiler version 7.00.9466
for Microsoft (R) .NET Framework version 1.0.3705
Copyright (C) Microsoft Corporation 2001. All rights reserved.

C:\cs>caller
Here is Version 1

C:\cs>ildasm caller.exe


    ผมลองแอบดู Manifest จะได้ผลลัพธ์ดังนี้ครับ

    จะเห็นนะครับว่า ใน Manifest ของ caller.exe มีการระบุว่าต้องเรียกใช้ Twoguru.Sample.sample1 แต่ต้องมี Publick Key Token 261638C06CEDAE17 เท่านั้น ซึ่งถึงคุณแอบปลอม Assembly ได้ แต่ตัวเรียกก็ไม่สามารถเรียกใช้ได้

    ผมลองทดสอบเล่นๆ ดูครับ ลองเอาโปรแกรมพวก Hexedit ไปแอบแก้ข้อมูล 1 byte ใน Twoguru.Sample.sample.dll ลองดูผลลัพธ์ที่รัน จะได้อย่างนี้ครับ

DOS Prompt

C:\cs>caller

Unhandled Exception: System.IO.FileLoadException: Strong name validation failed
for assembly 'Twoguru.Sample.sample1'.
File name: "Twoguru.Sample.sample1"
  at caller.Main()
Fusion log follows:
=== Pre-bind state information ===
LOG: DisplayName = Twoguru.Sample.sample1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=261638c06cedae17
(Fully-specified)
LOG: Appbase = C:\cs\
LOG: Initial PrivatePath = NULL
Calling assembly : caller, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: Application configuration file does not exist.
LOG: Publisher policy file is not found.
LOG: Host configuration file not found.
LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v1.0.3705\config\machine.config.
LOG: Post-policy reference: Twoguru.Sample.sample1, Version=1.0.0.0, Culture=neu
tral, PublicKeyToken=261638c06cedae17
LOG: Attempting download of new URL file:///C:/cs/Twoguru.Sample.sample1.DLL.


C:\cs1>

มันไม่ยอมทำงานครับ บอกแต่ว่า  Strong name validation failed ทำให้เราค่อนข้างมั่นใจได้ครับ

 

การ uninstall Assembly จาก GAC

    เราใช้ gacutil /u แล้วตามด้วยชื่อ Assembly แต่ต้องใส่ .dll ไว้ข้างหลังครับ

DOS Prompt

C:\cs>gacutil /u Twoguru.Sample.sample1
Microsoft (R) .NET Global Assembly Cache Utility.  Version 1.0.3705.0
Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
Assembly: Twoguru.Sample.sample1, Version=1.0.0.0, Culture=neutral, PublicKeyTok
en=261638c06cedae17, Custom=null
Uninstalled: Twoguru.Sample.sample1, Version=1.0.0.0, Culture=neutral, PublicKey
Token=261638c06cedae17, Custom=null

Number of items uninstalled = 1
Number of failures = 0

C:\cs> _

 

เก็บตก Shared Assembly

    ถ้าเรามี Assembly ทั้งใน GAC และ Local Directory ตัว CLR มันจะเลือกเรียกตัวที่อยู่ใน GAC มาใช้งานเสมอ ดังนั้นเวลาที่เราเขียนโปรแกรม เราสามารถ copy .dll จาก GAC ออกมาเก็บไว้ที่ Local Directory ได้เลย เวลา คอมไพล์โปรแกรม ก็ Reference กับ copy ที่อยู่ Local นั่นแหละครับ Assembly ที่อยู่ใน Local นั้น จะเอาไว้สำหรับคอมไพล์โปรแกรมเท่านั้น เวลารันไม่ได้ใช้ เวลา Deploy โปรแกรม ก็ไม่ต้องเอา Assembly เหล่านี้ไปด้วย มันเรียกใช้จาก GAC อยู่แล้วครับ


1/MAR/02

.NET Framework 1.0