阿里云CTF2023 字节码跳动 wp (javascript字节码)
本题主要考察javascript字节码的翻译。
V8 是 Google 开发的开源 JavaScript 引擎。 Chrome、Node.js和许多其他应用程序都在使用 V8。
如果只发布V8引擎编译后的字节码,就可以保护源码。
但为了保证大家能够把题目做出来,题目所提供的字节码中有详细的调试信息。
下面这篇文章非常详细,讲了变量声明、条件分支、循环、函数声明、类等最基本语法对应的字节码。
https://tw.coderbridge.com/series/817c07dc8e1c46f2b0a604b3b4e195c1
下面这个链接可以当v8的javascript字节码阅读官方文档来用。
https://github.com/v8/v8/blob/d84e9496d23cf1dc776ae32199d81accfabaafb5/src/interpreter/interpreter-generator.cc
其中,与汇编的区别主要在于acc寄存器。大部分的指令都围绕acc寄存器(比如lda系列指令的目标寄存器为acc,以及大部分的算术运算指令中acc默认为操作数之一),可以认为是具有特殊地位的rax。
另一个主要区别是constant pool,可以认为是把data段拆散了放在函数辖区内(在出题人不主动使用相关编译选项以提供constant pool的情况下,尚不清楚相关机理)。
解题思路
先在题目提供的filechecker_bytecode.txt中搜[generated,从海量函数中找到三个可疑函数:
- main 25264行
- aaa 25334行,constant pool中有一个极为可疑的常量,
3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae - ccc 25505行,
没有调用任何其他函数,返回值为布尔类型,函数过程出现了异或和与运算,以及大量jump。
原文译文对照
main的原文:
[generated bytecode for function: main (0x3711a3bdfd81 <SharedFunctionInfo main>)]
Bytecode length: 69
Parameter count 1
Register count 4
Frame size 32
OSR nesting level: 0
Bytecode Age: 01207 S> 0x3711a3be0a56 @ 0 : 21 00 00 LdaGlobal [0], [0]0x3711a3be0a59 @ 3 : c2 Star1 1215 E> 0x3711a3be0a5a @ 4 : 2d f9 01 02 LdaNamedProperty r1, [1], [2]0x3711a3be0a5e @ 8 : c2 Star1 0x3711a3be0a5f @ 9 : 0d 02 LdaSmi [2]1219 E> 0x3711a3be0a61 @ 11 : 2f f9 04 LdaKeyedProperty r1, [4]0x3711a3be0a64 @ 14 : c3 Star0 1270 S> 0x3711a3be0a65 @ 15 : 96 23 JumpIfToBooleanFalse [35] (0x3711a3be0a88 @ 50)0x3711a3be0a67 @ 17 : 17 03 LdaImmutableCurrentContextSlot [3]0x3711a3be0a69 @ 19 : c2 Star1 1279 E> 0x3711a3be0a6a @ 20 : 61 f9 fa 06 CallUndefinedReceiver1 r1, r0, [6]0x3711a3be0a6e @ 24 : c2 Star1 0x3711a3be0a6f @ 25 : 11 LdaTrue 1286 E> 0x3711a3be0a70 @ 26 : 6a f9 08 TestEqual r1, [8]0x3711a3be0a73 @ 29 : 98 15 JumpIfFalse [21] (0x3711a3be0a88 @ 50)1305 S> 0x3711a3be0a75 @ 31 : 21 02 09 LdaGlobal [2], [9]0x3711a3be0a78 @ 34 : c1 Star2 1313 E> 0x3711a3be0a79 @ 35 : 2d f8 03 0b LdaNamedProperty r2, [3], [11]0x3711a3be0a7d @ 39 : c2 Star1 0x3711a3be0a7e @ 40 : 13 04 LdaConstant [4]0x3711a3be0a80 @ 42 : c0 Star3 1313 E> 0x3711a3be0a81 @ 43 : 5d f9 f8 f7 0d CallProperty1 r1, r2, r3, [13]0x3711a3be0a86 @ 48 : 89 13 Jump [19] (0x3711a3be0a99 @ 67)1349 S> 0x3711a3be0a88 @ 50 : 21 02 09 LdaGlobal [2], [9]0x3711a3be0a8b @ 53 : c1 Star2 1357 E> 0x3711a3be0a8c @ 54 : 2d f8 03 0b LdaNamedProperty r2, [3], [11]0x3711a3be0a90 @ 58 : c2 Star1 0x3711a3be0a91 @ 59 : 13 05 LdaConstant [5]0x3711a3be0a93 @ 61 : c0 Star3 1357 E> 0x3711a3be0a94 @ 62 : 5d f9 f8 f7 0f CallProperty1 r1, r2, r3, [15]0x3711a3be0a99 @ 67 : 0e LdaUndefined 1378 S> 0x3711a3be0a9a @ 68 : a8 Return
Constant pool (size = 6)
0x3711a3be09e1: [FixedArray] in OldSpace- map: 0x2546cb1c12c1 <Map>- length: 60: 0x39ea5930f9e9 <String[7]: #process>1: 0x20e3b754b6d9 <String[4]: #argv>2: 0x26784b2e1619 <String[7]: #console>3: 0x0ef123a28e69 <String[3]: #log>4: 0x3711a3be0961 <String[6]: #Right!>5: 0x3711a3be0979 <String[6]: #Wrong!>
Handler Table (size = 0)
Source Position Table (size = 31)
0x3711a3be0aa1 <ByteArray[31]>
main的指令翻译如下:
acc = global.process
r1 = acc
acc = global.process.argv
r1 = acc
(now r1 = global.process.argv)
acc = 2
acc = r1[acc], 即acc = global.process.argv[2]
r0 = acc
(now r0 = global.process.argv[2])
如果r0的值转为boolean后为false,则跳转到wrong。意思应该是判空。
acc = 某外部函数
r1 = acc
调用r1,参数个数为1,参数放在r0,即调用某外部函数(global.process.argv[2])
r1 = acc
acc = True
acc如果不为true,则跳转到wrong。意思是某外部函数要返回true。
r2 = console
r1 = console.log
r3 = 'Right!'
console.log(r3)
jmp ret
console.log('Wrong!')
ret
可见main函数的关键是这个外部函数。
aaa函数原文:
[generated bytecode for function: aaa (0x3711a3bdfd31 <SharedFunctionInfo aaa>)]
Bytecode length: 91
Parameter count 2
Register count 7
Frame size 56
OSR nesting level: 0
Bytecode Age: 0757 S> 0x3711a3be0cde @ 0 : 21 00 00 LdaGlobal [0], [0]0x3711a3be0ce1 @ 3 : c0 Star3 768 E> 0x3711a3be0ce2 @ 4 : 2d f7 01 02 LdaNamedProperty r3, [1], [2]0x3711a3be0ce6 @ 8 : c0 Star3 0x3711a3be0ce7 @ 9 : 0b f7 Ldar r3757 E> 0x3711a3be0ce9 @ 11 : 68 f7 03 01 04 Construct r3, a0-a0, [4]0x3711a3be0cee @ 16 : c3 Star0 792 S> 0x3711a3be0cef @ 17 : 2d fa 02 06 LdaNamedProperty r0, [2], [6]0x3711a3be0cf3 @ 21 : c0 Star3 0x3711a3be0cf4 @ 22 : 0d 2b LdaSmi [43]799 E> 0x3711a3be0cf6 @ 24 : 6a f7 08 TestEqual r3, [8]0x3711a3be0cf9 @ 27 : 97 04 JumpIfTrue [4] (0x3711a3be0cfd @ 31)806 S> 0x3711a3be0cfb @ 29 : 12 LdaFalse 819 S> 0x3711a3be0cfc @ 30 : a8 Return 835 S> 0x3711a3be0cfd @ 31 : 21 00 00 LdaGlobal [0], [0]0x3711a3be0d00 @ 34 : c0 Star3 846 E> 0x3711a3be0d01 @ 35 : 2d f7 03 09 LdaNamedProperty r3, [3], [9]0x3711a3be0d05 @ 39 : c0 Star3 0x3711a3be0d06 @ 40 : 0d 2b LdaSmi [43]0x3711a3be0d08 @ 42 : bf Star4 0x3711a3be0d09 @ 43 : 0b f7 Ldar r3835 E> 0x3711a3be0d0b @ 45 : 68 f7 f6 01 0b Construct r3, r4-r4, [11]0x3711a3be0d10 @ 50 : c2 Star1 942 S> 0x3711a3be0d11 @ 51 : 21 00 00 LdaGlobal [0], [0]0x3711a3be0d14 @ 54 : bf Star4 949 E> 0x3711a3be0d15 @ 55 : 2d f6 01 02 LdaNamedProperty r4, [1], [2]0x3711a3be0d19 @ 59 : c0 Star3 0x3711a3be0d1a @ 60 : 13 04 LdaConstant [4]0x3711a3be0d1c @ 62 : be Star5 0x3711a3be0d1d @ 63 : 13 05 LdaConstant [5]0x3711a3be0d1f @ 65 : bd Star6 949 E> 0x3711a3be0d20 @ 66 : 5e f7 f6 f5 f4 0d CallProperty2 r3, r4, r5, r6, [13]0x3711a3be0d26 @ 72 : c1 Star2 1056 S> 0x3711a3be0d27 @ 73 : 17 02 LdaImmutableCurrentContextSlot [2]0x3711a3be0d29 @ 75 : c0 Star3 0x3711a3be0d2a @ 76 : 19 fa f6 Mov r0, r40x3711a3be0d2d @ 79 : 19 f9 f5 Mov r1, r50x3711a3be0d30 @ 82 : 19 f8 f4 Mov r2, r61063 E> 0x3711a3be0d33 @ 85 : 5f f7 f6 03 0f CallUndefinedReceiver r3, r4-r6, [15]1087 S> 0x3711a3be0d38 @ 90 : a8 Return
Constant pool (size = 6)
0x3711a3be0c69: [FixedArray] in OldSpace- map: 0x2546cb1c12c1 <Map>- length: 60: 0x3adf78836561 <String[6]: #Buffer>1: 0x2546cb1c4999 <String[4]: #from>2: 0x2546cb1c4d41 <String[6]: #length>3: 0x3adf78836c31 <String[5]: #alloc>4: 0x3711a3be0bb1 <String[86]: #3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae>5: 0x3adf78838741 <String[3]: #hex>
Handler Table (size = 0)
Source Position Table (size = 38)
0x3711a3be0d41 <ByteArray[38]>
aaa函数翻译如下:
r3 = Buffer
r3 = Buffer.from
acc = Buffer.from
r0 = new Buffer.from(a0),猜测此处的a0就是main传进来的用户输入。姑且称为buf。
r3 = buf.length
判断buf.length是否为43,如果不为则直接返回false。
r3 = Buffer
r3 = Buffer.alloc
r4 = 43
acc = Buffer.alloc
r1 = new Buffer.alloc(43)
r4 = Buffer
r3 = Buffer.from
r5 = 3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae
r6 = hex
r2 = Buffer.from(r5, 'hex')
r3 = 外部函数
r4 = r0 (用户输入,长度43)
r5 = r1 (空置buffer,长度43)
r6 = r2(内置常量,长度86)
return 外部函数(r4, r5, r6)
有理由怀疑aaa函数中的外部函数是ccc。
ccc函数原文:
[generated bytecode for function: ccc (0x3711a3bdfce1 <SharedFunctionInfo ccc>)]
Bytecode length: 188
Parameter count 4
Register count 4
Frame size 32
OSR nesting level: 0
Bytecode Age: 0173 S> 0x3711a3be1216 @ 0 : 00 0d aa 00 LdaSmi.Wide [170]0x3711a3be121a @ 4 : c3 Star0 188 S> 0x3711a3be121b @ 5 : 0c LdaZero 189 E> 0x3711a3be121c @ 6 : 23 00 00 StaGlobal [0], [0]194 S> 0x3711a3be121f @ 9 : 21 00 02 LdaGlobal [0], [2]0x3711a3be1222 @ 12 : c1 Star2 0x3711a3be1223 @ 13 : 0d 13 LdaSmi [19]194 E> 0x3711a3be1225 @ 15 : 6c f8 04 TestLessThan r2, [4]0x3711a3be1228 @ 18 : 98 2a JumpIfFalse [42] (0x3711a3be1252 @ 60)274 S> 0x3711a3be122a @ 20 : 21 00 02 LdaGlobal [0], [2]273 E> 0x3711a3be122d @ 23 : 2f 03 06 LdaKeyedProperty a0, [6]265 E> 0x3711a3be1230 @ 26 : 38 fa 08 Add r0, [8]277 E> 0x3711a3be1233 @ 29 : 44 33 09 AddSmi [51], [9]285 E> 0x3711a3be1236 @ 32 : 00 4c ff 00 05 00 BitwiseAndSmi.Wide [255], [5]0x3711a3be123c @ 38 : c3 Star0 305 S> 0x3711a3be123d @ 39 : 21 00 02 LdaGlobal [0], [2]0x3711a3be1240 @ 42 : c0 Star3 0x3711a3be1241 @ 43 : 0b fa Ldar r0308 E> 0x3711a3be1243 @ 45 : 34 04 f7 0a StaKeyedProperty a1, r3, [10]200 S> 0x3711a3be1247 @ 49 : 21 00 02 LdaGlobal [0], [2]0x3711a3be124a @ 52 : 50 0c Inc [12]200 E> 0x3711a3be124c @ 54 : 23 00 00 StaGlobal [0], [0]183 E> 0x3711a3be124f @ 57 : 88 30 00 JumpLoop [48], [0] (0x3711a3be121f @ 9)337 S> 0x3711a3be1252 @ 60 : 0d 55 LdaSmi [85]0x3711a3be1254 @ 62 : c2 Star1 352 S> 0x3711a3be1255 @ 63 : 0d 13 LdaSmi [19]353 E> 0x3711a3be1257 @ 65 : 23 00 00 StaGlobal [0], [0]359 S> 0x3711a3be125a @ 68 : 21 00 02 LdaGlobal [0], [2]0x3711a3be125d @ 71 : c1 Star2 0x3711a3be125e @ 72 : 0d 2b LdaSmi [43]359 E> 0x3711a3be1260 @ 74 : 6c f8 0d TestLessThan r2, [13]0x3711a3be1263 @ 77 : 98 34 JumpIfFalse [52] (0x3711a3be1297 @ 129)383 S> 0x3711a3be1265 @ 79 : 21 00 02 LdaGlobal [0], [2]0x3711a3be1268 @ 82 : c0 Star3 403 E> 0x3711a3be1269 @ 83 : 21 00 02 LdaGlobal [0], [2]402 E> 0x3711a3be126c @ 86 : 2f 03 10 LdaKeyedProperty a0, [16]394 E> 0x3711a3be126f @ 89 : 38 f9 0f Add r1, [15]407 E> 0x3711a3be1272 @ 92 : 00 4c ff 00 0e 00 BitwiseAndSmi.Wide [255], [14]386 E> 0x3711a3be1278 @ 98 : 34 04 f7 12 StaKeyedProperty a1, r3, [18]442 S> 0x3711a3be127c @ 102 : 21 00 02 LdaGlobal [0], [2]441 E> 0x3711a3be127f @ 105 : 2f 04 16 LdaKeyedProperty a1, [22]436 E> 0x3711a3be1282 @ 108 : 3f f9 15 BitwiseXor r1, [21]446 E> 0x3711a3be1285 @ 111 : 00 4c ff 00 14 00 BitwiseAndSmi.Wide [255], [20]0x3711a3be128b @ 117 : c2 Star1 365 S> 0x3711a3be128c @ 118 : 21 00 02 LdaGlobal [0], [2]0x3711a3be128f @ 121 : 50 18 Inc [24]365 E> 0x3711a3be1291 @ 123 : 23 00 00 StaGlobal [0], [0]347 E> 0x3711a3be1294 @ 126 : 88 3a 00 JumpLoop [58], [0] (0x3711a3be125a @ 68)531 S> 0x3711a3be1297 @ 129 : 00 0d 9f 00 LdaSmi.Wide [159]540 E> 0x3711a3be129b @ 133 : 6a f9 19 TestEqual r1, [25]0x3711a3be129e @ 136 : 98 32 JumpIfFalse [50] (0x3711a3be12d0 @ 186)563 S> 0x3711a3be12a0 @ 138 : 0c LdaZero 564 E> 0x3711a3be12a1 @ 139 : 23 00 00 StaGlobal [0], [0]569 S> 0x3711a3be12a4 @ 142 : 21 00 02 LdaGlobal [0], [2]0x3711a3be12a7 @ 145 : c1 Star2 0x3711a3be12a8 @ 146 : 0d 2b LdaSmi [43]569 E> 0x3711a3be12aa @ 148 : 6c f8 1a TestLessThan r2, [26]0x3711a3be12ad @ 151 : 98 21 JumpIfFalse [33] (0x3711a3be12ce @ 184)604 S> 0x3711a3be12af @ 153 : 21 00 02 LdaGlobal [0], [2]603 E> 0x3711a3be12b2 @ 156 : 2f 05 1b LdaKeyedProperty a2, [27]0x3711a3be12b5 @ 159 : c1 Star2 614 E> 0x3711a3be12b6 @ 160 : 21 00 02 LdaGlobal [0], [2]613 E> 0x3711a3be12b9 @ 163 : 2f 04 1d LdaKeyedProperty a1, [29]607 E> 0x3711a3be12bc @ 166 : 6a f8 1f TestEqual r2, [31]0x3711a3be12bf @ 169 : 97 04 JumpIfTrue [4] (0x3711a3be12c3 @ 173)636 S> 0x3711a3be12c1 @ 171 : 12 LdaFalse 649 S> 0x3711a3be12c2 @ 172 : a8 Return 575 S> 0x3711a3be12c3 @ 173 : 21 00 02 LdaGlobal [0], [2]0x3711a3be12c6 @ 176 : 50 20 Inc [32]575 E> 0x3711a3be12c8 @ 178 : 23 00 00 StaGlobal [0], [0]558 E> 0x3711a3be12cb @ 181 : 88 27 00 JumpLoop [39], [0] (0x3711a3be12a4 @ 142)682 S> 0x3711a3be12ce @ 184 : 11 LdaTrue 694 S> 0x3711a3be12cf @ 185 : a8 Return 705 S> 0x3711a3be12d0 @ 186 : 12 LdaFalse 718 S> 0x3711a3be12d1 @ 187 : a8 Return
Constant pool (size = 1)
0x3711a3be11c9: [FixedArray] in OldSpace- map: 0x2546cb1c12c1 <Map>- length: 10: 0x2546cb1c9159 <String[1]: #i>
Handler Table (size = 0)
Source Position Table (size = 119)
0x3711a3be12d9 <ByteArray[119]>
ccc函数翻译:
r0 = 170
i = 0
{循环体开始
r2 = i
判断r2 < 19
acc = a0[i]
acc += r0
acc += 51
acc &= 0xff
r0 = acc
r3 = i
acc = r0
a1[r3] = acc,即a1[i] = r0
i += 1
}循环体结束label_19轮循环过后:
r1 = 85
i = 19
{循环体开始
r2 = i
判断r2 < 43
r3 = i
acc = a0[i]
acc += r1
acc &= 0xff
a1[r3] = acc,即a1[i] = acc
acc = a1[i]
acc ^= r1
acc &= 0xff
r1 = acc
i += 1
}循环体结束label_43轮循环过后:
判断r1 == 159,如果不等,则return false
i = 0
{循环体开始
r2 = i
判断r2 < 43,如果超过,则return true
r2 = a2[i]
acc = a1[i]
判断是否相等,不相等则return false
i += 1
}循环体结束
解题脚本
mid = '3edd7925cd6e04ab44f25bef57bc53bd20b74b8c11f893090fdcdfddad0709100100fe6a9230333234fbae'
# print(len(mid))flag = ''
r0 = 170
for i in range(19):dst = int(mid[2 * i:2 * i + 2], 16)# print('dst:', dst, hex(dst))for ch in range(128):acc = chacc += r0acc += 51acc &= 0xff# print(ch, hex(ch))if acc == dst:flag += chr(ch)r0 = accbreak
print(r0)
print(flag)r1 = 85
for i in range(19, 43):dst = int(mid[2 * i:2 * i + 2], 16)# print('dst:', dst, hex(dst))for ch in range(128):acc = chacc += r1acc &= 0xffif acc == dst:flag += chr(ch)r1 = ((acc ^ r1) & 0xff)break
print(r1)
print(flag)