C#使用Win32?Api實(shí)現(xiàn)進(jìn)程注入到wechat的過程
引言
自從上篇使用Flaui實(shí)現(xiàn)微信自動(dòng)化之后,這段時(shí)間便一直在瞎研究微信這方面,目前破解了Window微信的本地的Sqlite數(shù)據(jù)庫,使用Openssl,以及Win32Api來獲取解密密鑰,今天作為第一張,先簡單寫一下,獲取微信的一些靜態(tài)數(shù)據(jù),以及將自己寫的c語言dll通過Api注入到微信進(jìn)程里面去,最后調(diào)用我們的dll的方法。話不多說,讓我們開始吧。
逆向
靜態(tài)數(shù)據(jù)的話,需要用到的軟件 CE,全稱是Cheat Engine,圖標(biāo)如下所示。接下來我們打開CE,可以看到左上角有一塊綠色的按鈕,我們點(diǎn)擊按鈕是附加進(jìn)程到CE,然后我們點(diǎn)擊附加微信到CE,在下面的圖中,我們看到已經(jīng)把微信進(jìn)程加載到了CE里面去,然后我們要開始獲取靜態(tài)數(shù)據(jù)了。
在獲取靜態(tài)數(shù)據(jù)之前,我們先開始講幾個(gè)概念,就是內(nèi)存的概念,我們都知道,在進(jìn)程啟動(dòng)的時(shí)候,操作系統(tǒng)會(huì)給我們的進(jìn)程分配虛擬內(nèi)存,默認(rèn)應(yīng)該是4g,具體是和操作系統(tǒng)位數(shù)也有關(guān)系,然后在運(yùn)行時(shí)也會(huì)動(dòng)態(tài)的分配內(nèi)存空間,我們學(xué)過計(jì)算機(jī)原理的肯定知道,我們的內(nèi)存存儲結(jié)構(gòu)就像是一個(gè)鏈表或者數(shù)組,我們在給這個(gè)進(jìn)程分配內(nèi)存空間的時(shí)候,他的樣子也是是類似數(shù)組的這種結(jié)構(gòu),首先假如我們的進(jìn)程現(xiàn)在有一個(gè)主模塊,主模塊里面又有自己的方法,自己的類,屬性等信息,那分配的這個(gè)主模塊的內(nèi)存就是一個(gè)數(shù)組,然后我們主模塊有一個(gè)基礎(chǔ)地址,你可以將這個(gè)基礎(chǔ)地址看作是這塊內(nèi)存數(shù)組的索引0,而我們主模塊的其他的方法,類,變量信息,都是在這個(gè)0的索引進(jìn)行移動(dòng)到指定的地址,這個(gè)地址指向我們的內(nèi)存,這個(gè)內(nèi)存存儲著我們要的信息。簡而言之,就是主模塊是的地址就是索引0,而其他變量信息可能在5,7,9等等,我們就需要判斷從0到5有多少間隔,這個(gè)就叫偏移量,我們通過屬性或者方法的內(nèi)存地址減去主模塊的地址,這個(gè)就是我們的偏移量,借這個(gè)例子就是5-0就是5,偏移量是5。
然后我們回來,我們加載微信進(jìn)程到了我們的CE之后,在wechat有一個(gè)模塊叫Wechatwin,這個(gè)是window操作系統(tǒng)下的微信用到的主要模塊,我們的和微信相關(guān)的基本都在這里,當(dāng)然不包括一些resource,這個(gè)有一個(gè)專門的模塊,我們在此不多贅述,所以我們假如要找我們的靜態(tài)數(shù)據(jù),例如微信昵稱,微信號,或者手機(jī)號,所在地區(qū),就需要找到我們的wechatwin的地址,這個(gè)就是這個(gè)模塊的基址,然后我們需要在CE中,檢索字符串找到我們要的數(shù)據(jù),例如昵稱,手機(jī)號等信息。然后用他的地址減去基址,得到偏移量。從而我們就可以在代碼中獲取到這些信息,接下來,我先帶大家在CE中找到我們想要找的數(shù)據(jù)。
在CE上方右側(cè),有一個(gè)輸入框,我們在這里輸入我們需要檢索的信息,支持的格式有byte,string,以及array,double等數(shù)據(jù)類型,我們需要找到是string,所以在ValueType那里,我們選擇string。我的微信昵稱是云淡風(fēng)輕,所以在這里搜索云淡風(fēng)輕,可以看到,就檢索出來我們的昵稱信息了,找到了這么多,這里我們往最下面拉,有一個(gè)綠色開頭的,Address是WechatWin的,就是我們要找的地址了,其他的也有的是綠色基于Wechatwin有的不是,有的就需要一個(gè)一個(gè)測試修改數(shù)據(jù)從而得到驗(yàn)證了。
我們雙擊那條綠色記錄,Wechatwin 將他加入到下面的列表去,代表我們選中的檢測的內(nèi)存,接下來我們驗(yàn)證一下,是否是找的正確的,雙擊Value,云淡風(fēng)輕,我們修改我們的Value,將云淡風(fēng)輕,改為good man 點(diǎn)擊ok,可以在下面看到,我們的微信昵稱已經(jīng)同步改為了good Man,說明我們找到的是對的,接下來,我們雙擊Address,
彈出Change address姐妹,我們復(fù)制WechatWin.dll,需要我們找到我們這個(gè)模塊的基址。然后在右邊有一個(gè)Add Address Manually,手動(dòng)添加地址,,我們把復(fù)制的WeChatWin.dll復(fù)制過去,然后點(diǎn)擊ok,在下面的列表我們就看到了這個(gè)模塊的基址,接下來,我們需要判斷這個(gè)基址和昵稱之間的偏移量,按照我們剛才所說的方式計(jì)算,轉(zhuǎn)換16進(jìn)制就是0x7ffd3d668308-0x7ffd39b40000,隨便找一個(gè)16進(jìn)制計(jì)算器,算下來的結(jié)果就是3B28308,也就是Address里面顯示的那個(gè),實(shí)際上CE已經(jīng)給我們把偏移算出來了,接下來按照同樣的方式,去搜索我們的所在地區(qū),以及手機(jī)號,如果有的信息找不到的話,我們選擇我們的昵稱哪一行數(shù)據(jù),右鍵,選擇Browse this Memory region,在內(nèi)存頁顯示這個(gè)內(nèi)存記錄,然后我們在旁邊就可以看到我們的國家,以及省份地區(qū)信息了,如果有查看地址,在右側(cè),選擇我們要復(fù)制的記錄,右鍵,有一個(gè)goto Address,然后就導(dǎo)航到了我們的內(nèi)存,然后復(fù)制地址即可。
c#代碼獲取數(shù)據(jù)以及遠(yuǎn)程注入
在上面我們講了,如何使用CE,去獲取我們微信的一些靜態(tài)數(shù)據(jù),接下來,我們就需要使用c#代碼,去實(shí)現(xiàn)我們獲取靜態(tài)數(shù)據(jù),以及最后寫的一個(gè)遠(yuǎn)程注入,來調(diào)用我們寫的一個(gè)庫。首先我們需要用到的有幾個(gè)Api函數(shù),
WaitForSingleObject,等待某個(gè)句柄多長時(shí)間,在我們創(chuàng)建遠(yuǎn)程線程的時(shí)候需要使用這個(gè)函數(shù)來等待線程執(zhí)行結(jié)束。參數(shù)是等待的句柄,我們填寫我們的線程句柄。
GetProcAddress,需要使用這個(gè)函數(shù)來調(diào)用kernel32.dll的LoadLibraryA方法,來加載我們的自己寫的dll,因?yàn)樵诿總€(gè)進(jìn)程啟動(dòng)的時(shí)候,都會(huì)去調(diào)用這個(gè)方法來加載程序所依賴的dll,還有一個(gè)方法是LoadLibraryW,和這個(gè)方法區(qū)別在于不同針對不同的編碼來進(jìn)行調(diào)用,W結(jié)尾主要是針對UNICODE的編碼,A結(jié)尾對應(yīng)Ascii編碼,所以各位在調(diào)用的時(shí)候根據(jù)自己的編碼去調(diào)用,如果一個(gè)找不到就試試另一個(gè)。
GetModuleHandle,這個(gè)函數(shù)是用來獲取kernel32.dll,結(jié)合上面的GetProdAddress來使用。
OpenProcess,這個(gè)方法是根據(jù)指定的PID,對應(yīng)就是Process類的Id,打開指定的進(jìn)程,同時(shí)指定以什么權(quán)限打開這個(gè)進(jìn)程,參數(shù)是三個(gè),第一個(gè)是權(quán)限,第二個(gè)是返回值是否可以被繼承,返回的進(jìn)程句柄是否可以被繼承,第三個(gè)參數(shù)就是我們的PID。
VirtualAllocEx,給指定的進(jìn)程分配虛擬內(nèi)存,第一個(gè)參數(shù)是進(jìn)程的句柄,OpenProcess返回值,第二個(gè)參數(shù)指定進(jìn)程內(nèi)那個(gè)內(nèi)存地址分配的內(nèi)存,此處我們只是加載dll調(diào)用方法,并不注入到某個(gè)方法或者哪里所以是Intptr.Zero,第三個(gè)參數(shù)是,分配的內(nèi)存長度,我們加載dll需要dll的路徑,這里就選擇路徑.Length就行,字符串的長度就可以,第三個(gè)參數(shù)是內(nèi)存分配的一些配置,可選值在后面會(huì)有,此處我們選擇Memory_Commit,第四個(gè)參數(shù)是內(nèi)存權(quán)限相關(guān),內(nèi)存是只讀還是可以讀寫,以及用來執(zhí)行代碼或者怎么樣,這里我們選擇可以讀寫。
ReadProcessMemory,讀指定進(jìn)程的內(nèi)存,第一個(gè)參數(shù)進(jìn)程句柄,OpenProcess返回值,第二個(gè)參數(shù)是這個(gè)進(jìn)程某個(gè)內(nèi)存的地址,第三個(gè)是數(shù)據(jù)緩沖區(qū),讀取之后的內(nèi)容就在這個(gè)緩沖區(qū),我們讀取這個(gè)緩沖區(qū)就可以拿到數(shù)據(jù),第四就是緩沖區(qū)的長度,第五個(gè)就是讀取的字節(jié)數(shù)量。
GetLastError,用來獲取Win32api調(diào)用的時(shí)候的errorcode,錯(cuò)誤編碼,
CloseHandle,關(guān)閉某一個(gè)句柄,關(guān)閉基礎(chǔ),關(guān)閉線程。
WriteProcessMemory,寫入內(nèi)存,我們需要將我們的dll地址寫入到指定內(nèi)存中去,第一個(gè)參數(shù)進(jìn)程句柄,OpenProcess返回值,第二個(gè)參數(shù),要寫入的內(nèi)存地址基址,例如我們后期需要在某個(gè)方法進(jìn)行注入,這塊就需要寫入這個(gè)方法的內(nèi)存地址,第三個(gè)參數(shù),寫入的byte數(shù)據(jù),第四個(gè)參數(shù)是第三個(gè)參數(shù)的長度,最后一個(gè)參數(shù)是寫入的數(shù)據(jù)數(shù)量。
CreateRemoteThread,在指定的進(jìn)程中創(chuàng)建遠(yuǎn)程線程,第一個(gè)參數(shù) OpenProcess返回值,第二個(gè)參數(shù)是線程安全的一些特性描述,按網(wǎng)上所說,一般null或者IntPtr.Zero,第三個(gè)參數(shù)設(shè)置線程堆棧大小,默認(rèn)是0,即使用默認(rèn)的大小,第四個(gè)參數(shù)是線程函數(shù)的地址,我們要通過這個(gè)方法去調(diào)用Kernel32的LoadLibrary方法加載我們的dll,那這個(gè)參數(shù)就填寫我們的GetProcAddress返回值,第四個(gè)參數(shù)就是創(chuàng)建這個(gè)線程的參數(shù),就是分配的遠(yuǎn)程內(nèi)存的地址VirtualAllocEx返回值,就是說通過創(chuàng)建遠(yuǎn)程線程來調(diào)用LoadLibrary方法加載我們寫入指定內(nèi)存地址的dll庫,來實(shí)現(xiàn)注入,是這樣一個(gè)邏輯,第五個(gè)參數(shù)是線程創(chuàng)建的一些參數(shù),是創(chuàng)建后掛起還是直接運(yùn)行等,最后一個(gè)參數(shù)是輸出參數(shù),記錄創(chuàng)建的遠(yuǎn)程線程的ID。
以上是我們所需要用到的所有的Win32Api函數(shù),接下來我們進(jìn)入代碼階段。
在下面的窗體,窗體會(huì)在加載的時(shí)候就去調(diào)用注入我們的dll,同時(shí)界面在加載的時(shí)候就獲取獲取我們的靜態(tài)信息。我們的dll地址是E盤下面的一個(gè)dll,這個(gè)Dll使用c語言編寫。在啟動(dòng)的時(shí)候我們?nèi)カ@取我們的微信進(jìn)程,拿到的ID,然后去注入我們的Dll,在下面的代碼里,我們判斷是否模塊是WechatWin.dll,如果是,就定義了phone,NickName,Provice,Area等int值,這個(gè)其實(shí)就是我們在CE拿到的靜態(tài)數(shù)據(jù)的內(nèi)存地址,減去我們的Wechatwin.Dll的出來的偏移量,然后定義了我們各個(gè)靜態(tài)數(shù)據(jù)的緩沖區(qū),用來讀取從微信進(jìn)程讀取的內(nèi)存數(shù)據(jù)。然后我們調(diào)用了ReadProcessMemory函數(shù)讀取內(nèi)存,獲取我們需要的靜態(tài)數(shù)據(jù)。然后使用Utf8轉(zhuǎn)為字符串,顯示到界面上。這就是獲取靜態(tài)數(shù)據(jù)的源碼,然后關(guān)閉我們的進(jìn)程句柄,并不是關(guān)閉微信,而是關(guān)閉我們獲取的這個(gè)進(jìn)程句柄。
string dllpath = @"E:\CoreRepos\ConsoleApplication2\x64\Debug\Inject.dll"; var process = Process.GetProcessesByName("wechat").FirstOrDefault(); InjectDll(process.Id, dllpath); var pid = OpenProcess(ProcessAccessFlags.PROCESS_ALL_ACCESS, false, process.Id); int bytesRead; int bytesWritten; foreach (ProcessModule item in process.Modules) { if (item.ModuleName.ToLower() == "WechatWin.dll".ToLower()) { int phone = 0x3B28248; int NickName = 0x3b28308; int provice = 0x3B282A8; int Area = 0x3B282C8; var Nickbuffer = new byte[12]; var Phonebuffer = new byte[11]; var proviceBuffer= new byte[12]; var areaBuffer=new byte[12]; ReadProcessMemory(process.Handle, item.BaseAddress + NickName, Nickbuffer, Nickbuffer.Length, out bytesRead); ReadProcessMemory(process.Handle, item.BaseAddress + phone, Phonebuffer, Phonebuffer.Length, out bytesRead); ReadProcessMemory(process.Handle, item.BaseAddress + provice, proviceBuffer, proviceBuffer.Length, out bytesRead); ReadProcessMemory(process.Handle, item.BaseAddress + Area, areaBuffer, areaBuffer.Length, out bytesRead); var Nickvalue = Encoding.UTF8.GetString(Nickbuffer); var Phonevalue = Encoding.UTF8.GetString(Phonebuffer); var Provicevalue = Encoding.UTF8.GetString(proviceBuffer); var Areavalue = Encoding.UTF8.GetString(areaBuffer); label1.Text = Nickvalue; label2.Text = Phonevalue; label3.Text = Provicevalue; label4.Text = Areavalue; var buf = Encoding.UTF8.GetBytes("我是你爹"); CloseHandle(process.Handle); } }
然后我們開始看看注入DLL的代碼,我們先引入了諸多函數(shù),然后定義了OpenProcess第一個(gè)參數(shù)權(quán)限的枚舉,定義了INFINITE 用來WaitForSingleObject等待指定的句柄進(jìn)行某些操作的執(zhí)行結(jié)束,當(dāng)然有一些我沒有定義完整,只定義我們此處需要的,完整的可以參考官網(wǎng)api去進(jìn)行看。在剛進(jìn)入這段代碼,我們調(diào)用OpenProcess指定最高權(quán)限打開這個(gè)進(jìn)程,然后獲取我們的dll地址的byte數(shù)組,并將分配內(nèi)存VirtualAllocEx到我們這個(gè)進(jìn)程里面,同時(shí)最后兩個(gè)參數(shù)代表分配內(nèi)存的一些操作,例如內(nèi)存是Memory_Commit,0x1000,以及內(nèi)存是可以讀寫的0x04,分配好內(nèi)存之后,我們?nèi)ネ覀兎峙浜玫膬?nèi)存寫入我們的dll路徑,調(diào)用WriteProcessMemory方法,傳入進(jìn)程句柄,內(nèi)存地址,寫入的數(shù)據(jù)等,在下面GetProcAddress和GetModuleHandle用來加載kernel32的LoadraryA方法句柄,最后我們調(diào)用了CreateRemoteThread函數(shù)將我們的dll注入到遠(yuǎn)程進(jìn)程中去。
#region 32 api [DllImport("kernel32.dll", SetLastError = true)] public static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); [DllImport("kernel32.dll")] public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); [DllImport("kernel32.dll")] public static extern IntPtr GetModuleHandle(string lpModuleName); [DllImport("kernel32.dll", SetLastError = true)] public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, int flAllocationType, int flProtect); [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)] public static extern bool ReadProcessMemory( IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int dwSize, out int lpNumberOfBytesRead ); [DllImport("kernel32.dll")] public static extern IntPtr OpenProcess( ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId ); [DllImport("kernel32.dll")] static extern uint GetLastError(); [DllImport("kernel32.dll")] public static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32.dll")] public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out int lpNumberOfBytesWritten); [DllImport("kernel32.dll")] public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); // 進(jìn)程訪問權(quán)限標(biāo)志位 [Flags] public enum ProcessAccessFlags : uint { PROCESS_ALL_ACCESS = 0x1F0FFF, PROCESS_CREATE_PROCESS = 0x0080, PROCESS_QUERY_INFORMATION = 0x0400, } const uint INFINITE = 0xFFFFFFFF; #endregionpublic bool InjectDll(int processId, string dllPath) { IntPtr hProcess = OpenProcess(ProcessAccessFlags.PROCESS_ALL_ACCESS, false, processId); if (hProcess == IntPtr.Zero) { Console.WriteLine("打開失敗"); return false; } byte[] dllBytes = Encoding.UTF8.GetBytes(dllPath); ; IntPtr remoteMemory = VirtualAllocEx(hProcess, IntPtr.Zero, dllBytes.Length, 0x1000, 0x04); int bytesWritten; if (!WriteProcessMemory(hProcess, remoteMemory, dllBytes, dllBytes.Length, out bytesWritten)) { var ooo = GetLastError(); Console.WriteLine("寫入失敗"); return false; } IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); var ooaa = GetLastError(); if (loadLibraryAddr == IntPtr.Zero) { Console.WriteLine("獲取LoadraryA失敗"); } // 創(chuàng)建遠(yuǎn)程線程,在目標(biāo)進(jìn)程中調(diào)用 LoadLibraryA 加載 DLL var hRemoteThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLibraryAddr, remoteMemory, 0, IntPtr.Zero); var ooaa1 = GetLastError(); if (hRemoteThread == IntPtr.Zero) { Console.WriteLine("目標(biāo)進(jìn)程創(chuàng)建遠(yuǎn)程線程失敗"); } // 等待遠(yuǎn)程線程執(zhí)行完畢 WaitForSingleObject(hRemoteThread, 0xFFFFFFFF); WaitForSingleObject(hRemoteThread, INFINITE); CloseHandle(hRemoteThread); CloseHandle(hProcess); Console.WriteLine("注入成功"); return true; }
我們看看我寫的dll里面是包括了什么內(nèi)容,我們的dll內(nèi)容很簡單,就是創(chuàng)建一個(gè)txt文件,然后寫入一個(gè)數(shù)據(jù)就行,這里需要注意的是,在使用vs創(chuàng)建dll的時(shí)候 選項(xiàng)必須是選擇的是動(dòng)態(tài)鏈接庫,這樣才有DLLMain方法,這樣在調(diào)用LoadraryA方法的時(shí)候才會(huì)調(diào)用我們的dll,自動(dòng)調(diào)用DLLMain方法,同時(shí)里面還有一個(gè)switch case語句是進(jìn)程加載線程加載,以及線程卸載,進(jìn)程卸載的判斷 我們可以在這里去去一些我們的邏輯判斷,此處我并沒有寫,只是在外層創(chuàng)建了一個(gè)文件夾,接下來運(yùn)行一下我們的winform,看看有沒有獲取到靜態(tài)數(shù)據(jù),以及將我們的dll注入進(jìn)去。馬賽克手機(jī)號。
可以看到我們啟動(dòng)了界面之后,查看我們的Process.Modules,可以看到我們注入的Inject.dll,那我們看看有沒有創(chuàng)建txt呢。在下面可以看到,我們已經(jīng)成功注入到微信進(jìn)程并且創(chuàng)建了一個(gè)example.txt,并且寫入的內(nèi)容和上圖定義的內(nèi)容是一致的,到此,我們將我們dll注入到了微信進(jìn)程中去了。
結(jié)語
在上面我們講了一些如何找到靜態(tài)數(shù)據(jù),以及根據(jù)基址,偏移量在進(jìn)程啟動(dòng)的時(shí)候找到我們想要的數(shù)據(jù),并且將我們的dll成功注入到進(jìn)程里面去,在后面,我可能還會(huì)在深入研究一下逆向,到時(shí)候會(huì)繼續(xù)發(fā)文,感興趣的朋友可以關(guān)注一波,同時(shí),近期,還破解了微信Sqlite本地?cái)?shù)據(jù)庫獲取了一些內(nèi)容,下面是獲取的數(shù)據(jù)內(nèi)容,這個(gè)我應(yīng)該不會(huì)開源,但是會(huì)有一個(gè)c語言的寫的解密demo開源,同時(shí)可能會(huì)分享一部分c#獲取解密密鑰的代碼,同時(shí)也需要一些逆向的知識,win32api,這個(gè)東西由于涉及個(gè)人隱私,所以我尚不確定是否開源,因?yàn)榇嬖谟械娜巳绻麙祚R,可以竊取他人的隱私,所以后續(xù)再說,同時(shí)在寫的,講的不對的地方,歡迎各位大佬指正。
到此這篇關(guān)于C#使用Win32 Api實(shí)現(xiàn)進(jìn)程注入到wechat的文章就介紹到這了,更多相關(guān)c#使用Win32 Api內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談C# 非模式窗體show()和模式窗體showdialog()的區(qū)別
下面小編就為大家?guī)硪黄獪\談C# 非模式窗體show()和模式窗體showdialog()的區(qū)別。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-07-07Winform界面中實(shí)現(xiàn)通用工具欄按鈕的事件處理方法
下面小編就為大家分享一篇Winform界面中實(shí)現(xiàn)通用工具欄按鈕的事件處理方法,具有很好的參考價(jià)值,希望對大家有所幫助2017-11-11C#實(shí)現(xiàn)強(qiáng)制關(guān)閉當(dāng)前程序進(jìn)程
這篇文章主要介紹了C#實(shí)現(xiàn)強(qiáng)制關(guān)閉當(dāng)前程序進(jìn)程,本文直接給出實(shí)現(xiàn)代碼,可以實(shí)現(xiàn)完全Kill掉不留痕跡,需要的朋友可以參考下2015-06-06DevExpress之ChartControl實(shí)現(xiàn)餅狀圖百分比演示實(shí)例
這篇文章主要介紹了DevExpress之ChartControl實(shí)現(xiàn)餅狀圖百分比演示的方法,實(shí)例講述了窗體與圖形繪制函數(shù)的用法,需要的朋友可以參考下2014-10-10C#操作LINQ to SQL組件進(jìn)行數(shù)據(jù)庫建模的基本教程
這篇文章主要介紹了C#操作LINQ to SQL組件進(jìn)行數(shù)據(jù)庫建模的基本教程,LINQ to SQL被集成在.NET框架之中,需要的朋友可以參考下2016-03-03