前言
上一篇我們講了標準 base64
算法還原,為了進一步學習 base64
算法特點,本文將結合 jsvmp
日志,實戰還原出 base64
魔改算法。
為了方便大家學習,我將入參和上篇文章一樣,入參為 Hello, World!
。
插樁
在js代碼中,找到 運算符
位置以及 apply
插樁點,結合插樁日志框架,代碼插樁如下:
jsvmp源碼:https://t.zsxq.com/B5aRh
日志分析
代碼執行后,日志如下:
第 1 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 0 ] 結果: 72
第 2 條: 函數: [Function: push] 調用者: [ 72 ] 參數: [ 72 ] 結果: 1
第 3 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 1 ] 結果: 101
第 4 條: 函數: [Function: push] 調用者: [ 72, 101 ] 參數: [ 101 ] 結果: 2
第 5 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 2 ] 結果: 108
第 6 條: 函數: [Function: push] 調用者: [ 72, 101, 108 ] 參數: [ 108 ] 結果: 3
第 7 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 3 ] 結果: 108
第 8 條: 函數: [Function: push] 調用者: [ 72, 101, 108, 108 ] 參數: [ 108 ] 結果: 4
第 9 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 4 ] 結果: 111
第 10 條: 函數: [Function: push] 調用者: [ 72, 101, 108, 108, 111 ] 參數: [ 111 ] 結果: 5
第 11 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 5 ] 結果: 44
第 12 條: 函數: [Function: push] 調用者: [ 72, 101, 108, 108, 111, 44 ] 參數: [ 44 ] 結果: 6
第 13 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 6 ] 結果: 32
第 14 條: 函數: [Function: push] 調用者: [72, 101, 108, 108,111, 44, 32
] 參數: [ 32 ] 結果: 7
第 15 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 7 ] 結果: 87
第 16 條: 函數: [Function: push] 調用者: [72, 101, 108, 108,111, 44, 32, 87
] 參數: [ 87 ] 結果: 8
第 17 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 8 ] 結果: 111
第 18 條: 函數: [Function: push] 調用者: [72, 101, 108, 108,111, 44, 32, 87,111
] 參數: [ 111 ] 結果: 9
第 19 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 9 ] 結果: 114
第 20 條: 函數: [Function: push] 調用者: [72, 101, 108, 108,111, 44, 32, 87,111, 114
] 參數: [ 114 ] 結果: 10
第 21 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 10 ] 結果: 108
第 22 條: 函數: [Function: push] 調用者: [72, 101, 108, 108,111, 44, 32, 87,111, 114, 108
] 參數: [ 108 ] 結果: 11
第 23 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 11 ] 結果: 100
第 24 條: 函數: [Function: push] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100
] 參數: [ 100 ] 結果: 12
第 25 條: 函數: [Function: charCodeAt] 調用者: Hello, World! 參數: [ 12 ] 結果: 33
第 26 條: 函數: [Function: push] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 33 ] 結果: 13
第 27 條: 0 + 3 ====> 3
第 28 條: 函數: [Function: slice] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 0, 3 ] 結果: [ 72, 101, 108 ]
第 29 條: 函數: [Function: slice] 調用者: [ 72, 101, 108 ] 參數: [] 結果: [ 72, 101, 108 ]
第 30 條: 3 - 3 ====> 0
第 31 條: 72 & 255 ====> 72
第 32 條: 101 & 255 ====> 101
第 33 條: 108 & 255 ====> 108
第 34 條: 72 << 16 ====> 4718592
第 35 條: 101 << 8 ====> 25856
第 36 條: 4718592 | 25856 ====> 4744448
第 37 條: 4744448 | 108 ====> 4744556
第 38 條: 4744556 >> 18 ====> 18
第 39 條: 18 & 63 ====> 18
第 40 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 18 ] 結果: w
第 41 條: 4744556 >> 12 ====> 1158
第 42 條: 1158 & 63 ====> 6
第 43 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 6 ] 結果: 4
第 44 條: 4744556 >> 6 ====> 74133
第 45 條: 74133 & 63 ====> 21
第 46 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 21 ] 結果: X
第 47 條: 4744556 & 63 ====> 44
第 48 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 44 ] 結果: u
第 49 條: 函數: [Function: apply] 調用者: [Function: t4] 參數: [ null, [ 72, 101, 108 ] ] 結果: [ 'w', '4', 'X', 'u' ]
第 50 條: + w ====> w
第 51 條: w + 4 ====> w4
第 52 條: w4 + X ====> w4X
第 53 條: w4X + u ====> w4Xu
第 54 條: 0 + 3 ====> 3
第 55 條: 3 + 3 ====> 6
第 56 條: 函數: [Function: slice] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 3, 6 ] 結果: [ 108, 111, 44 ]
第 57 條: 函數: [Function: slice] 調用者: [ 108, 111, 44 ] 參數: [] 結果: [ 108, 111, 44 ]
第 58 條: 3 - 3 ====> 0
第 59 條: 108 & 255 ====> 108
第 60 條: 111 & 255 ====> 111
第 61 條: 44 & 255 ====> 44
第 62 條: 108 << 16 ====> 7077888
第 63 條: 111 << 8 ====> 28416
第 64 條: 7077888 | 28416 ====> 7106304
第 65 條: 7106304 | 44 ====> 7106348
第 66 條: 7106348 >> 18 ====> 27
第 67 條: 27 & 63 ====> 27
第 68 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 27 ] 結果: +
第 69 條: 7106348 >> 12 ====> 1734
第 70 條: 1734 & 63 ====> 6
第 71 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 6 ] 結果: 4
第 72 條: 7106348 >> 6 ====> 111036
第 73 條: 111036 & 63 ====> 60
第 74 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 60 ] 結果: S
第 75 條: 7106348 & 63 ====> 44
第 76 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 44 ] 結果: u
第 77 條: 函數: [Function: apply] 調用者: [Function: t4] 參數: [ null, [ 108, 111, 44 ] ] 結果: [ '+', '4', 'S', 'u' ]
第 78 條: w4Xu + + ====> w4Xu+
第 79 條: w4Xu+ + 4 ====> w4Xu+4
第 80 條: w4Xu+4 + S ====> w4Xu+4S
第 81 條: w4Xu+4S + u ====> w4Xu+4Su
第 82 條: 3 + 3 ====> 6
第 83 條: 6 + 3 ====> 9
第 84 條: 函數: [Function: slice] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 6, 9 ] 結果: [ 32, 87, 111 ]
第 85 條: 函數: [Function: slice] 調用者: [ 32, 87, 111 ] 參數: [] 結果: [ 32, 87, 111 ]
第 86 條: 3 - 3 ====> 0
第 87 條: 32 & 255 ====> 32
第 88 條: 87 & 255 ====> 87
第 89 條: 111 & 255 ====> 111
第 90 條: 32 << 16 ====> 2097152
第 91 條: 87 << 8 ====> 22272
第 92 條: 2097152 | 22272 ====> 2119424
第 93 條: 2119424 | 111 ====> 2119535
第 94 條: 2119535 >> 18 ====> 8
第 95 條: 8 & 63 ====> 8
第 96 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 8 ] 結果: K
第 97 條: 2119535 >> 12 ====> 517
第 98 條: 517 & 63 ====> 5
第 99 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 5 ] 結果: h
第 100 條: 2119535 >> 6 ====> 33117
第 101 條: 33117 & 63 ====> 29
第 102 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 29 ] 結果: U
第 103 條: 2119535 & 63 ====> 47
第 104 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 47 ] 結果: F
第 105 條: 函數: [Function: apply] 調用者: [Function: t4] 參數: [ null, [ 32, 87, 111 ] ] 結果: [ 'K', 'h', 'U', 'F' ]
第 106 條: w4Xu+4Su + K ====> w4Xu+4SuK
第 107 條: w4Xu+4SuK + h ====> w4Xu+4SuKh
第 108 條: w4Xu+4SuKh + U ====> w4Xu+4SuKhU
第 109 條: w4Xu+4SuKhU + F ====> w4Xu+4SuKhUF
第 110 條: 6 + 3 ====> 9
第 111 條: 9 + 3 ====> 12
第 112 條: 函數: [Function: slice] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 9, 12 ] 結果: [ 114, 108, 100 ]
第 113 條: 函數: [Function: slice] 調用者: [ 114, 108, 100 ] 參數: [] 結果: [ 114, 108, 100 ]
第 114 條: 3 - 3 ====> 0
第 115 條: 114 & 255 ====> 114
第 116 條: 108 & 255 ====> 108
第 117 條: 100 & 255 ====> 100
第 118 條: 114 << 16 ====> 7471104
第 119 條: 108 << 8 ====> 27648
第 120 條: 7471104 | 27648 ====> 7498752
第 121 條: 7498752 | 100 ====> 7498852
第 122 條: 7498852 >> 18 ====> 28
第 123 條: 28 & 63 ====> 28
第 124 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 28 ] 結果: W
第 125 條: 7498852 >> 12 ====> 1830
第 126 條: 1830 & 63 ====> 38
第 127 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 38 ] 結果: o
第 128 條: 7498852 >> 6 ====> 117169
第 129 條: 117169 & 63 ====> 49
第 130 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 49 ] 結果: J
第 131 條: 7498852 & 63 ====> 36
第 132 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 36 ] 結果: L
第 133 條: 函數: [Function: apply] 調用者: [Function: t4] 參數: [ null, [ 114, 108, 100 ] ] 結果: [ 'W', 'o', 'J', 'L' ]
第 134 條: w4Xu+4SuKhUF + W ====> w4Xu+4SuKhUFW
第 135 條: w4Xu+4SuKhUFW + o ====> w4Xu+4SuKhUFWo
第 136 條: w4Xu+4SuKhUFWo + J ====> w4Xu+4SuKhUFWoJ
第 137 條: w4Xu+4SuKhUFWoJ + L ====> w4Xu+4SuKhUFWoJL
第 138 條: 9 + 3 ====> 12
第 139 條: 12 + 3 ====> 15
第 140 條: 函數: [Function: slice] 調用者: [72, 101, 108, 108, 111,44, 32, 87, 111, 114,108, 100, 33
] 參數: [ 12, 15 ] 結果: [ 33 ]
第 141 條: 函數: [Function: slice] 調用者: [ 33 ] 參數: [] 結果: [ 33 ]
第 142 條: 3 - 1 ====> 2
第 143 條: 函數: [Function: push] 調用者: [ 33, 0 ] 參數: [ 0 ] 結果: 2
第 144 條: 3 - 1 ====> 2
第 145 條: 函數: [Function: push] 調用者: [ 33, 0, 0 ] 參數: [ 0 ] 結果: 3
第 146 條: 3 - 1 ====> 2
第 147 條: 33 & 255 ====> 33
第 148 條: 0 & 255 ====> 0
第 149 條: 0 & 255 ====> 0
第 150 條: 33 << 16 ====> 2162688
第 151 條: 0 << 8 ====> 0
第 152 條: 2162688 | 0 ====> 2162688
第 153 條: 2162688 | 0 ====> 2162688
第 154 條: 2162688 >> 18 ====> 8
第 155 條: 8 & 63 ====> 8
第 156 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 8 ] 結果: K
第 157 條: 2162688 >> 12 ====> 528
第 158 條: 528 & 63 ====> 16
第 159 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 16 ] 結果: f
第 160 條: 2162688 >> 6 ====> 33792
第 161 條: 33792 & 63 ====> 0
第 162 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 0 ] 結果: D
第 163 條: 2162688 & 63 ====> 0
第 164 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 0 ] 結果: D
第 165 條: 函數: [Function: apply] 調用者: [Function: t4] 參數: [ null, [ 33, 0, 0 ] ] 結果: [ 'K', 'f', 'D', 'D' ]
第 166 條: w4Xu+4SuKhUFWoJL + K ====> w4Xu+4SuKhUFWoJLK
第 167 條: w4Xu+4SuKhUFWoJLK + f ====> w4Xu+4SuKhUFWoJLKf
第 168 條: w4Xu+4SuKhUFWoJLKf + = ====> w4Xu+4SuKhUFWoJLKf=
第 169 條: w4Xu+4SuKhUFWoJLKf= + = ====> w4Xu+4SuKhUFWoJLKf==
第 170 條: 12 + 3 ====> 15
第 171 條: 函數: [Function: t4] 調用者: Object [global] {'@faceless': <ref *1> Object [global] {global: [Circular *1],queueMicrotask: [Function: queueMicrotask],clearImmediate: [Function: clearImmediate],setImmediate: [Function: setImmediate] {[Symbol(nodejs.util.promisify.custom)]: [Getter]},structuredClone: [Getter/Setter],clearInterval: [Function: clearInterval],clearTimeout: [Function: clearTimeout],setInterval: [Function: setInterval],setTimeout: [Function: setTimeout] {[Symbol(nodejs.util.promisify.custom)]: [Getter]},atob: [Getter/Setter],btoa: [Getter/Setter],performance: [Getter/Setter],fetch: [AsyncFunction: fetch],window: [Circular *1]},arguments: undefined,optimizedBase64Encode: [Function: t4]
} 參數: [ 'Hello, World!' ] 結果: w4Xu+4SuKhUFWoJLKf==
w4Xu+4SuKhUFWoJLKf==
實戰過程中,當看到 w4Xu+4SuKhUFWoJLKf==
這個字符后,看起來像是 base64 編碼,所以通過 atob
嘗試解碼,得到 ?\x85??\x84?*\x15\x05Z\x82K)
,顯然不對。
那分析日志吧!
第 29 條: 函數: [Function: slice] 調用者: [ 72, 101, 108 ] 參數: [] 結果: [ 72, 101, 108 ]
第 30 條: 3 - 3 ====> 0
第 31 條: 72 & 255 ====> 72
第 32 條: 101 & 255 ====> 101
第 33 條: 108 & 255 ====> 108
第 34 條: 72 << 16 ====> 4718592
第 35 條: 101 << 8 ====> 25856
第 36 條: 4718592 | 25856 ====> 4744448
第 37 條: 4744448 | 108 ====> 4744556
第 38 條: 4744556 >> 18 ====> 18
第 39 條: 18 & 63 ====> 18
第 40 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 18 ] 結果: w
第 41 條: 4744556 >> 12 ====> 1158
第 42 條: 1158 & 63 ====> 6
第 43 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 6 ] 結果: 4
第 44 條: 4744556 >> 6 ====> 74133
第 45 條: 74133 & 63 ====> 21
第 46 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 21 ] 結果: X
第 47 條: 4744556 & 63 ====> 44
第 48 條: 函數: [Function: charAt] 調用者: Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe= 參數: [ 44 ] 結果: u
看到這段日志,有什么發現嗎?
上篇文章講過,如果是 3
個字節值通過位運算得到一個 4
個字節值,即:72, 101, 108
===>
18, 6, 21, 44
所以,我們判斷,它大概是 標準base64
或者變種 base64
算法,由于我們驗證過,它不是標準base64
,所以它應該是變種base64
。
那么,既然知道它大概是變種base64
,那我們還需要一步一步去日志還原嗎?
答案肯定不需要的。
我們只需要拿著 base64
標準算法模板去改就行。俗稱:套模板
,大大節省我們的時間,而且也不容易出錯!
標準 base64
算法模版:https://t.zsxq.com/sphu5
首先控制入參一樣,即:Hello, World!
。
分析日志發現,首先它把標準的 base64
算法,碼表由 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
變成了 Dkdpgh4ZKsQB80/Mfvw36XI1R25+WUAlEi7NLboqYTOPuzmFjJnryx9HVGcaStCe=
。
其次是:
第 31 條: 72 & 255 ====> 72
第 32 條: 101 & 255 ====> 101
第 33 條: 108 & 255 ====> 108
每次位運算之前,都進行了 & 255
操作。
所以我們只需要微調代碼,如下:
function processGroup(a, b, c) {a = a & 255; // 追加內容b = b & 255; // 追加內容c = c & 255; // 追加內容var combined = (a << 16) | (b << 8) | c;return [base64Chars.charAt((combined >> 18) & 63),base64Chars.charAt((combined >> 12) & 63),base64Chars.charAt((combined >> 6) & 63),base64Chars.charAt(combined & 63)];
}
最后,我們再驗證一下,發現結果也是 w4Xu+4SuKhUFWoJLKf==
,和日志結果一致,說明我們還原成功!
完整代碼:https://t.zsxq.com/ROTc7
下一篇文章我們分析 RC4
jsvmp算法,學習它的日志特點。