人均瑞數系列,瑞數 5 代 JS 逆向分析

来源:https://www.cnblogs.com/ikdl/archive/2022/09/01/16647423.html
-Advertisement-
Play Games

聲明 本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據介面等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關! 本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權, ...


聲明

本文章中所有內容僅供學習交流使用,不用於其他任何目的,不提供完整代碼,抓包內容、敏感網址、數據介面等均已做脫敏處理,嚴禁用於商業用途和非法用途,否則由此產生的一切後果均與作者無關!

本文章未經許可禁止轉載,禁止任何修改後二次傳播,擅自使用本文講解的技術而導致的任何意外,作者均不負責,若有侵權,請在公眾號【K哥爬蟲】聯繫作者立即刪除!

前言

01

瑞數動態安全 Botgate(機器人防火牆)以“動態安全”技術為核心,通過動態封裝、動態驗證、動態混淆、動態令牌等技術對伺服器網頁底層代碼持續動態變換,增加伺服器行為的“不可預測性”,實現了從用戶端到伺服器端的全方位“主動防護”,為各類 Web、HTML5 提供強大的安全保護。

在 K 哥往期的文章《人均瑞數系列,瑞數 4 代 JS 逆向分析》中,詳細介紹了瑞數的特征、如何區分不同版本、瑞數的代碼結構以及各自的作用,本文就不再贅述了,不瞭解的同志可以先去看看之前的文章。

本文案例中瑞數 5 代網站為:aHR0cHM6Ly93d3cubm1wYS5nb3YuY24vZGF0YXNlYXJjaC9ob21lLWluZGV4Lmh0bWw=

定位 Cookie,首選 Hook 來的最快,通過 Fiddler 插件、油猴腳本、瀏覽器插件等方式註入以下 Hook 代碼:

(function() {
    // 嚴謹模式 檢查所有錯誤
    'use strict';
    // document 為要hook的對象 這裡是hook的cookie
	var cookieTemp = "";
    Object.defineProperty(document, 'cookie', {
		// hook set方法也就是賦值的方法 
		set: function(val) {
				// 這樣就可以快速給下麵這個代碼行下斷點
				// 從而快速定位設置cookie的代碼
				console.log('Hook捕獲到cookie設置->', val);
                debugger;
				cookieTemp = val;
				return val;
		},
		// hook get 方法也就是取值的方法 
		get: function()
		{
			return cookieTemp;
		}
    });
})();

斷下之後往上跟棧,可以看到組裝 Cookie 後賦值給 document.cookie 的代碼,類似如下結構:

02

繼續往上跟棧,和4代瑞數類似,(772, 1) 的位置是入口,4代有一次生成假 cookie 的過程,5代就沒有了,如下圖所示:

03

再往前跟棧,來到首頁代碼,這裡就是我們熟悉的 call 位置了,圖中 _$ug 實際上是 eval 方法,傳入的第一個參數 _$Cs 是 Window 對象,第二個對象 _$Dm 是我們前面看到的 VM 虛擬機中的 IIFE 自執行代碼。

04

VM 代碼以及 $_ts 變數獲取

獲取 VM 代碼和 $_ts 變數是第一步,和4代類似,複製外鏈 JS(例如 fjtvkgf7LVI2.a670748.js)的代碼和 412 頁面的自執行代碼到文件,本地直接運行即可,需要輕度補一下環境,缺啥補啥,大致補一下 window、location、document 就行了,補的具體內容可以直接在瀏覽器控制台使用 copy() 命令複製過來,然後 VM 代碼我們就可以直接 Hook eval 的方式得到,這裡 $_ts 變數的獲取和4代有點兒區別,4代我們的做法是運行完代碼後直接取 window.$_ts 就行了,5代運行完代碼後會有一個清空 $_ts 的操作,可以自己跟棧看一下邏輯,要麼把清空的邏輯刪了,要麼定義一個全局變數,然後直接在 call 的地方將 $_ts 的值導出來:

05

大致的補環境代碼如下:

var eval_js = ""
var rs_ts = ""

window = {
    $_ts: {},
    eval: function (data) {
        eval_js = data
    }
}

