从一道CTF题学习python字节码到源码逆向 - nLesxw - 博客园
前文
先了解python的字节码结构,如下:
源码行号 | 指令在函数中的偏移 | 指令符号 | 指令参数 | 实际参数值
第一个为源码行号,从上述题目中我们可以了解到,本题只有5行源码,但是只给了从第3行开始的字节码
第二个指令在函数中的偏移和第四个指令参数对于做题来说不是太过重要,我们在做题的时候主要还是看指令符号和实际参数值
在第3行的字节码指令符号中,有LOAD_CONST ,BUILD_LIST ,STORE_FAST 三种类型
LOAD_CONST 加载常量,通常为整数值
BUILD_LIST 创建一个列表
STORE_FAST 一般用于保存值到局部变量
SETUP_LOOP,用于开始循环,括号里的189表示循环退出点(字节码结构中的第二个指令在函数中偏移)
LOAD_GLOBAL,用来加载全局变量,包括指定函数名,类名,模块名等全局符号
CALL_FUNCTION,用来表示前面加载全局变量的参数个数
GET_ITER,FOR_ITER ,获取参数,开始迭代。这两个不需要过多理解,属于for-in结构特有的,它们通常同时出现。
BINARY_SUBSCR ,读取迭代器中某个下标的值
BINARY_RSHIFT,进行右移运算
BINARY_LSHIFT ,左移运算
BINARY_OR,或运算
BINARY_AND ,与运算
STORE_SUBSCR ,修改迭代器中某个下标的值
JUMP_ABSOLUTE ,回到循环起点
RETURN_VALUE ,函数结束标志
POP_BLOCK,特有的,不用特别理解,对转换回源码不重要,通常和LOAD_CONST 0 (None)一起出现。
例题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
| ezRe (Python 3.9) [Code] File Name: flag_checker.py Object Name: <module> Arg Count: 0 Pos Only Arg Count: 0 KW Only Arg Count: 0 Locals: 0 Stack Size: 7 Flags: 0x00000040 (CO_NOFREE) [Names] 'base64' 'input' 'text' 'key' 'list' 'range' 's' 'j' 'i' 'len' 'data' '_' 'append' 'result' 'zip' 'c' 'k' 'chr' 'ord' 'b64encode' 'encode' 'decode' 'enc' 'print' [Var Names] [Free Vars] [Cell Vars] [Constants] 0 None 'Flag: ' '7e021a7dd49e4bd0837e22129682551b' [Code] File Name: flag_checker.py Object Name: <listcomp> Arg Count: 1 Pos Only Arg Count: 0 KW Only Arg Count: 0 Locals: 2 Stack Size: 4 Flags: 0x00000043 (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE) [Names] 'ord' [Var Names] '.0' 'i' [Free Vars] [Cell Vars] [Constants] 102 [Disassembly] 0 BUILD_LIST 0 2 LOAD_FAST 0: .0 4 FOR_ITER 16 (to 22) 6 STORE_FAST 1: i 8 LOAD_GLOBAL 0: ord 10 LOAD_FAST 1: i 12 CALL_FUNCTION 1 14 LOAD_CONST 0: 102 16 BINARY_XOR 18 LIST_APPEND 2 20 JUMP_ABSOLUTE 4 22 RETURN_VALUE '<listcomp>' 256 50 1 '' 51 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg==' 'yes!' 'try again...' [Disassembly] 0 LOAD_CONST 0: 0 2 LOAD_CONST 1: None 4 JUMP_FORWARD 0 (to 6) 6 JUMP_FORWARD 0 (to 8) 8 JUMP_FORWARD 0 (to 10) 10 IMPORT_NAME 0: base64 12 STORE_NAME 0: base64 14 LOAD_NAME 1: input 16 LOAD_CONST 2: 'Flag: ' 18 CALL_FUNCTION 1 20 STORE_NAME 2: text 22 LOAD_CONST 3: '7e021a7dd49e4bd0837e22129682551b' 24 STORE_NAME 3: key 26 LOAD_CONST 4: <CODE> <listcomp> 28 LOAD_CONST 5: '<listcomp>' 30 MAKE_FUNCTION 0 32 LOAD_NAME 3: key 34 GET_ITER 36 CALL_FUNCTION 1 38 STORE_NAME 3: key 40 LOAD_NAME 4: list 42 LOAD_NAME 5: range 44 LOAD_CONST 6: 256 46 CALL_FUNCTION 1 48 CALL_FUNCTION 1 50 STORE_NAME 6: s 52 LOAD_CONST 0: 0 54 STORE_NAME 7: j 56 LOAD_NAME 5: range 58 LOAD_CONST 6: 256 60 CALL_FUNCTION 1 62 GET_ITER 64 FOR_ITER 62 (to 128) 66 STORE_NAME 8: i 68 LOAD_NAME 7: j 70 LOAD_NAME 6: s 72 LOAD_NAME 8: i 74 BINARY_SUBSCR 76 BINARY_ADD 78 LOAD_NAME 3: key 80 LOAD_NAME 8: i 82 LOAD_NAME 9: len 84 LOAD_NAME 3: key 86 CALL_FUNCTION 1 88 BINARY_MODULO 90 BINARY_SUBSCR 92 BINARY_ADD 94 LOAD_CONST 6: 256 96 BINARY_MODULO 98 STORE_NAME 7: j 100 LOAD_NAME 6: s 102 LOAD_NAME 7: j 104 BINARY_SUBSCR 106 LOAD_NAME 6: s 108 LOAD_NAME 8: i 110 BINARY_SUBSCR 112 ROT_TWO 114 LOAD_NAME 6: s 116 LOAD_NAME 8: i 118 STORE_SUBSCR 120 LOAD_NAME 6: s 122 LOAD_NAME 7: j 124 STORE_SUBSCR 126 JUMP_ABSOLUTE 64 128 LOAD_CONST 0: 0 130 DUP_TOP 132 STORE_NAME 8: i 134 STORE_NAME 7: j 136 BUILD_LIST 0 138 STORE_NAME 10: data 140 LOAD_NAME 5: range 142 LOAD_CONST 7: 50 144 CALL_FUNCTION 1 146 GET_ITER 148 FOR_ITER 88 (to 238) 150 STORE_NAME 11: _ 152 LOAD_NAME 8: i 154 LOAD_CONST 8: 1 156 BINARY_ADD 158 LOAD_CONST 6: 256 160 BINARY_MODULO 162 STORE_NAME 8: i 164 LOAD_NAME 7: j 166 LOAD_NAME 6: s 168 LOAD_NAME 8: i 170 BINARY_SUBSCR 172 BINARY_ADD 174 LOAD_CONST 6: 256 176 BINARY_MODULO 178 STORE_NAME 7: j 180 LOAD_NAME 6: s 182 LOAD_NAME 7: j 184 BINARY_SUBSCR 186 LOAD_NAME 6: s 188 LOAD_NAME 8: i 190 BINARY_SUBSCR 192 ROT_TWO 194 LOAD_NAME 6: s 196 LOAD_NAME 8: i 198 STORE_SUBSCR 200 LOAD_NAME 6: s 202 LOAD_NAME 7: j 204 STORE_SUBSCR 206 LOAD_NAME 10: data 208 LOAD_METHOD 12: append 210 LOAD_NAME 6: s 212 LOAD_NAME 6: s 214 LOAD_NAME 8: i 216 BINARY_SUBSCR 218 LOAD_NAME 6: s 220 LOAD_NAME 7: j 222 BINARY_SUBSCR 224 BINARY_ADD 226 LOAD_CONST 6: 256 228 BINARY_MODULO 230 BINARY_SUBSCR 232 CALL_METHOD 1 234 POP_TOP 236 JUMP_ABSOLUTE 148 238 LOAD_CONST 9: '' 240 STORE_NAME 13: result 242 LOAD_NAME 14: zip 244 LOAD_NAME 2: text 246 LOAD_NAME 10: data 248 CALL_FUNCTION 2 250 GET_ITER 252 FOR_ITER 32 (to 286) 254 UNPACK_SEQUENCE 2 256 STORE_NAME 15: c 258 STORE_NAME 16: k 260 LOAD_NAME 13: result 262 LOAD_NAME 17: chr 264 LOAD_NAME 18: ord 266 LOAD_NAME 15: c 268 CALL_FUNCTION 1 270 LOAD_NAME 16: k 272 BINARY_XOR 274 LOAD_CONST 10: 51 276 BINARY_XOR 278 CALL_FUNCTION 1 280 INPLACE_ADD 282 STORE_NAME 13: result 284 JUMP_ABSOLUTE 252 286 LOAD_NAME 0: base64 288 LOAD_METHOD 19: b64encode 290 LOAD_NAME 13: result 292 LOAD_METHOD 20: encode 294 CALL_METHOD 0 296 CALL_METHOD 1 298 LOAD_METHOD 21: decode 300 CALL_METHOD 0 302 STORE_NAME 22: enc 304 LOAD_NAME 22: enc 306 LOAD_CONST 11: 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg==' 308 COMPARE_OP 2 (==) 310 POP_JUMP_IF_FALSE 324 314 LOAD_NAME 23: print 316 LOAD_CONST 12: 'yes!' 318 CALL_FUNCTION 1 320 POP_TOP 322 JUMP_FORWARD 8 (to 332) 324 LOAD_NAME 23: print 326 LOAD_CONST 13: 'try again...' 328 CALL_FUNCTION 1 330 POP_TOP 332 LOAD_CONST 1: None 334 RETURN_VALUE
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import base64
# 原始密钥和加密的目标输出 key = '7e021a7dd49e4bd0837e22129682551b' encoded_target = 'w53Cj3HDgzTCsSM5wrg6FMKcw58Qw7RZSFLCljRxwrxbwrVdw4AEwqMjw7/DkMKTw4/Cv8Onw4NGw7jDmSdcwq4GGg=='
# 处理密钥 processed_key = [ord(i) ^ 102 for i in key]
# RC4 类似的流密码算法构建 s = list(range(256)) j = 0 for i in range(256): j = (j + s[i] + processed_key[i s[i], s[j] = s[j], s[i]
# 生成数据流 `data` i = j = 0 data = [] for _ in range(50): # 生成50个密钥流字节 i = (i + 1) j = (j + s[i]) s[i], s[j] = s[j], s[i] data.append(s[(s[i] + s[j])
# 解码 `encoded_target` 并转换为原始字符串 target_bytes = base64.b64decode(encoded_target).decode() decoded_text = ''
# 使用 `data` 对 `target_bytes` 进行解码 for c, k in zip(target_bytes, data): decoded_text += chr((ord(c) ^ k) ^ 51)
print("Decoded text (flag):", decoded_text)
|