# BKSEC TTV WU REVERSE ## I. jslock * Chall cho ta một đường link web ![image](https://hackmd.io/_uploads/rJ__32Bp6.png) * Web đơn giản là check pass bằng cách nhập mật khẩu nằm trong cận [0, 9]. Để hiểu rõ hơn về phương thức mã hóa của web thì ta xem source code của web * Để tiện phân tích thì mình sẽ dùng Edge Browser để mở Devtools lên ![image](https://hackmd.io/_uploads/SJ8-62Bpp.png) * Ở lock.js ta đã thấy hàm check pass của web * Đơn giản thì chương trình lấy dữ liệu input sau đó biến đổi thành một mảng sử dụng `Array.from(dials)`. * Tiếp theo tạo ra một mảng mới gọi là `decodedPassword`, trong đó mỗi phần tử là tổng của ba số liên tiếp trong mảng `enteredPassword`. * Hàm so sánh mảng `decodedPassword` với mảng các hằng số `correctPassword` nếu giống nhau thì ret true * Việc xử lý cũng khá đơn giản, mình sẽ brute force để tìm key ``` def find_password(): for digit_0 in range(10): for digit_1 in range(10): for digit_2 in range(10): for digit_3 in range(10): for digit_4 in range(10): entered_password = [digit_0, digit_1, digit_2, digit_3, digit_4] if (entered_password[0] + entered_password[1] + entered_password[2] == 10 and entered_password[1] + entered_password[2] + entered_password[3] == 12 and entered_password[2] + entered_password[3] + entered_password[4] == 10 and entered_password[3] + entered_password[4] + entered_password[0] == 11 and entered_password[4] + entered_password[0] + entered_password[1] == 11): return entered_password password = find_password() print("Correct password:", password) ``` * Key đúng sẽ là ![image](https://hackmd.io/_uploads/SJ-2V1IpT.png) * Nhập vào và lấy flag ![Screenshot 2024-03-06 194319](https://hackmd.io/_uploads/r1axSk8ap.png) > BKSEC{MzMzNTMyMzUzMw==} ## II. jslockpremium ![image](https://hackmd.io/_uploads/Hk6_S1Lp6.png) * Đến bài này cũng là phân tích source web nhưng đã được nâng cấp đôi chút khi ta phải nhập tận 10 key và cận rất lớn từ [0, 999]. * Vì vậy phương pháp brute force thông thường sẽ không khả thi, thay vào đó đề có gợi ý cho chúng ta về `Z3` * Z3 là một hệ thống hỗ trợ chứng minh tự động và giải quyết các bài toán toán học. Được phát triển bởi `Microsoft Research`, Z3 được sử dụng rộng rãi trong nhiều lĩnh vực như kiểm tra phần mềm, tự động hóa, và lý thuyết đồng thời. Link cài đặt cũng như cách dùng thì [ở đây](https://youtu.be/rTVNvZx83IE?si=7M6qf9u8b4IEkJ4h) * Quay trở lại với bài thì lần này có vẻ như hàm check pass ngoằn nghoèo hơn tí ![image](https://hackmd.io/_uploads/HkBIWRIap.png) * Tổng cộng có 12 key cần tìm và cũng như mảng const Password[11] * Mình sẽ dùng Z3 để solve ``` from z3 import * correctPassword = [43490, 822, 2243, 1105, 4652, 429, 1187, 3901, 395, 1067, 622] enteredPassword = [Int('enteredPassword_' + str(i)) for i in range(12)] solver = Solver() solver.add(enteredPassword[0] * enteredPassword[1] - 2 * enteredPassword[2] == correctPassword[0]) solver.add(enteredPassword[1] + 5 * enteredPassword[2] - 3 * enteredPassword[3] == correctPassword[1]) solver.add(enteredPassword[2] + enteredPassword[3] * enteredPassword[4] - enteredPassword[0] == correctPassword[2]) solver.add(enteredPassword[5] + 2 * enteredPassword[4] + enteredPassword[2] == correctPassword[3]) solver.add(20 * enteredPassword[1] - 2 * enteredPassword[2] - enteredPassword[4] == correctPassword[4]) solver.add(enteredPassword[2] + enteredPassword[5] - 2 * enteredPassword[1] == correctPassword[5]) solver.add(2 * enteredPassword[1] + enteredPassword[2] + 3 * enteredPassword[6] + 4 * enteredPassword[7] == correctPassword[6]) solver.add(5 * enteredPassword[5] - enteredPassword[7] - enteredPassword[8] + 2 * enteredPassword[9] == correctPassword[7]) solver.add(3 * enteredPassword[8] + 2 * enteredPassword[9] + 4 * enteredPassword[10] - 3 * enteredPassword[6] == correctPassword[8]) solver.add(9 * enteredPassword[11] + 2 * enteredPassword[10] - enteredPassword[8] == correctPassword[9]) solver.add(4 * enteredPassword[7] - enteredPassword[8] + enteredPassword[10] == correctPassword[10]) for i in range(12): solver.add(And(enteredPassword[i] >= 0, enteredPassword[i] <= 999)) result = solver.check() if result == sat: model = solver.model() enteredPasswordValues = [] for i in range(11): enteredPasswordValues.append(model[enteredPassword[i]].as_long()) print( enteredPasswordValues) else: print('Nope') ``` * Mấu chốt ở đây mình sẽ k tìm key thứ 12 bởi vì nhìn kỹ ở key này sẽ thấy ` enteredPassword[3] ** enteredPassword[8] + 3 * enteredPassword[11] - 2 * enteredPassword[7] + enteredPassword[6] + enteredPassword[5] + enteredPassword[10]`. Key này có mũ nên việc dùng z3 với mũ sẽ k khả thi(Có lẽ là vậy vì mình chạy k được:v) * Cách giải quyết thì mình sẽ bỏ qua key này và chạy 11 key đầu tiên, việc tiếp theo là dùng 11 key tìm được và thay vào tìm key thứ 12, một điều lưu ý nữa là cần đặt cận [0, 999] nếu không kết quả sẽ sai vì ra âm hoặc out cận ![image](https://hackmd.io/_uploads/SJ4IGRUpp.png) * 11 Key đầu và thế vào sẽ được key thứ 12 là `99` * Nhập vào web và lấy flag ![Screenshot 2024-03-07 123132](https://hackmd.io/_uploads/H1a_zA86T.png) ## III. BabyIDA1 * Load file vào IDA ![image](https://hackmd.io/_uploads/SyxgQ0IT6.png) * Ta thấy luôn hai phần của flag, phần đầu có vẻ như là chuẩn nhưng phần thứ 2 thì hình như đã bị đảo lộn thứ tự, bây giờ mình sẽ phân tích kỹ chương trình hơn * khởi tạo `v13` và `v12` chứa các phần của key. v13 chứa phần đầu của khóa và v12 chứa phần cuối. * Tiếp theo, chương trình yêu cầu người dùng nhập một chuỗi vào biến `v10`, rồi so sánh chuỗi đó với chuỗi `v13`. Nếu chuỗi nhập vào giống với `v13`, chương trình tiếp tục, chương trình yêu cầu người dùng nhập một chuỗi khác vào biến `v9` sau đó so với `v12` đã bị splicing với -1 * Đến đây có lẻ đã quá rõ ràng ta chỉ cần splice lại `v12` sau đó cộng với `v13` là xong ``` s = "})12387642t234g789_UaD_t3h_@Uhc}eheh_n0ul_1Uv_Al_" reversed_string = s[::-1] print(reversed_string) ``` ![image](https://hackmd.io/_uploads/Hy119086T.png) > BKSEC{rev3r5e_r@t_lA_vU1_lu0n_hehe}chU@_h3t_DaU_987g432t24678321)} ## IV. Babylua * Đề cho ta 2 file .lua, mở lần lượt để xem thử ![image](https://hackmd.io/_uploads/HJLN-yvTT.png) * File main khá đơn giản dùng để check input là flag ta nhập vào nhưng hình như vẫn còn thiếu điều kiện * file flag.lua là file binary nên mình decompile ra để lấy code ![Screenshot from 2024-03-07 01-40-52](https://hackmd.io/_uploads/SyD2byDT6.png) * Hàm check nhận hai đối số là chuỗi đầu vào (inp) và khóa (key) * Vòng lặp đầu tiên chạy qua từng ký tự trong chuỗi đầu vào (inp). Mỗi ký tự được XOR với ký tự tương ứng trong key, sau đó được cộng thêm với giá trị của ký tự trước đó trong chuỗi đầu vào * Vòng lặp thứ hai so sánh từng phần tử trong mảng encoded với từng phần tử trong mảng arr. Nếu các giá trị tại vị trí tương ứng trong hai mảng giống nhau, biến result được tăng lên một * Kết hợp với file flag.lua đã decompile cùng với key `ThisIsAFlag` ở main.lua ta có thể brute để tìm flag ``` arr = [22, 101, 133, 137, 79, 75, 166, 157, 189, 57, 172, 155, 144, 91, 137, 222, 52, 144, 211, 101, 114, 116, 121, 76, 154, 168, 83, 94] cmp_str = 'ThisIsAFlag' charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+-=<>,.?/{}[]\|~:;"\'' flag = " " test = 0 for i in range(len(arr)): for char in charset: temp = (ord(char) ^ ord(cmp_str[i % len(cmp_str)])) + test if temp == arr[i]: flag += char test = ord(char) break print(flag) ``` ![image](https://hackmd.io/_uploads/HJ7REyvpa.png) > BKSEC{ju$t_h@rd3r_2_re@d_:P} ## V. BabyRust * Đề cho ta một file thực thi `Rust0` ![Screenshot from 2024-03-07 01-57-14](https://hackmd.io/_uploads/B1pOBJvap.png) * Vẫn mô tuýp cũ là bắt ta nhập pass để check, tiến hành phân tích bằng IDA ![image](https://hackmd.io/_uploads/H1mMq1w6T.png) * Ok, điển hình của Rust khi disas ra thì hàm main sẽ được wrap khá sâu trong các hàm khác ở đây hàm main được wrap trong `std::rt::lang_start::hf8b53e431764034c()` mà ở trong cũng sẽ được wrap bởi một hàm khác là `std::rt::lang_start_internal::h76f3e81e6b8f13f9()` đấy sẽ là main thật của chúng ta ``` void __cdecl std::rt::lang_start_internal::h76f3e81e6b8f13f9() { __int64 v0; // rdi __int64 v1; // rsi char v2; // r8 unsigned __int64 v3; // r12 char v4; // bp unsigned int v5; // eax int v6; // ecx void (*v7)(int); // rsi __int64 v8; // rax __int64 v9; // rax unsigned __int64 v10; // rbx __int64 v11; // r13 pthread_t v12; // rax void (*v13)(int); // r14 unsigned __int64 v14; // r12 unsigned __int64 v15; // rdx unsigned __int64 v16; // rbx __int64 v17; // rax __int64 v18; // rsi void *v19[2]; // [rsp+0h] [rbp-158h] BYREF size_t stacksize[4]; // [rsp+10h] [rbp-148h] BYREF __int64 v21; // [rsp+30h] [rbp-128h] BYREF char **v22; // [rsp+40h] [rbp-118h] __int64 v23; // [rsp+48h] [rbp-110h] size_t *v24; // [rsp+50h] [rbp-108h] __int64 v25; // [rsp+58h] [rbp-100h] struct sigaction fds; // [rsp+60h] [rbp-F8h] BYREF __int64 v27[2]; // [rsp+100h] [rbp-58h] BYREF __int64 v28; // [rsp+110h] [rbp-48h] __int64 v29; // [rsp+118h] [rbp-40h] size_t *v30; // [rsp+120h] [rbp-38h] BYREF v4 = v2; v29 = v1; v28 = v0; fds.sa_handler = 0LL; *(_OWORD *)fds.sa_mask.__val = xmmword_41B70; do { if ( poll((struct pollfd *)&fds, 3uLL, 0) != 0xFFFFFFFF ) { if ( (BYTE6(fds.sa_sigaction) & 0x20) != 0 && open64(szd, 2, 0LL) == 0xFFFFFFFF || (fds.sa_mask.__val[0] & 0x20000000000000LL) != 0 && open64(szd, 2, 0LL) == 0xFFFFFFFF ) { goto LABEL_xE2AA; } if ( (fds.sa_mask.__val[1] & 0x20000000000000LL) == 0 ) { goto LABEL_xDFDB; } LABEL_xDFBC: if ( open64(szd, 2, 0LL) != 0xFFFFFFFF ) { goto LABEL_xDFDB; } LABEL_xE2AA: abort(); } v5 = *_errno_location(); } while ( v5 == 4 ); if ( v5 > 0x16 ) { goto LABEL_xE2AA; } v6 = 0x401800; if ( !_bittest(&v6, v5) || fcntl(0, 1) == 0xFFFFFFFF && *_errno_location() == 9 && open64(szd, 2, 0LL) == 0xFFFFFFFF ) { goto LABEL_xE2AA; } if ( fcntl(1, 1) == 0xFFFFFFFF && *_errno_location() == 9 && open64(szd, 2, 0LL) == 0xFFFFFFFF ) { goto LABEL_xE2AA; } if ( fcntl(2, 1) == 0xFFFFFFFF && *_errno_location() == 9 ) { goto LABEL_xDFBC; } LABEL_xDFDB: v7 = (void (*)(int))(&dword_0 + 1); switch ( v4 ) { case 0: goto LABEL_xE010; case 1: std::sys::unix::UNIX_SIGPIPE_ATTR_SPECIFIED::h1ae45800e8663548 = 1; goto LABEL_xE025; case 2: goto LABEL_xE009; case 3: v7 = 0LL; LABEL_xE009: std::sys::unix::UNIX_SIGPIPE_ATTR_SPECIFIED::h1ae45800e8663548 = 1; LABEL_xE010: if ( signal(0xD, v7) == (__sighandler_t)0xFFFFFFFFFFFFFFFFLL ) { fds.sa_mask.__val[1] = (unsigned __int64)&off_512E0; fds.sa_mask.__val[2] = 1LL; fds.sa_handler = 0LL; fds.sa_mask.__val[3] = (unsigned __int64)&unk_42560; fds.sa_mask.__val[4] = 0LL; stacksize[0] = (size_t)&fds; stacksize[1] = (size_t)_$LT$core..fmt..Arguments$u20$as$u20$core..fmt..Display$GT$::fmt::h15bd174c5b27285e; v22 = &off_507C8; v23 = 2LL; v21 = 0LL; v24 = stacksize; v25 = 1LL; std::io::Write::write_fmt::h88186074961638e4(); core::ptr::drop_in_place$LT$core..result..Result$LT$$LP$$RP$$C$std..io..error..Error$GT$$GT$::h47051a553bf87b8d(); LABEL_xE414: std::sys::unix::abort_internal::h1bcf881e3e6f862f(); } LABEL_xE025: memset(&fds, 0, sizeof(fds)); sigaction(0xB, 0LL, &fds); if ( !fds.sa_handler ) { fds.sa_flags = 0x8000004; fds.sa_handler = (__sighandler_t)std::sys::unix::stack_overflow::imp::signal_handler::h9eed1699e3a2066b; sigaction(0xB, &fds, 0LL); std::sys::unix::stack_overflow::imp::NEED_ALTSTACK::h0328ae7462edf624 = 1; } sigaction(7, 0LL, &fds); if ( !fds.sa_handler ) { fds.sa_flags = 0x8000004; fds.sa_handler = (__sighandler_t)std::sys::unix::stack_overflow::imp::signal_handler::h9eed1699e3a2066b; sigaction(7, &fds, 0LL); std::sys::unix::stack_overflow::imp::NEED_ALTSTACK::h0328ae7462edf624 = 1; } std::sys::unix::stack_overflow::imp::make_handler::h945115486f96f7e5(); std::sys::unix::stack_overflow::imp::MAIN_ALTSTACK::h1249fea233ef7b4a = v8; v9 = sysconf(0x1E); std::sys::unix::thread::guard::PAGE_SIZE::hb394fa245618b31c = v9; v10 = v9; if ( !v9 ) { LABEL_xE467: core::panicking::panic::h0ead933cb8f56d66(); } v11 = v9; memset(&fds, 0, 0x38); v12 = pthread_self(); if ( pthread_getattr_np(v12, (pthread_attr_t *)&fds) ) { v13 = 0LL; } else { v19[0] = 0LL; stacksize[0] = 0LL; LODWORD(v27[0]) = pthread_attr_getstack((const pthread_attr_t *)&fds, v19, stacksize); if ( LODWORD(v27[0]) ) { goto LABEL_xE434; } v14 = (unsigned __int64)v19[0]; LODWORD(stacksize[0]) = pthread_attr_destroy((pthread_attr_t *)&fds); if ( LODWORD(stacksize[0]) ) { LABEL_xE434: v22 = 0LL; core::panicking::assert_failed::h92efb50074ef787c(); } if ( (v10 | v14) >> 0x20 ) { v15 = v14 % v10; } else { v15 = (unsigned int)v14 % (unsigned int)v10; } v16 = v10 - v15; if ( !v15 ) { v16 = 0LL; } v3 = v16 + v14; v10 = v3 - v11; v13 = (void (*)(int))(&dword_0 + 1); } _$LT$$RF$$u5b$u8$u5d$$u20$as$u20$alloc..ffi..c_str..CString..new..SpecNewImpl$GT$::spec_new_impl::ha43b7641c7a37b38(); if ( stacksize[2] ) { v30 = stacksize; v19[0] = &v30; v19[1] = _$LT$core..result..Result$LT$T$C$E$GT$$u20$as$u20$core..fmt..Debug$GT$::fmt::h6b7406c389cb2859; fds.sa_mask.__val[1] = (unsigned __int64)&off_507E8; fds.sa_mask.__val[2] = 1LL; fds.sa_handler = 0LL; fds.sa_mask.__val[3] = (unsigned __int64)v19; fds.sa_mask.__val[4] = 1LL; v27[0] = (__int64)&fds; v27[1] = (__int64)_$LT$core..fmt..Arguments$u20$as$u20$core..fmt..Display$GT$::fmt::h15bd174c5b27285e; v22 = &off_507C8; v23 = 2LL; v21 = 0LL; v24 = (size_t *)v27; v25 = 1LL; std::io::Write::write_fmt::h88186074961638e4(); core::ptr::drop_in_place$LT$core..result..Result$LT$$LP$$RP$$C$std..io..error..Error$GT$$GT$::h47051a553bf87b8d(); goto LABEL_xE414; } std::thread::Thread::new::hcf5764422a3481dd(); fds.sa_handler = v13; fds.sa_mask.__val[0] = v10; fds.sa_mask.__val[1] = v3; v18 = v17; std::sys_common::thread_info::set::h5a58eaf2e04c69a0(); (*(void (__fastcall **)(__int64, __int64))(v29 + 0x28))(v28, v18); if ( std::rt::cleanup::CLEANUP::h83d636d36c69baa1 != 4 ) { LOBYTE(v21) = 1; fds.sa_handler = (__sighandler_t)&v21; std::sys_common::once::futex::Once::call::h3553f533aaf8caf1(); } return; default: goto LABEL_xE467; } } ``` * Tạm gác main qua chúng ta có thể tìm hàm check bằng cách search msgbox đã hiện khi ta run thử chương trình ta tìm thấy hàm check điều kiện đúng ![image](https://hackmd.io/_uploads/BJ_Hhyv6a.png) * nhìn vào garph ta có thể thấy nếu nhập vào pass thì pass sẽ được check ở hàm màu đen và sau đó mới in ra terminal ![image](https://hackmd.io/_uploads/ryx2p1vpp.png) * Cụ thể sẽ là hàm `chall::main::hcc105c60431fd7b5()` ``` void __cdecl chall::check::h428e27b3168ba563() { _BYTE *v0; // rdi unsigned __int64 v1; // rsi unsigned __int64 v2; // rax bool v3; // [rsp+167h] [rbp-1h] core::str::_$LT$impl$u20$str$GT$::len::hc55f8affae116d65(); if ( v2 >= 0x15 ) { if ( v1 <= 9 ) { core::panicking::panic_bounds_check::h937aba65fb5d17a6(); } if ( v0[9] == 0x43 && v0[5] == 0x7B && v0[2] == 0x53 && v0[6] == 0x77 && *v0 == 0x42 ) { if ( v1 <= 0x14 ) { core::panicking::panic_bounds_check::h937aba65fb5d17a6(); } if ( v0[0x14] == 0x7D && v0[4] == 0x43 && v0[0xC] == 0x45 && v0[0x13] == 0x76 && v0[0x11] == 0x52 && v0[7] == 0x33 && v0[3] == 0x45 && v0[0xE] == 0x74 && v0[0xF] == 0x4F && v0[0x10] == 0x5F && v0[0xA] == 0x30 && v0[0x12] == 0x33 && v0[0xD] == 0x5F && v0[8] == 0x6C && v0[1] == 0x4B ) { v3 = v0[0xB] == 0x6D; } } } } ``` * Hàm này đơn giản check input ta nhập vào với const là mảng v0[i] sắp xếp thứ tự mảng ta có `0x42, 0x4B, 0x53, 0x45, 0x43, 0x7B, 0x77, 0x33, 0x6C, 0x43, 0x30, 0x6D, 0x45, 0x5F, 0x74, 0x4F, 0x5F, 0x52, 0x33, 0x76, 0x7D` * Decode sang ASCII ta được flag ![image](https://hackmd.io/_uploads/HyADblDap.png) ## VI. BabyASM * Bài này xem thử khả năng đọc hiểu code nasm và cách thức truyền input và output của stack của chúng ta đến đâu và cũng khá giống với 5 bài `GDB Step` trên Pico mình đã từng làm để xem thử bài cho ta những gì ``` babyasm: <+0>: push ebp <+1>: mov ebp,esp <+3>: xor eax,eax <+5>: mov ah,BYTE PTR [ebp+0x9] <+8>: shl ax,0x10 <+12>: sub al,BYTE PTR [ebp+0xe] <+15>: add ah,BYTE PTR [ebp+0xf] <+18>: xor ax,WORD PTR [ebp+0x12] <+22>: nop <+23>: pop ebp <+24>: ret ``` * Bài chỉ cho ta hàm `babyasm` và k có _start hay _end * Ngoài ra bài cho ta 3 đối số `0xabcd1456, 0xdfed7768, 0x68686868` * Ban đầu bài này mình định nhớ AI để convert sang python cho dễ đọc nhưng sau khi thử một lúc thì AI convert sai nên mình đành làm cách thủ công là viết hoàn chỉnh lại chương trình này:vv * Để làm chay thì ta cần lưu ý một tí về stack như sau, nếu Input được truyền vào trước thì sẽ ra sau cùng( cơ chế LIFO) vì thế đề bài cho ta 3 đối số lần lượt lượt là `0xabcd1456, 0xdfed7768, 0x68686868` ta cần biết thêm về cơ chế của thanh ghi `EBP` * Chức năng chính của thanh ghi EBP là lưu trữ địa chỉ cơ sở (base address) của frame của hàm hiện tại trên ngăn xếp (stack frame). Khi một hàm được gọi, một frame mới sẽ được tạo trên ngăn xếp để lưu trữ các biến cục bộ, các tham số của hàm và các giá trị trung gian trong quá trình thực thi hàm đó. Thanh ghi EBP được sử dụng để trỏ đến phần đầu của frame này, giúp chương trình truy cập các biến cục bộ và tham số của hàm một cách dễ dàng. Còn khi một hàm kết thúc, frame của nó sẽ được giải phóng và giá trị của thanh ghi EBP sẽ được khôi phục về giá trị ban đầu của nó trước khi hàm được gọi. * Chính vì cơ chế LIFO nên ta k thể push value như các ngôn ngữ bậc cao được mà phải thực hiện ngược lại bằng cách push giá trị sao cùng lên đầu và giá trị đầu về sau cùng, viết lại thì chương trình sẽ như sau ``` section .text global _start _start: push 0x68686868 push 0xdfed7768 push 0xabcd1456 call babyasm mov eax, 1 xor ebx, ebx int 0x80 babyasm: push ebp mov ebp,esp xor eax,eax mov ah,BYTE [ebp+0x9] shl ax,0x10 sub al,BYTE [ebp+0xe] add ah,BYTE [ebp+0xf] xor ax,WORD [ebp+0x12] nop pop ebp ret ``` * Giờ thì compile nó sang file thực thi và quăng vào `Peda` để run thử ![Screenshot from 2024-03-07 05-24-01](https://hackmd.io/_uploads/Hytj8fDTT.png) * Mình đặt breakpoint ở ` 0x0804900f` ngay lệnh call hàm babyasm ![Screenshot from 2024-03-07 05-24-28](https://hackmd.io/_uploads/SJEp8Gw6p.png) * Đến cuối giá trị cuối cùng của EAX là `0xb77b` > BKSEC{0xb77b}