location = {
    "ancestorOrigins": {},
    "href": "https://脫敏處理/datasearch/home-index.html",
    "origin": "https://脫敏處理",
    "protocol": "https:",
    "host": "www.脫敏處理.cn",
    "hostname": "www.脫敏處理.cn",
    "port": "",
    "pathname": "/datasearch/home-index.html",
    "search": "",
    "hash": ""
}

document = {
    "scripts": ["script", "script"]
}

獲取 VM 代碼以及 $_ts 變數:

06

善用 Watch 跟蹤功能

在跟棧分析之前,有必要瞭解一下瀏覽器開發者工具的 Watch 功能,它能夠持續跟蹤某個變數的值,對於瑞數這種控制流很多的情況,設置相應的變數跟蹤,能夠讓你知道你現在處於哪個控制流中,以及生成的數組的變化,不至於跟著跟著不知道到哪一步了。如下圖所示,_$S8 表示目前正處於第 279 號大控制流,_$5x 表示大控制流下的哪個分支,_$mz 表示 128 位大數組。

07

跟棧分析

老樣子,本地替換一套 412 頁面的代碼,固定下來,然後開始跟棧分析。直接從 (772, 1) 開始跟(文中說的第多少號控制流、第幾步均為作者自己的叫法,第多少步並不代表實際上的步驟,僅表示關鍵步驟):

08

單步進來,_$qh 是傳進來的參數 1,即將進入 742 號控制流:

09

進入 742 號控制流,第 1 步通過一個方法獲取了一個時間戳,進入這個方法內部,對時間戳進行了差值計算,會發現有兩個變數 _$tb_$t1 已經生成了值:

10

11

這兩個值也是時間戳,怎麼來的?直接搜索這兩個變數,搜索結果有幾個全部打上斷點,刷新斷下後往前跟棧,會發現是最開始走了一遍 703 號控制流:

12

先單步跟一遍 703 號控制流,703 號控制流第 1 步是進入 699 號控制流,返回一個數組,沒有特別的,直接扣代碼即可:

13

703 號控制流第 2、3 步分別取數組的值:

14

15

703 號控制流第 4、5、6 步生成兩個時間戳並賦值給前面提到的 _$tb_$t1 變數,涉及到的方法也沒有什麼特別的,缺啥搜啥補啥即可:

16

17

18

703 號控制流第 7 步,這裡修改了 $_ts 的某個值(VM 代碼中,$_ts 被賦值給了另一個變數,下圖中是 _$iw),_$iw._$uq 原本的值是 _$ou,修改後的值是 181,這個值也是後面關鍵 4 位數組中的其中一個,具體邏輯後面再講。

19

703 號控制流結束,我們繼續前面的 742 號控制流,742 號控制流第 2 步,將前面生成的時間戳賦值給另一個變數。

20

742 號控制流第 3 步,進入 279 號控制流,279 號控制流是生成 128 位數組的關鍵。

21

進入 279 號控制流,第 1 步定義了一個變數:

22

279 號控制流,第 2 步,進入 157 號控制流,157 號控制流主要是做自動化檢測

23

24

279 號控制流,第 3、4、5 步,做了一些運算,一些全局變數的值會改變,後續的數組裡會用到。

25

26

27

279 號控制流,第 6 步,初始化了一個 128 位的空數組,後續的操作都是為了往這個數組裡面填充值。

28

279 號控制流,第 7 步,進入 695 號控制流,生成一個 20 位的數組。

29

進入 695 號控制流看一下,第 1 步,取 $_ts 的一個值,生成 16 位數組。

30

695 號控制流,第 2 步,取 $_ts 里的四個值,與前面的 16 位數組一起組成 20 位數組。

31

這裡註意這四個值怎麼來的,以第二個值 _$iw._$KI 為例,搜索發現有一條語句 _$iw._$KI = _$iw[_$iw._$KI](_$bl, _$n2);,首先等號右邊取 _$iw._$KI 的值為 _$Mo,然後 _$iw["_$Mo"] 實際上就是 _$iw._$Mo,前面的定義 _$iw._$Mo = _$1D_$1D 是個方法,所以原語句相當於 _$iw._$KI = _$1D(_$bl, _$n2),其他三個值的來源也是類似的。

