【學習】Windows PE文件學習(一:導出表)

来源:http://www.cnblogs.com/Madridspark/archive/2016/08/16/WinPEFile.html
-Advertisement-
Play Games

今天做了一個讀取PE文件導出表的小程式,用來學習。 參考了《Windows PE權威指南》一書。 首先, PE文件的全稱是Portable Executable,可移植的可執行的文件,常見的EXE、DLL、OCX、SYS、COM都是PE文件。 我們知道,一個Windows程式,它所實現的所有功能最終 ...


  今天做了一個讀取PE文件導出表的小程式,用來學習。

  參考了《Windows PE權威指南》一書。

  首先, PE文件的全稱是Portable Executable,可移植的可執行的文件,常見的EXE、DLL、OCX、SYS、COM都是PE文件。

  我們知道,一個Windows程式,它所實現的所有功能最終幾乎都是調用系統DLL提供的API函數。要使用任何一個DLL所提供的函數,我們需要將它導入,也就是用到了導入表。然而對於那些提供了被導出的函數的DLL程式來說,他們必須使用導出表將函數導出,之後別的程式才可以使用。無論是系統提供的標準DLL還是個人編寫的DLL,只要想提供自己的函數給別人使用就必須建立導出表。一般使用任何開發環境編寫具有導出功能的程式,導出表都是由鏈接器自動建立的。程式員只需指定被導出的函數名稱或序號即可。

  導出表通常出現在DLL文件的.edata節中。

  知道了導出表的位置,我們可以得到導出函數的地址,進而對這些函數進行Hook。而我們現在的目的是為了學習PE文件中導出表的構成,所以有必要瞭解PE文件的結構。

  

1 基本概念

  註:以下引用部分均來自網路

下表描述了貫穿於本文中的一些概念:

名稱 描述
地址 是“虛擬地址”而不是“物理地址”。為什麼不是“物理地址”呢?因為數據在記憶體的位置經常在變,這樣可以節省記憶體開支、避開錯誤的記憶體位置等的優勢。同時用戶並不需要知道具體的“真實地址”,因為系統自己會為程式準備好記憶體空間的(只要記憶體足夠大)
鏡像文件 包含以EXE文件為代表的“可執行文件”、以DLL文件為代表的“動態鏈接庫”。為什麼用“鏡像”?這是因為他們常常被直接“複製”到記憶體,有“鏡像”的某種意思。看來西方人挺有想象力的哦^0^
RVA 英文全稱Relatively Virtual Address。偏移(又稱“相對虛擬地址”)。相對鏡像基址的偏移。(有時候不一定是相對鏡像的基址,還可能以某個結構的首地址為基址)
節是PE文件中代碼或數據的基本單元。原則上講,節只分為“代碼節”和“數據節”。(文件中節大小通常以磁碟的一個物理扇區也就是512B對齊,若是鏡像文件載入到記憶體中,以一個記憶體頁大小對齊,32位為4K,64位為8K)
VA 英文全稱Virtual Address。虛擬地址(虛擬記憶體中的正常地址,不需要進行轉換)

    有特殊的節無論是在文件中還是在記憶體中,對齊粒度與其他的節都不同,如:資源位元組碼以雙字對齊

2 PE文件的結構

  PE文件的總體結構:如果形象地說,即是3個頭和身子。3個頭是Dos頭、Nt頭和節表(節頭),身子就是一個一個地節(存放數據和代碼的地方)以上的各個頭部都是數據結構,可以在winnt.h頭文件中找到它們對應的struct定義(Nt頭分為32位和64位)。

  由於PE文件是相容Windows NT以前的Dos系統的,所以現在的任何一個PE文件拿到Dos系統上都是可以運行的,不過大多數可能也只能打出一句話:“This program cannot be run in DOS mode”。這是由PE文件的結構中的Dos頭決定的。

用記事本打開任何一個鏡像文件,其頭2個位元組必為字元串“MZ”,這是Mark Zbikowski的姓名縮寫,他是最初的MS-DOS設計者之一。然後是一些在MS-DOS下的一些參數,這些參數是在MS-DOS下運行該程式時要用到的。在這些參數的末尾也就是文件的偏移0x3C(第60位元組)處是是一個4位元組的PE文件簽名的偏移地址。該地址有一個專用名稱叫做“E_lfanew”。這個簽名是“PE00”(字母“P”和“E”後跟著兩個空位元組)。緊跟著E_lfanew的是一個MS-DOS程式。那是一個運行於MS-DOS下的合法應用程式。當可執行文件(一般指exe、com文件)運行於MS-DOS下時,這個程式顯示“This program cannot be run in DOS mode(此程式不能在DOS模式下運行)”這條消息。用戶也可以自己更改該程式,有些還原軟體就是這麼乾的。同時,有些程式既能運行於DOS又能運行於Windows下就是這個原因。Notepad.exe整個DOS頭大小為224個位元組,大部分不能在DOS下運行的Win32文件都是這個值。MS-DOS程式是可有可無的,如果你想使文件大小儘可能的小可以省掉MS-DOS程式,同時把前面的參數都清0。