32

695 號控制流結束,回到 279 號控制流,第 8 步,將前面的時間戳轉換成了一個 8 位數組。

33

279 號控制流,第 9 步,往 128 位數組裡面添加了一個值。

34

_$ae 這個值怎麼來的?搜索下斷點並跟棧,發現是開頭走了第 178 號控制流得來的,跟著走一遍即可。

35

36

279 號控制流,第 10 步,又往 128 位數組裡面添加了一個值,這個值是開始 279 號控制流傳過來的。

37

38

279 號控制流,第 11、12、13、14 步,時間戳相關計算,然後生成兩個 2 位數組。註意這裡面的兩個變數,_$ll_$ed,在刷新 cookie、生成尾碼的時候可能是有值的,僅訪問主頁沒有值不影響。

39

40

41

42

279 號控制流,第 15 步,往 128 位數組裡面添加了一個 4 位數組 _$bl,搜索也可以找到是通過 723 號控制流得來的。

43

44

這裡的 723 號控制流,實際上是取了 $_ts 某個值進行運算,生成 16 位數組,然後截取前 4 位數組返回的。

45

46

279 號控制流,第 16 步,往 128 位數組裡面添加了一個 8 位數組 _$Yb

47

8 位數組 _$Yb 同樣搜索打斷點,可以在一個賦值語句斷下:

48

可以看到 _$EJ 的值就是 _$Yb,往前跟棧,會發現先後經過了 657 號、10 號、777 號控制流,其中 777 號控制流是入口:

49

50

51

如果單步跟 777 號控制流,你會發現步驟較多,中間有些語句不好處理,且容易跟丟,所以我們這裡就直接關註 657 號控制流就行了,777 號控制流直接到 10 號控制流,再到 657 號控制流,中間的一些過程暫時不管,跟到缺什麼的時候再說(後續有很多取值賦值等操作都是在 777 號控制流里實現的,可以註意一下),這段邏輯在本地表現的代碼如下圖所示:

52

這裡直接單步跟一下 657 號控制流,第 1、2 步 new 了一個方法。

53

54

這裡就要註意了,容易跟丟,先進入 _$bH 方法打上斷點,然後下一個斷點就走到裡面了,接著在單步調試,會進到另一個小的控制流裡面,如下圖所示:

55

56

開始單步跟第 96 號小控制流,第 1 步定義了一個變數。

57

96 號小控制流,第 2 步將 _$PI 的值賦值給了 _$fT,而 _$PI 的值其實是 window.localStorage.$_YWTUwindow.localStorage 裡面有很多值,這個東西我們文章最後再講,其中一些值與瀏覽器指紋相關,這裡先知道他是取值就行了。

58

96 號小控制流,第 3 步,進入第 94 號小控制流,最終生成的是一個 8 位數組,這個其實就是前面我們想要的 _$Yb 的值了。

59

後面沒有什麼特別的,中間幾步我就省略了,照著扣代碼就行了,然後 96 號小控制流,第 4 步,就將 _$EJ 的值賦值給 _$Yb 了。

60

到這裡先別急著結束,後面還有關鍵的幾步,96 號小控制流,第 5 步,又遇到了和前面類似的寫法。

61

同樣的,先進 _$pu 打斷點,再單步跟。

62

來到另一個小控制流,如下圖所示:

63

10 號小控制流第 1 步,取 window.localStorage.$_cDro 的值,轉為 int 類型,賦值給 _$5s,這個 _$5s 後續也會加到 128 位大數組裡面。

64

10 號小控制流後續還有幾步,沒啥用可以省略,最後一步返回 96 號小控制流。

65

然後 96 號小控制流後續也沒啥了,返回 657 號控制流。

66

此時我們已經拿到 _$Yb 了,777 號控制流就先不管了,後續還有些代碼先不管不用扣,等用到的時候再說,返回 279 號控制流,接著前面的步驟,來到第 17 步,變數 _$5s 經過 264 號控制流後,生成了一個值並添加到 128 位大數組裡面,而 _$5s 的值正是前面我們跟 _$Yb 時,通過 777 號控制流拿到的,實際上也就是取 window.localStorage.$_cDro 的值,轉為了 int 類型。

67

279 號控制流,第 18、19、20 步,往 128 位數組裡面添加了兩個定值、一個 8 位數組。

68

69

70

279 號控制流,第 21 步,往 128 位數組裡面添加了一個 undefined 占位,後續會有操作將其填充值。

71

72

279 號控制流,第 22 步,進入 58 號控制流,58 號控制流與 window.localStorage.$_fb 的值有關,如果有這個值,就會生成 20 位數組,如果沒有就是 undefined。58 號控制流就只有一步,返回一個變數,本文中是 _$0g

73

74

這個 _$0g 是咋來的呢?同樣的直接搜索,下斷點,發現是通過 112 號控制流得來的,往前跟棧,同樣是先經過了 777 號控制流,和之前的情況類似,中間的過程就不看了,直接看這個 112 號控制流。

75

本文中,112 號控制流傳的參是 _$bd[279]$_fb,112 號控制流第 1 步,進入 247 號控制流。

76

247 號控制流就 3 步,先將 window.localStorage 賦值給一個變數,然後取其中 $_fb 的值再返回。

77

78

79

112 號控制流第 2、3 步,一個 try-catch 語句,取 window.localStorage.$_fb 計算得到 25 位數組,然後取前 20 位並返回,這就是前面我們需要的 _$0g 的值了。

80

81

279 號控制流,第 23 步,將前面 window.localStorage.$_fb 計算得到的 20 位數組添加到 128 位大數組裡面,註意這一步如果沒有 window.localStorage.$_fb 值的話,是不會添加的。

82

279 號控制流,第 24 步,對一個變數進行位運算,然後取 window.localStorage.$_f0 進行運算,如果 $_f0 為空的話是不會往 128 位大數組裡添加值的。

83

84

85

279 號控制流,第 25 步,對一個變數進行位運算,然後取 window.localStorage.$_fh0 進行運算,如果 $_fh0 為空的話是不會往 128 位大數組裡添加值的。

86

87

88

279 號控制流,第 26 步,對一個變數進行位運算,然後取 window.localStorage.$_f1 進行運算,如果 $_f1 為空的話是不會往 128 位大數組裡添加值的。

89

90

91

279 號控制流,第 27 步,進入 611 號控制流,611 號控制流主要是檢測 window.navigator.connection.type,即 NetworkInformation 網路相關信息,裡面判斷了 type 是不是 bluetoothcellularethernetwifiwimax,正常的話應該返回 0。

92

93

94

279 號控制流,接下來幾步都是類似的,這裡就直接統稱第 28 步了,首先對一個變數進行位運算,然後分別取 window.localStorage.$_frwindow.localStorage.$_fpn1window.localStorage.$_vvCIwindow.localStorage.$_JQnh 進行運算,同樣如果這些變數為空的話,也是不會往 128 位大數組裡添加值的。

96

97

98

99

279 號控制流,第 29 步,往 128 位大數組裡添加了一個定值 4,本文中該變數名是 _$kW

100

_$kW 這個變數是咋來的,和前面的套路類似,直接搜索下斷,同樣是經過開頭的 777 號控制流得來的,如下圖所示:

101

繼續 279 號控制流,中間有一些變數位運算之類的就省略了,第 30、31 步,取了一個 https:443 的長度進行計算,先後往 128 位大數組裡添加了一個定值和一個 9 位數組。

102

103

279 號控制流,接下來幾步都是在取值,都差不多,就統稱為第 32 步了。

104

105

106

107

108

109

279 號控制流,第 33 步,之前 128 位大數組第 12 位是個 undefined,這裡就將第 12 位填充上了一個 4 位數組,其中有個變數 _$8L,前面我們跟步驟的時候就有一個變數一直在做位運算,此處的 _$8L 就是這麼來的。

110

279 號控制流,最後兩步,原來的 128 位大數組,只取有值的前 21 位,一共有多少位與 window.localStorage 的某些值有關,有值的話就長一些,沒有就短一些,然後再將數組的每個元素合併成最終的一個大數組並返回,279 號控制流就結束了。