3 Nt頭部 IMAGE_NT_HEADERS

  PE文件中較為複雜的部分就是這裡了。

  在 2 中說到的DosHeader->E_lfanew所指向的簽名“PE\0\0”就是Nt頭的第一個成員了,我們在編程中得到Nt頭的方法也是這樣做的,因為Dos頭的第二部分MS-DOS程式部分的大小是可以改變的,連帶著整個Dos就是不定長的了,只有其中的E_lfanew指向它自己的末尾。

  Nt頭同樣分為兩部分(除去簽名4個位元組):

  給出winnt.h中的定義

1 typedef struct _IMAGE_NT_HEADERS {
2     DWORD Signature;                        //4 bytes PE文件頭標誌:(e_lfanew)->‘PE\0\0’
3     IMAGE_FILE_HEADER FileHeader;           //20 bytes PE文件物理分佈的信息
4     IMAGE_OPTIONAL_HEADER32 OptionalHeader; //224bytes PE文件邏輯分佈的信息
5 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

  其中的IMAGE_FILE_HEADER我們稱作文件頭,IMAGE_OPTIONAL_HEADER32稱作可選映像頭(我習慣稱之為選項頭)。有點滑稽的是,選項頭可以說是PE文件中最重要、最複雜的部分了,卻是可選的。。

同時我們看到,選項頭在32位和64位PE文件中結構是有所不同的,註意,只是有所不同而已,大致上還是沒什麼區別的。但是在編程中我們必須將其考慮進去,由於選項頭是不同的,所以Nt頭也會是不同的。

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;                //運行平臺
    WORD    NumberOfSections;        //文件區塊數目
    DWORD   TimeDateStamp;            //文件創建日期和時間
    DWORD   PointerToSymbolTable;    //指向符號表(主要用於調試)
    DWORD   NumberOfSymbols;        //符號表中符號個數
    WORD    SizeOfOptionalHeader;        //IMAGE_OPTIONAL_HEADER32 結構大小
    WORD    Characteristics;            //文件屬性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

  但是文件頭還是很清晰明瞭的,其中比較常用的成員就是Machine和Characteristics了。都是用來判斷的,其中Machine標誌了PE文件需要運行的目標平臺,也就是期望在哪種指令集的CPU的平臺上被載入,一般可以用來判斷PE文件是64位還是32位的;Characteristics是採用標誌位的方式來判斷許多關於PE文件的信息,其中最重要的是判斷其是不是dll,使用的時候與(&)上就行了。

#define IMAGE_FILE_RELOCS_STRIPPED           0x0001  // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE          0x0002  // File is executable  (i.e. no unresolved external references).這是標誌其能不能獨立運行,像dll就必須讓別的模塊來載入自己,但是exe和sys是自己載入運行的
#define IMAGE_FILE_LINE_NUMS_STRIPPED        0x0004  // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED       0x0008  // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM         0x0010  // Aggressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE       0x0020  // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO         0x0080  // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE             0x0100  // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED            0x0200  // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP   0x0400  // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP         0x0800  // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM                    0x1000  // System File.
#define IMAGE_FILE_DLL                       0x2000  // File is a DLL.重要
#define IMAGE_FILE_UP_SYSTEM_ONLY            0x4000  // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI         0x8000  // Bytes of machine word are reversed.

#define IMAGE_FILE_MACHINE_UNKNOWN           0
#define IMAGE_FILE_MACHINE_I386              0x014c  // Intel 386.32位
#define IMAGE_FILE_MACHINE_R3000             0x0162  // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000             0x0166  // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000            0x0168  // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2         0x0169  // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA             0x0184  // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3               0x01a2  // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP            0x01a3
#define IMAGE_FILE_MACHINE_SH3E              0x01a4  // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4               0x01a6  // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5               0x01a8  // SH5
#define IMAGE_FILE_MACHINE_ARM               0x01c0  // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB             0x01c2  // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT             0x01c4  // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33              0x01d3
#define IMAGE_FILE_MACHINE_POWERPC           0x01F0  // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP         0x01f1
#define IMAGE_FILE_MACHINE_IA64              0x0200  // Intel 64 64位
#define IMAGE_FILE_MACHINE_MIPS16            0x0266  // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64           0x0284  // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU           0x0366  // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16         0x0466  // MIPS
#define IMAGE_FILE_MACHINE_AXP64             IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE           0x0520  // Infineon
#define IMAGE_FILE_MACHINE_CEF               0x0CEF
#define IMAGE_FILE_MACHINE_EBC               0x0EBC  // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64             0x8664  // AMD64 (K8) 64位
#define IMAGE_FILE_MACHINE_M32R              0x9041  // M32R little-endian
#define IMAGE_FILE_MACHINE_CEE               0xC0EE

   接下來重點介紹選項頭 IMAGE_OPTIONAL_HEADER。

 

偏移(32/64) 大小 英文名 中文名 描述
0 2 Magic 魔數 這個無符號整數指出了鏡像文件的狀態。
0x10B表明這是一個32位鏡像文件。
0x107表明這是一個ROM鏡像。
0x20B表明這是一個64位鏡像文件。
2 1 MajorLinkerVersion 鏈接器的主版本號 鏈接器的主版本號。
3 1 MinorLinkerVersion 鏈接器的次版本號 鏈接器的次版本號。
4 4 SizeOfCode 代碼節大小 一般放在“.text”節里。如果有多個代碼節的話,它是所有代碼節的和。必須是FileAlignment的整數倍,是在文件里的大小。
8 4 SizeOfInitializedData 已初始化數大小 一般放在“.data”節里。如果有多個這樣的節話,它是所有這些節的和。必須是FileAlignment的整數倍,是在文件里的大小。
12 4 SizeOfUninitializedData 未初始化數大小 一般放在“.bss”節里。如果有多個這樣的節話,它是所有這些節的和。必須是FileAlignment的整數倍,是在文件里的大小。
16 4 AddressOfEntryPoint 入口點 當可執行文件被載入進記憶體時其入口點RVA。對於一般程式鏡像來說,它就是啟動地址。為0則從ImageBase開始執行。對於dll文件是可選的。
20 4 BaseOfCode 代碼基址 當鏡像被載入進記憶體時代碼節的開頭RVA。必須是SectionAlignment的整數倍。
24 4 BaseOfData 數據基址 當鏡像被載入進記憶體時數據節的開頭RVA。(在64位文件中此處被併入緊隨其後的ImageBase中。)必須是SectionAlignment的整數倍。
28/24 4/8 ImageBase 鏡像基址 當載入進記憶體時鏡像的第1個位元組的首選地址。它必須是64K的倍數。DLL預設是10000000H。Windows CE 的EXE預設是00010000H。Windows 系列的EXE預設是00400000H。
32 4 SectionAlignment 記憶體對齊 當載入進記憶體時節的對齊值(以位元組計)。它必須≥FileAlignment。預設是相應系統的頁面大小。
36 4 FileAlignment 文件對齊 用來對齊鏡像文件的節中的原始數據的對齊因數(以位元組計)。它應該是界於512和64K之間的2的冪(包括這兩個邊界值)。預設是512。如果SectionAlignment小於相應系統的頁面大小,那麼FileAlignment必須與SectionAlignment相等。
40 2 MajorOperatingSystemVersion 主系統的主版本號 操作系統的版本號可以從“我的電腦”→“幫助”裡面看到,Windows XP是5.1。5是主版本號,1是次版本號
42 2 MinorOperatingSystemVersion 主系統的次版本號
44 2 MajorImageVersion 鏡像的主版本號
46 2 MinorImageVersion 鏡像的次版本號
48 2 MajorSubsystemVersion 子系統的主版本號
50 2 MinorSubsystemVersion 子系統的次版本號
52 2 Win32VersionValue 保留,必須為0
56 4 SizeOfImage 鏡像大小 當鏡像被載入進記憶體時的大小,包括所有的文件頭。向上舍入為SectionAlignment的倍數。
60 4 SizeOfHeaders 頭大小 所有頭的總大小,向上舍入為FileAlignment的倍數。可以以此值作為PE文件第一節的文件偏移量。
64 4 CheckSum 校驗和 鏡像文件的校驗和。計算校驗和的演算法被合併到了Imagehlp.DLL 中。以下程式在載入時被校驗以確定其是否合法:所有的驅動程式、任何在引導時被載入的DLL以及載入進關鍵Windows進程中的DLL。
68 2 Subsystem 子系統類型 運行此鏡像所需的子系統。參考後面的“Windows子系統”部分。
70 2 DllCharacteristics DLL標識 參考後面的“DLL特征”部分。
72 4/8 SizeOfStackReserve 堆棧保留大小 最大大小。CPU的堆棧。預設是1MB。
76/80 4/8 SizeOfStackCommit 堆棧提交大小 初始提交的堆棧大小。預設是4KB。
80/88 4/8 SizeOfHeapReserve 堆保留大小 最大大小。編譯器分配的。預設是1MB。
84/96 4/8 SizeOfHeapCommit 堆棧交大小 初始提交的局部堆空間大小。預設是4KB。
88/104 4 LoaderFlags 保留,必須為0
92/108 4 NumberOfRvaAndSizes 目錄項數目

數據目錄項的個數。由於以前發行的Windows NT的原因,它只能為16。

96/112 8*16 DataDirectory 數據目錄

目錄項數組,包含16個目錄項

 

  這是完整的選項頭的結構,其中只提Magic和DataDirectory,至於鏡像載入時的基址與重定向問題,本文不做介紹,因為PE文件解析並不需要把鏡像給載入到我們自己的程式中,只需要映射到記憶體中,對其內容進行解析即可。

  對Magic域進行判斷,可以區分文件是64位還是32位,所以到現在我們有兩種方法來區分。

  本文的主角——導出表就是由DataDirectory[0]中的目錄項指出的,具體如下:

typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD   VirtualAddress;
    DWORD   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

  由此我們可以知道,DataDirectory並不是直接指嚮導出表的,真相是這樣的:DataDirectory是一個數組,每個項都是一樣的,IMAGE_DATA_DIRECTORY,每一項都由一個地址和大小,這就告訴我們導出表的基地址和其大小(別小看這個大小,我們會用到的)。

  得到了導出表的地址和大小,那麼我們就可以搞些事情了(23333~)。

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;            // 這是這個PE文件的模塊名
    DWORD   Base;           
    DWORD   NumberOfFunctions;     // 這兩個域按字面意思理解,這個為總的導出函數的個數
    DWORD   NumberOfNames;      // 這個是有名稱的函數的個數,因為有的導出函數是沒有名字的,只有序號
    DWORD   AddressOfFunctions;     // RVA from base of image 這三個就是所謂的EAT,導出地址表
    DWORD   AddressOfNames;         // RVA from base of image Nt頭基址加上這個偏移得到的數組中存放所有的名稱字元串
    DWORD   AddressOfNameOrdinals;  // RVA from base of image Nt頭基址加上這個偏移得到的數組中存放所有的函數序號,並不一定是連續的,但一般和導出地址表是一一對應的
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

 

   這是導出表的結構,其中重要的域我用紅色的字標註了出來。

  我在網上查的資料說的比較清晰:

導出地址表(Export Address Table,EAT)

導出地址表的格式為下表所述的兩種格式之一。如果指定的地址不是位於導出節(其地址和長度由NT頭給出)中,那麼這個域就是一個Export RVA;否則這個域是一個Forwarder RVA,它給出了一個位於其它DLL中的符號的名稱。

偏移 大小 描述
0 4 Export RVA 當載入進記憶體時,導出函數RVA。
0 4 Forwarder RVA 這是指嚮導出節中一個以NULL結尾的ASCII碼字元串的指針。這個字元串必須位於Export Table(導出表)數據目錄項給出的範圍之內。這個字元串給出了導出函數所在DLL的名稱以及導出函數的名稱(例如“MYDLL.expfunc”),或者DLL的名稱以及導出函數的序數值(例如“MYDLL.#27”)。

Forwarder RVA導出了其它鏡像中定義的函數,使它看起來好像是當前鏡像導出的一樣。因此對於當前鏡像來說,這個符號同時既是導入函數又是導出函數。

例如對於Windows XP系統中的Kernel32.dll文件來說,它導出的“HeapAlloc”被轉發到“NTDLL.RtlAllocateHeap”。這樣就允許應用程式使用Windows XP系統中的Ntdll.dll模塊而不需要實際包含任何相關的導入信息。應用程式的導入表只與Kernel32.dll有關。

導出地址表的的值有時為0,此時表明這裡沒有導出函數。這是為了能與以前版本相容,省去修改的麻煩。

導出名稱指針表

導出名稱指針表是由導出名稱表中的字元串的地址(RVA)組成的數組。二進位進行排序的,以便於搜索。

只有當導出名稱指針表中包含指向某個導出名稱的指針時,這個導出名稱才算被定義。換句話說,導出名稱指針表的值有可能為0,這是為了能與前面版本相容。

導出序數表

導出序數表是由導出地址表的索引組成的一個數組,每個序數長16位。必須從序數值中減去Ordinal Base域的值得到的才是導出地址表真正的索引。註意,導出地址表真正的索引真正的索引是從0開始的。由此可見,微軟弄出Ordinal Base是找麻煩的。導出序數表的值和導出地址表的索引的值都是無符號數。

導出名稱指針表和導出名稱序數表是兩個併列的數組,將它們分開是為了使它們可以分別按照各自的邊界(前者是4個位元組,後者是2個位元組)對齊。在進行操作時,由導出名稱指針這一列給出導出函數的名稱,而由導出序數這一列給出這個導出函數對應的序數。導出名稱指針表的成員和導出序數表的成員通過同一個索引相關聯。

導出名稱表(Export Name Table,ENT)

導出名稱表的結構就是長度可變的一系列以NULL結尾的ASCII碼字元串。 導出名稱表包含的是導出名稱指針表實際指向的字元串。這個表的RVA是由導出名稱指針表的第1個值來確定的。這個表中的字元串都是函數名稱,其它文件可以通過它們調用函。

  這裡需要特別註意的是,有時候你在遍歷導出地址表的時候,有可能得到的並不是一個地址(或者說並不是目標函數的地址),而是一個字元串。那麼這就是遇到了函數轉發的情況。判斷方法就是上面所說的判斷這個指針是不是在導出表的範圍內。

  學習PE文件可能比較難想象其中的數據結構組織,因為比較複雜,所以我建議可以上網找關於PE文件各個結構的示意圖看看。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 結論: > Socket 理論上 支持 只上行,或者 只下行。 > 心跳包 必須是 上下行的 —— 心跳包請求(上行) - 心跳包響應(下行)。 > 如果 長時間 只有單向鏈接(只發送不接受,或者 只接受不發送) —— 路由器 就會 丟棄 Socket數據。 > 心跳包 不是 必須的 —— 任意 上 ...
  • 在一個基於面向服務的分散式環境中,藉助一個標準的、平臺無關的通信協議,使各個服務通過SOAP Message實現相互之間的交互。這個交互的過程實際上就是信息交換的過程。WCF支持不同形式的信息交換,我們把這稱之為信息交換模式(Message Exchange Pattern(簡稱MEP),下同), ... ...
  • 提交git代碼的時候報的錯誤 這是因為修改的東西太少的原因,應該多修改一些就可以提交了 例如:只是刪除了一個空格或者一個字元就提交git代碼的話就會提示這個錯誤 解決方法:多多的改變一下代碼,比如增加一下回車 ...
  • 直接看圖吧: ...
  • 在設計資料庫的時候,經常碰到那些表示狀態或類型的欄位,比如訂單的狀態,或者支付的類型。要為這一類數據選擇合適的數據類型,比較常用的有以下兩種方法。 方法一:tinyint+byte(枚舉) 資料庫中類型:tinyint c#中類型:byte,如代碼: 方法二:varchar(xx)+string(c... ...
  • 快捷鍵說明 1.Ctrl+B可以選擇顯示或隱藏左邊的項目導航框; 2.Ctrl+\可以實現在右邊再打開一個編輯工作區域。配合利用Ctrl+1,2,3可以快速切換編輯視窗 3.Ctrl+P快速打開搜索框搜索文件 4.Ctrl+Shift+P命令編輯。 5.Ctrl+Tab列出你所打開的所有文件列表。 ...
  • 目錄索引 【無私分享:ASP.NET CORE 項目實戰】目錄索引 簡介 在Asp.net Core VS2015中,我們發現還有很多不太簡便的地方,比如右擊添加視圖,轉到試圖頁等功能圖不見了,雖然我們可以通過工具欄的自定義命令,把這兩個右擊菜單添加上,但是貌似是灰色的不能用。 其實,這樣也好,通過 ...
  • 之前是打算寫一篇文章叫:Taurus.MVC 從入門到精通,一篇完事篇!後來轉指一念,還是把教程集在這個企業站項目上吧,之前發過一個幫師妹寫的企業站:“最近花了幾個夜晚幫師妹整了一個企業網站”技術風格是:文本資料庫(txt)+WebForm 這次轉型風格:文本資料庫(txt)+Taurus.MVC ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...