111

112

返回到文章開頭的邏輯,279 號控制流結束,返回到 742 號控制流,第 2 步,定義了一個變數並生成了一個 32 位數組。

113

114

742 號控制流,第 3 步,取 $_ts 裡面的某個值並賦值給一個變數。

115

742 號控制流,第 4 步,將前面 279 號控制流得到的大數組與上一步 $_ts 裡面的某個值進行合併,合併後計算得到一個值。

116

742 號控制流,第 4 步,將上一步得到的值進一步計算得到一個 4 位數組,再將其和大數組合併。

117

742 號控制流,接下來幾步是對時間戳進行各種操作,這裡統稱為第 5 步。

118

119

120

121

742 號控制流,第 6 步,將上一步得到的 4 個時間戳進行計算,得到一個 16 位數組。

122

742 號控制流,第 7 步,將上一步得到的 16 位數組進行異或運算。

123

742 號控制流,第 8 步,將上一步的 16 位數組進行計算,得到一個字元串。

124

742 號控制流,第 9 步,正式生成 cookie 值,其中 _$bd[274] 定值,一般視為版本號,將上一步得到的字元串、之前得到的大數組和一個 32 位數組進行計算、組合,得到最終結果。

125

742 號控制流結束,返回 772 號控制流,利用了一個方法,組裝 cookie,然後賦值給 document.cookie,整個流程就結束了。

126

127

128

代碼中用到的 $_ts 的值需要我們自己去匹配出來,動態替換,這些步驟和 4 代是類似的,本文就不再重覆敘述,可以參考 K 哥 4 代的那篇文章進行處理即可。

129

尾碼生成

本例中,請求頭中有個 sign 參數,Query String Parameters 有兩個尾碼參數,這兩個尾碼和 4 代類似,都是瑞數生成的。

130

131

和 4 代的處理方法一樣,我們下一個 XHR 斷點,先讓網頁載入完畢,然後打開開發者工具,過掉無限 debugger 後,點擊搜索就會斷下,如下圖所示:

132

往上跟棧到 hasTokenGet,是一個 sojson 旗下的 jsjiami v6 混淆,不值一提,重點是 jsonMD5ToStr 方法,先對傳進去的參數做了一些編碼處理,最後返回的是 hex_md5,和線上 MD5 加密的結果是一樣的,說明是標準的 MD5。

133

134

重點來看瑞數的兩個尾碼生成方式,和 4 代一樣,XMLHttpRequest.sendXMLHttpRequest.open 被重寫了,如下圖所示,在 XMLHttpRequest.open 下個斷點,也就是圖中的 _$RQ 方法,arguments[1] 就是原始 URL,經過圖中的 _$tB 方法處理後就能拿到尾碼。

135

跟進圖中的 _$tB 方法,_$tB 方法里嵌套了一些其他方法,走一遍邏輯,到圖中的 _$5j 方法里,前面的一部分都是在對傳入的 URL 做處理。

136

接下來是生成了一個 16 位數組:

137

然後這個 16 位數組經過一個方法後就生成了第一個尾碼,如下圖所示,本文中這個方法是 _$ZO

138

跟進 _$ZO 方法,主要有以下 5 步:

第 1 步:生成了一個 32 位數組;

第 2 步:將之前的 16 位數組以及兩個變數拼接生成一個 50 位的數組;

第 3 步:進入 744 控制流,這裡你會發現和之前我們跟 cookie 時的 742 號控制流是一樣的,重覆走了一遍,所以這裡就不再跟了;

第 4 步:將生成的第一個尾碼值進行處理,得到一個兩位的字元串,這個字元串在獲取第二個尾碼的時候會用到;

第 5 步:將第一個尾碼名稱和值進行拼接並返回,此時,第一個尾碼 hKHnQfLv 就生成了。

139

接著前面的 _$5j 方法,圖中的 _$5j 這一步,就是獲取第二個尾碼 8X7Yi61c 的值:

140

主要是看一下圖中的 _$UM 方法,先將前面生成的兩位的字元串與 URL 參數進行拼接,然後會經過一個 _$Nr 方法就能得到第二個尾碼的值了。

141

再來看一下 _$Nr 方法,先生成一個類似 53924 的值,然後一個 try 語句,註意這裡有個方法,圖中的 _$Js 方法,裡面用到了 $_ts 裡面的某個值,後面又生成了一個由數字組成的字元串,再次經過組合、計算後得到最終的值。

142

143

回到前面的 _$UM 方法,首碼 8X7Yi61c 與值組合,自此,兩個尾碼都拿到了:

144

指紋生成

我們前面已經分析了,在往 128 位數組裡添加值的時候,會有取 window.localStorage 裡面的某些值進行計算的步驟,這些值就是取瀏覽器 canvas 等指紋生成的,指紋隨機就能併發,通常訪問單獨的一個 html 頁面是不校驗指紋的,生成的短 cookie 就能通過,但是一些查詢數據介面會校驗指紋,通過觸發 load 事件來向 cookie 里添加指紋,使得 cookie 長度變長,怎麼查找指紋在哪裡生成的,這裡推薦直接看視頻資料,已經講得很清楚了,篇幅太長,本文就不再贅述了,資料鏈接:https://mp.weixin.qq.com/s/DEUc1K8WaO_Cq1a2r0Ge5g


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

-Advertisement-
Play Games
更多相關文章
  • 山河遠闊,煙火人間,又一年,千里嬋娟~ 今天給大家帶來的是給玉兔投喂月餅的小游戲。八月十五中秋夜晚,讓我們對著月亮許願:希望我們在意和在意我們的人,諸邪避退、百事無忌、平安喜樂、萬事勝意。提前祝大家中秋節快樂。 中秋節的起源 中秋節起源於上古時代,普及於漢代,定型於唐朝初年,盛行於宋朝以後。中秋節是 ...
  • 目錄 一.OpenGL 圖像飽和度調節 1.原始圖片 2.效果演示 二.OpenGL 圖像飽和度調節源碼下載 三.猜你喜歡 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學 ...
  • JavaGUI-坦克大戰 1.Java繪圖坐標體系 坐標體系介紹:下圖說明瞭一個Java坐標體系。坐標原點位於左上角,以像素為單位。在Java坐標體系中,第一個是x坐標,表示當前位置為水平方向,距離坐標原點x個像素;第二個是y坐標,表示當前位置為垂直方向,距離坐標原點y個像素。 坐標體系-像素: 繪 ...
  • Spring-04 聲明式事務 1、事務的定義 事務就是由一組邏輯上緊密關聯的多個工作單元(資料庫操作)而合併成一個整體,這些操作要麼都執行,要麼都不執行。 2、事務的特性:ACID 1)原子性A :原子即不可再分,表現:一個事務涉及的多個操作在業務邏輯上缺一不可,保證同一個事務中的操作要不都提交, ...
  • Vector底層實現 vector的三個私有成員 :_start 記錄初始位置 , _finish 記錄有效字元 , _endofstoage 記錄容量大小 vector會存儲的類型不同,所以要用模版來定類型 typedef T* iterator; iterator _start; iterato ...
  • Java註解 1.註解的理解 註解(Annotation)也被稱為元數據(Metadata),用於修飾解釋 包、類、方法、屬性、構造器、局部變數等數據信息 和註釋一樣,註解不影響程式邏輯,但註解可以被編譯或者運行,相當於嵌入在代碼中的補充信息 在JavaSE中,註解的使用目的比較簡單,例如標記過時的 ...
  • 前言 嗨嘍~大家好呀,這裡是魔王吶 ! 又是學習的一天,讓我們開始叭~ 環境使用: Python 3.8 Pycharm 模塊使用: requests >>> pip install requests re csv 如果安裝python第三方模塊: win + R 輸入 cmd 點擊確定, 輸入安裝 ...
  • 數組(array):是一種用於存儲多個相同數據類型的存儲模型(可以理解為容器) 數組定義和靜態初始化 數組的兩種定義格式: 格式1: 數據類型[] 變數名; 範例: int[] arr; // 定義了一個int類型的數組,數組名是arr 格式2: 數據類型 變數名[]; 範例: int arr[]; ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...