RE_Challenge

主要是以虎符和近期国际赛的题为主,梳理一下学到的方法和技巧。

虎符

fype

基于libbpf编写的bpf程序逆向,原理类似hook。主要分为两部分,一部分是bpf代码,一部分是用户代码。本题是 libbpf-bootstrap结构下的uprobe模板,用于对程序内函数进行hook(或称跟踪)。

uprobe.bpf.c

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
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright (c) 2020 Facebook */
#include <linux/bpf.h>
#include <linux/ptrace.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

char LICENSE[] SEC("license") = "Dual BSD/GPL";

SEC("uprobe/func")
int BPF_KPROBE(uprobe, int a, int b)
{
bpf_printk("UPROBE ENTRY: a = %d, b = %d\n", a, b);
return 0;
}

SEC("uretprobe/func")
int BPF_KRETPROBE(uretprobe, int ret)
{
bpf_printk("UPROBE EXIT: return = %d\n", ret);
//类似printf 把格式化字符串放入 /sys/kernel/debug/tracing/trace_pipe 文件中,需要sudo 即root权限下运行
return 0;
}
//LICENSE变量定义了BPF代码的许可证。指定许可证是强制性的,由内核强制执行。某些 BPF 功能不可用于非 GPL 兼容代码。
//其中uprobe/func 为进入函数 , uretprobe/func为退出函数。

BPF程序一般需要4个步骤,即创建并打开open、加载并验证loda、附加attach、退出destroy

bpf程序用户代码模板函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
libbpf_set_print(libbpf_print_fn);//设置日志输出函数libbpf_print_fn

bump_memlock_rlimit();//提高内核内部用户的内存权限,为bpf程序的执行做准备

fype(name)_bpf__open_and_load();//加载bpf字节码文件到内核中
//又细分为如下两个函数

fpbe_bpf *obj=fpbe_bpf__open();//创建并打开bpf
//-- fpbe_bpf__create_skeleton(obj)
//bpftool 会根据 bpf 字节码文件(xxx.bpf.o)生成对应的 skeleton 文件——xxx.skel.h。这个文件中包含了关键的函数和结构体。比如,这个文件中包含了 xxx.bpf.o 文件的内容。
//这个函数的内容十分关键

fpbe_bpf__load(obj);//进行check

bpf_program__attach_uprobe() //用于绑定 uprobe 和 uretprobe 追踪目标的信息
//如下 最后一个参数是函数偏移 base_addr调试为0x400000即Imagebase
obj->links.uprobe = bpf_program__attach_uprobe(
obj->progs.uprobe,
0,
0,
"/proc/self/exe",//跟踪程序
(size_t)uprobed_function - base_addr);//跟踪函数

fpbe_bpf__destroy(obj);//退出

image-20220325212024715

了解过bpf程序的框架之后,再分析程序就比较清晰了。将钩子挂在了当前程序的uprobed函数上,该函数内容为hash check,对flag的内容进行检测。

image-20220325212213296

故关键处理在bpf代码,处理后的结果再进行比对,我们只需拿到bpf字节码反汇编逆出逻辑问题就解决了。

方法一:

image-20220326000537317

可以直接在create_skeleton下找到ebpf字节码文件(elf),用ipython dump,当然最后看了wp发现binwalk直接分离是一种不错的方法。

1
2
3
4
5
6
7
8
9
10
import idautils
adr=0x00000000004F4018
len=1648
f=open(r'G:\dump','wb')
s=[]
for i in range(len):
s.append(Byte(adr+i))
f.write(bytes(s))
f.close()
print('ok')

分离出的是bpf字节码,IDA无法直接反汇编,需要找到相应的解释器。

saaph/eBPF_processor . disassemble eBPF bytecode

image-20220326001348915

配置好后打开dump出的文件,选择EBPF,即可对字节码反汇编,查看汇编代码即可,主要是求解线性方程组,z3即可,通过hash来check flag是否正确

image-20220326001641632

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from z3 import *
from hashlib import sha256
a=[Int('a{}'.format(i)) for i in range(4)]
s=Solver()
s.add(a[3]*0x6DC0+a[2]*0xfb88+a[1]*0x71fb+a[0]*0xcc8e==0xBE18A1735995)
s.add(a[3]*0xF1BF+a[2]*0x6AE5+a[1]*0xADD3+a[0]*0x9284==0xA556E5540340)
s.add(a[3]*0xDD85+a[2]*0x8028+a[1]*0x652D+a[0]*0xE712==0xA6F374484DA3)
s.add(a[3]*0x822C+a[2]*0xCA43+a[1]*0x7C8E+a[0]*0xF23A==0xB99C485A7277)
s.check()
ans=s.model()
res=[]
for i in range(4):
res.append(int.to_bytes(ans[a[i]].as_long(),4,'little').decode())
for i in range(3,-1,-1):
print(res[i],end='')
print()
print(sha256(b'0vR3sAlbs8pD2h53').hexdigest())
#0vR3sAlbs8pD2h53

方法二:

使用bpftool,bpftool是一个用来检查 BPF 程序和映射的内核工具。

bpftool prog 可以用来检查系统中运行程序的情况

bpf prog dump 获取到了程序标识符后,可以执行 bpf prog dump 来获取整个程序的数据,能够看到由编译器生成的字节码。

当然拿到编译器生成的字节码已经可读了,如果代码量大的话,则需要修改一下格式之后用C重新编译,详见Mas0n师傅的做法。

参考:

git-libbpf-bootstrap source

ebpf:基于 uprobe 的自定义例程

the shellcode

赛后复现,程序加了Themida / Winlicense v3.0.0.0 - 3.0.4.0强保护壳,硬脱比较困难,并且壳代码有反调试,可以先运行,之后IDA attach来进行调试。

image-20220323165537110

将the shell程序代码段的数据用IDA python批量转为代码,大小0x12f7。

1
2
3
4
5
6
7
8
9
10
11
import idaapi
adr=0x00631000
size=0x12f7
end=adr+size
while adr<end:
idaapi.create_insn(adr)
insn=idaapi.insn_t()
len=idaapi.decode_insn(insn,adr)
adr+=len
print('ok')
# 自动化转data 为 code

之后通过运行时的输出字符定位关键处理函数。

首先对opcode进行base64解密,解密函数通过 密文<->索引表下标 的方式实现,之后又发现了位运算和魔改xxTea。

1
2
3
4
5
6
7
8
9
10
from base64 import b64encode
s=b'abc'
print(b64encode(s))
c=b'YWJj'
table=[0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x40, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3E, 0x42, 0x42, 0x42, 0x3F, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x42, 0x42, 0x42, 0x41, 0x42, 0x42, 0x42, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42]
sum=0
for i in c:
sum=(sum<<6)+table[i]
print(hex(sum))
#base64解密

之后是逐字节循环左移3

1
2
for ( i = 0; i < v2; ++i )
v4->m128i_i8[i] = __ROL1__(v4->m128i_i8[i], 3);

image-20220321234846646

xxTea将明文前一项右移5改为了右移6。

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
#include<iostream>
#define ut32 unsigned int
#define delta 0x9e3779b9
using namespace std;
void XXTea_Decrypt(ut32* enc, ut32 n, ut32* key) {
ut32 y, z, sum;
ut32 e, rounds;
int p;
rounds = 6 + 52 / n;
sum = delta * rounds;

do {
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--) {
y = enc[(p + 1) % n];
z = enc[(p - 1)];
enc[p] -= (((z >> 6 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)));
}
y = enc[1];
z = enc[n - 1];
enc[0] -= (((z >> 6 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(0 & 3) ^ e] ^ z)));
sum -= delta;
} while (--rounds);

}


int main() {
ut32 m[66] = { 0x4B6B89A1, 0x74C15453, 0x4092A06E, 0x429B0C07, 0x40281E84, 0x8B5B44C9, 0x66FEB37B, 0x3C77A603,
0x79C5892D, 0x0D7ADA97, 0x1D51AA56, 0x02D4D703, 0x4FA526BA, 0x32FAD64A, 0x0C0F6091, 0x562B7593,
0xDB9ADD67, 0x76165563, 0xA5F79315, 0x3AEB991D, 0x1AB721D4, 0xAACD9D2C, 0x825C2B27, 0x76A7761A,
0xB4005F18, 0x117F3763, 0x512CC540, 0xC594A16F, 0xD0E24F8C, 0x9CA3E2E9, 0x0A9CC2D5, 0x4629E61D,
0x637129E3, 0xCA4E8AD7, 0xF5DFAF71, 0x474E68AB, 0x542FBC3A, 0xD6741617, 0xAD0DBBE5, 0x62F7BBE3,
0xC8D68C07, 0x880E950E, 0xF80F25BA, 0x767A264C, 0x9A7CE014, 0x5C8BC9EE, 0x5D9EF7D4, 0xB999ACDE,
0xB2EC8E13, 0xEE68232D, 0x927C5FCE, 0xC9E3A85D, 0xAC74B56B, 0x42B6E712, 0xCD2898DA, 0xFCF11C58,
0xF57075EE, 0x5076E678, 0xD4D66A35, 0x95105AB9, 0x1BB04403, 0xB240B959, 0x7B4E261A, 0x23D129D8,
0xF5E752CD, 0x4EA78F70 };
ut32 k[4] = { 0x74,0x6f,0x72,0x61 };
XXTea_Decrypt(m, 66, k);
for (int i = 0; i < 4 * 66; i++) {
uint8_t tmp = *((uint8_t*)m + i);
*((uint8_t*)m + i) = ((tmp >> 3) | (tmp << 5)) & 0xff;
}
for (int i = 0; i < 4 * 66; i++) {
printf("0x%x,", *((uint8_t*)m + i));
}
return 0;
}

from base64 import b64encode
code=[0x60,0xfc,0x68,0x4c,0x77,0x26,0x7,0x33,0xd2,0x64,0x8b,0x52,0x30,0x8b,0x52,0xc,0x8b,0x52,0x14,0x8b,0x72,0x28,0xf,0xb7,0x4a,0x26,0x33,0xff,0x33,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0xc1,0xcf,0xd,0x3,0xf8,0xe2,0xf0,0x52,0x57,0x8b,0x52,0x10,0x8b,0x42,0x3c,0x3,0xc2,0x8b,0x40,0x78,0x85,0xc0,0xf,0x84,0xbe,0x0,0x0,0x0,0x3,0xc2,0x50,0x8b,0x48,0x18,0x8b,0x58,0x20,0x3,0xda,0x83,0xf9,0x0,0xf,0x84,0xa9,0x0,0x0,0x0,0x49,0x8b,0x34,0x8b,0x3,0xf2,0x33,0xff,0x33,0xc0,0xac,0xc1,0xcf,0xd,0x3,0xf8,0x3a,0xc4,0x75,0xf4,0x3,0x7c,0x24,0x4,0x3b,0x7c,0x24,0xc,0x75,0xd9,0x33,0xff,0x33,0xc9,0x83,0xc2,0x50,0xf,0xb6,0x4,0xa,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xf1,0xc1,0xcf,0xd,0x57,0x33,0xff,0x33,0xc9,0x8b,0x54,0x24,0x3c,0x52,0xf,0xb6,0x1c,0xe,0xb8,0x67,0x66,0x66,0x66,0xf7,0xeb,0xd1,0xfa,0x8b,0xc2,0xc1,0xe8,0x1f,0x3,0xc2,0x8d,0x4,0x80,0x2b,0xd8,0x5a,0xf,0xb6,0x4,0xa,0x2b,0xc3,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xd4,0xc1,0xcf,0xd,0x3b,0x3c,0x24,0x74,0x16,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x6e,0x6f,0x0,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0xeb,0x14,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x79,0x65,0x73,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x61,0xc3,0x58,0x5f,0x5a,0x8b,0x12,0xe9,0xb,0xff,0xff,0xff]
print(b64encode(bytes(code)))
"""shellcode
YPxoTHcmBzPSZItSMItSDItSFItyKA+3SiYz/zPArDxhfAIsIMHPDQP44vBSV4tSEItCPAPCi0B4hcAPhL4AAAADwlCLSBiLWCAD2oP5AA+EqQAAAEmLNIsD8jP/M8Cswc8NA/g6xHX0A3wkBDt8JAx12TP/M8mDwlAPtgQKwc8NA/hBg/kOdfHBzw1XM/8zyYtUJDxSD7YcDrhnZmZm9+vR+ovCwegfA8KNBIAr2FoPtgQKK8PBzw0D+EGD+Q511MHPDTs8JHQWaCVzAACLxGhubwAAVFCLXCRI/9PrFGglcwAAi8RoeWVzAFRQi1wkSP/TWFhYWFhYWFhYYcNYX1qLEukL////
"""

image-20220322001247766

shellcode check成功,之后就是执行shellcode来解密flag。

image-20220323171640667

用函数指针来调用解密的shellcode,以便进一步调试。

1
2
3
4
5
6
7
8
9
10
11
12
#include<stdio.h>
#include<Windows.h>
typedef void (*func)(unsigned int(_cdecl*)(unsigned int), char*, unsigned int, unsigned int, unsigned int);
int main() {
unsigned char a[264] = { 0x60,0xfc,0x68,0x4c,0x77,0x26,0x7,0x33,0xd2,0x64,0x8b,0x52,0x30,0x8b,0x52,0xc,0x8b,0x52,0x14,0x8b,0x72,0x28,0xf,0xb7,0x4a,0x26,0x33,0xff,0x33,0xc0,0xac,0x3c,0x61,0x7c,0x2,0x2c,0x20,0xc1,0xcf,0xd,0x3,0xf8,0xe2,0xf0,0x52,0x57,0x8b,0x52,0x10,0x8b,0x42,0x3c,0x3,0xc2,0x8b,0x40,0x78,0x85,0xc0,0xf,0x84,0xbe,0x0,0x0,0x0,0x3,0xc2,0x50,0x8b,0x48,0x18,0x8b,0x58,0x20,0x3,0xda,0x83,0xf9,0x0,0xf,0x84,0xa9,0x0,0x0,0x0,0x49,0x8b,0x34,0x8b,0x3,0xf2,0x33,0xff,0x33,0xc0,0xac,0xc1,0xcf,0xd,0x3,0xf8,0x3a,0xc4,0x75,0xf4,0x3,0x7c,0x24,0x4,0x3b,0x7c,0x24,0xc,0x75,0xd9,0x33,0xff,0x33,0xc9,0x83,0xc2,0x50,0xf,0xb6,0x4,0xa,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xf1,0xc1,0xcf,0xd,0x57,0x33,0xff,0x33,0xc9,0x8b,0x54,0x24,0x3c,0x52,0xf,0xb6,0x1c,0xe,0xb8,0x67,0x66,0x66,0x66,0xf7,0xeb,0xd1,0xfa,0x8b,0xc2,0xc1,0xe8,0x1f,0x3,0xc2,0x8d,0x4,0x80,0x2b,0xd8,0x5a,0xf,0xb6,0x4,0xa,0x2b,0xc3,0xc1,0xcf,0xd,0x3,0xf8,0x41,0x83,0xf9,0xe,0x75,0xd4,0xc1,0xcf,0xd,0x3b,0x3c,0x24,0x74,0x16,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x6e,0x6f,0x0,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0xeb,0x14,0x68,0x25,0x73,0x0,0x0,0x8b,0xc4,0x68,0x79,0x65,0x73,0x0,0x54,0x50,0x8b,0x5c,0x24,0x48,0xff,0xd3,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x61,0xc3,0x58,0x5f,0x5a,0x8b,0x12,0xe9,0xb,0xff,0xff,0xff };
void* data = VirtualAlloc(0, 264, 0x00001000, 0x40);
memcpy(data, a, 264 * sizeof(unsigned char));
func test = (func)data;
char str[15] = "aaaaaaaaaaaaaa";
test((unsigned int(_cdecl*)(unsigned int))printf,str,0,264,4096);
return 0;
}

编译成32位可执行文件,调试定位到flag的check点。

image-20220323221616553

察check_num和v23的变化,发现基本结构一致,故只要让v22[i]=flag[i]-v17[i]%5,那么v23最终结果一定和check num相同,所有数据均可通过调试获得。

1
2
3
4
5
6
from hashlib import m
v17='LoadLibraryExA'
v22=[0x69, 0x73, 0x20, 0x70, 0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E]
flag=''
for i in range(14):
flag+=chr(v22[i]+ord(v17[i])%5)

当然github上也能找到针对WinLicense/Themida 2.x 和 3.x脱壳的工具,不过attach这种方法更趋向于通解,即可能适用于多种壳型。

foreign challenges

浅浅记录一下近期国际比赛上的一些题目,容易踩坑或者考点比较新颖。

OFPPT-CTF . unicode

unicode2utf-8

题目给了一个flag.jpg.utf8的文件,将图片存为了utf-8编码的格式。unicode-utf-8对照表

只需要将utf-8读入转为unicode编码,python3的chr和ord函数默认是以unicode编码来处理字符的,所以只需以utf-8编码格式读取数据,再用ord遍历就拿到了对应的unicode值。

1
2
3
4
5
6
7
import codecs
f=codecs.open(r'flag.jpg.utf8','r',encoding='utf-8').read()
# print(f)
s=[]
for i in f:
s.append(ord(i))
f=open(r'flag.jpg','wb').write(bytes(s))

codecs是python用于处理文本编码的包,支持多种编码。…其实这题挺坑的…

xxx_game

python打包的elf files

python打包的elf文件,之前多是打包exe,并且pyinstaller打包的exe图标很容易识别。还有一种是py2exe打包,较前者出现频率较少,并且打包exe文件查看strings窗口会有PY2EXE的字符。

image-20220327210353884

根据strings窗口下很多py命名的函数,所以认为是经过了pyc文件经过打包生成的exe,直接用pyinstxtractor解包会出错,正确步骤如下。

参考Extracting Linux ELF binaries第一种简单的方法。

1
2
3
4
5
#Using objcopy dump the section named pydata to a file.
objcopy --dump-section pydata=pydata.dump testfile.elf

#Now you can run pyinstxtractor on the dumped file.
python2 pyinstxtractor.py pydata.dump

image-20220327211244130

之后顺序同打包exe的解法一致。

picoCTF_game2

hide and exec code

通过对称加密算法来解码code并执行,当然可以通过给出高版本的pyc文件来让这个问题更有趣。

1
2
3
4
5
6
7
8
9
10
11
12
13
import base64
from cryptography.fernet import Fernet

payload = b'gAAAAABiMD1Dt87s50caSunQlHoZqPOwtGNaQXexN-gjKwZeuLEN_-v6UcFJHVXOT4B6DcD1SB7cnd6yTcHg9e9LZCAeJY2cJ0r0sfyGPRiH60F-WbkyUjlAdDywI8RPdTpDYLuBmpZ_Kun-kHyTzMjeKR6R26Z4JITUS8n7Dj9X--9eNLajH6UuYD4GkjRACpsidl_8z33DlB86u_xDCMMm7HFK2oJTs8HG1fBex6enQsu0frUAJbx56DxhRvWawAysDMtLE50vaohrzkVV7Yaz4ClilwgfjQ=='

key_str = 'correctstaplecorrectstaplecorrec'
key_base64 = base64.b64encode(key_str.encode())
f = Fernet(key_base64)
plain = f.decrypt(payload)
print(plain)

# exec(plain.decode())
#b"\npw = input('What\\'s the password? ')\n\nif pw == 'batteryhorse':\n print('picoCTF{175_chr157m45_8aef58d2}')\nelse:\n print('That password is incorrect.')\n\n"

Gandalf Baba

apk-webview-js

webview封装的apk程序,主要通过调用js来进行交互,需要进行get传参,并且请求头满足要求才能getflag。

js代码在assets文件下,内容如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
User=MyInterface.GetUser();
Serial=MyInterface.GetSerial();
Model=MyInterface.GetModel();
Product=MyInterface.GetProduct();
Auth=MyInterface.GetAuth();
function myFunction() {
var x = document.getElementById("myTable").rows[1].cells;
x[0].innerHTML =User ;

var y = document.getElementById("myTable").rows[1].cells;
x[1].innerHTML =Serial;

var z = document.getElementById("myTable").rows[1].cells;
x[2].innerHTML =Model ;
var a = document.getElementById("myTable").rows[1].cells;
x[3].innerHTML =Product ;

var b = document.getElementById("myTable").rows[1].cells;
x[4].innerHTML =Auth ;
}
</script>

其中并没有调用关键的flag函数,故可以添加该函数并重新打包签名,当然也可以直接逆向关键函数的逻辑,这里我采用后者。

image-20220327213347459

对题目给出的网站get传入参数,满足其请求头的内容要求即可绕过Gandalf Baba成功拿到flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a='RheO5PB6mfL5N3YBH45e5XuCEaWpvWUFESqTYnZk'
c=[38, 0, 0, 60, 93, 49, 50, 95, 25, 22, 45, 71, 47, 94, 60, 54, 45, 70]
for i in range(len(c)):
print(chr(c[i]^ord(a[i])),end='')
print()
c=[80, 104, 97, 67, 36, 64, 66, 52, 97, 100, 64, 33, 70, 33, 72, 64]
print(bytes(c))

import requests as req
headers = {'user-agent': 'PhaC$@B4ad@!F!H@',
'Accept-Language':"From JavaScript Interface"
}

resp = req.get("https://teambounters.com/shapa.php?theshapitparameter=givemeflag",headers=headers)
print(resp.text)
#参考: https://geek-docs.com/python/python-tutorial/python-requests.html

虎符的Contra 2048也采用的webview封装,对js和消息处理目前还不是太了解,之后会给出复现。

amazing_code

Evm Opcodes

打开附件,Evm Opcode,一般出现在智能合约的逆向,目前是有工具可以做到反编译的所以无需硬读。github- Evm Opcode对照 HEX

1
2
3
4
5
6
7
8
9
10
11
PUSH1	0x80
PUSH1 0x40
MSTORE
PUSH1 0x20
PUSH1 0xf8
SHL
PUSH1 0x00
DUP1
PUSH2 0x0100
EXP
...

先将opcode转为16进制数据。

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
from pyevmasm import assemble_hex

op=['PUSH1', 'MSTORE', 'SHL', 'DUP1', 'PUSH2', 'EXP', 'DUP2', 'SLOAD', 'MUL', 'NOT', 'AND', 'SWAP1', 'DUP4', 'SHR', 'OR', 'SSTORE', 'POP', 'DIV', 'XOR', 'CALLVALUE', 'ISZERO', 'JUMPI', 'REVERT', 'JUMPDEST', 'CODECOPY', 'RETURN', 'INVALID', 'CALLDATASIZE', 'LT', 'CALLDATALOAD', 'PUSH4', 'GT', 'EQ', 'JUMP', 'MLOAD', 'SWAP2', 'SUB', 'PUSH32', 'DUP3', 'ADD', 'DUP5', 'SWAP3', 'LOG2', 'PUSH5', 'SLT', 'SHA3', 'PUSH19', 'CALLER', 'SDIV', 'LOG4', 'PUSH17']
tb=[0x60+i for i in range(32)]
k=[]
for i in op:
try:
k.append(int(assemble_hex(i),16))
except:
k.append(tb[int(i[4:])-1])
table=dict(zip(op,k))
print(table) #根据用到的op生成字典

table={'PUSH1': 96, 'MSTORE': 82, 'SHL': 27, 'DUP1': 128, 'PUSH2': 97, 'EXP': 10, 'DUP2': 129, 'SLOAD': 84, 'MUL': 2, 'NOT': 25, 'AND': 22, 'SWAP1': 144, 'DUP4': 131, 'SHR': 28, 'OR': 23, 'SSTORE': 85, 'POP': 80, 'DIV': 4, 'XOR': 24, 'CALLVALUE': 52, 'ISZERO': 21, 'JUMPI': 87, 'REVERT': 253, 'JUMPDEST': 91, 'CODECOPY': 57, 'RETURN': 243, 'INVALID': 254, 'CALLDATASIZE': 54, 'LT': 16, 'CALLDATALOAD': 53, 'PUSH4': 99, 'GT': 17, 'EQ': 20, 'JUMP': 86, 'MLOAD': 81, 'SWAP2': 145, 'SUB': 3, 'PUSH32': 127, 'DUP3': 130, 'ADD': 1, 'DUP5': 132, 'SWAP3': 146, 'LOG2': 162, 'PUSH5': 100, 'SLT': 18, 'SHA3': 32, 'PUSH19': 114, 'CALLER': 51, 'SDIV': 5, 'LOG4': 164, 'PUSH17': 112}
f=open(r'amazing_code','r')
byt=''
while True:
buf=f.readline()
if buf:
stu=buf.replace('\n','').split(' ')
try:
byt+=hex(table[stu[0]])[2:].zfill(2)+stu[1].replace('0x','')
except:
pass
else:
break
print(byt)

得到的16进制数据可以通过Online Solidity Decompiler在线反编译,结果可能有些偏差比较难读,关键部分还是要参考字节码文件。

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
contract Contract {
function main() {
memory[0x40:0x60] = 0x80;
storage[0x00] = (storage[0x00] & ~0xff) | ((0x20 << 0xf8) >> 0xf8);
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x01)) | ((0x53 << 0xf8) >> 0xf8) * 0x0100 ** 0x01;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x02)) | ((0x6f << 0xf8) >> 0xf8) * 0x0100 ** 0x02;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x03)) | ((0x6c << 0xf8) >> 0xf8) * 0x0100 ** 0x03;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x04)) | ((0x69 << 0xf8) >> 0xf8) * 0x0100 ** 0x04;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x05)) | ((0x64 << 0xf8) >> 0xf8) * 0x0100 ** 0x05;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x06)) | ((0x60 << 0xf8) >> 0xf8) * 0x0100 ** 0x06;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x07)) | ((0x74 << 0xf8) >> 0xf8) * 0x0100 ** 0x07;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x08)) | ((0x79 << 0xf8) >> 0xf8) * 0x0100 ** 0x08;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x09)) | ((0x5f << 0xf8) >> 0xf8) * 0x0100 ** 0x09;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0a)) | ((0x42 << 0xf8) >> 0xf8) * 0x0100 ** 0x0a;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0b)) | ((0x69 << 0xf8) >> 0xf8) * 0x0100 ** 0x0b;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0c)) | ((0x74 << 0xf8) >> 0xf8) * 0x0100 ** 0x0c;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0d)) | ((0x77 << 0xf8) >> 0xf8) * 0x0100 ** 0x0d;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0e)) | ((0x69 << 0xf8) >> 0xf8) * 0x0100 ** 0x0e;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x0f)) | ((0x73 << 0xf8) >> 0xf8) * 0x0100 ** 0x0f;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x10)) | ((0x65 << 0xf8) >> 0xf8) * 0x0100 ** 0x10;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x11)) | ((0x5f << 0xf8) >> 0xf8) * 0x0100 ** 0x11;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x12)) | ((0x4f << 0xf8) >> 0xf8) * 0x0100 ** 0x12;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x13)) | ((0x70 << 0xf8) >> 0xf8) * 0x0100 ** 0x13;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x14)) | ((0x65 << 0xf8) >> 0xf8) * 0x0100 ** 0x14;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x15)) | ((0x72 << 0xf8) >> 0xf8) * 0x0100 ** 0x15;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x16)) | ((0x61 << 0xf8) >> 0xf8) * 0x0100 ** 0x16;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x17)) | ((0x74 << 0xf8) >> 0xf8) * 0x0100 ** 0x17;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x18)) | ((0x69 << 0xf8) >> 0xf8) * 0x0100 ** 0x18;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x19)) | ((0x6f << 0xf8) >> 0xf8) * 0x0100 ** 0x19;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1a)) | ((0x6e << 0xf8) >> 0xf8) * 0x0100 ** 0x1a;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1b)) | 0x00;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1c)) | 0x00;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1d)) | 0x00;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1e)) | 0x00;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x1f)) | 0x00;
storage[0x01] = (storage[0x01] & ~0xff) | ((0x62 << 0xf8) >> 0xf8);
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x01)) | ((0x10 << 0xf8) >> 0xf8) * 0x0100 ** 0x01;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x02)) | ((0x3b << 0xf8) >> 0xf8) * 0x0100 ** 0x02;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x03)) | ((0x2a << 0xf8) >> 0xf8) * 0x0100 ** 0x03;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x04)) | ((0x12 << 0xf8) >> 0xf8) * 0x0100 ** 0x04;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x05)) | ((0x26 << 0xf8) >> 0xf8) * 0x0100 ** 0x05;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x06)) | ((0x09 << 0xf8) >> 0xf8) * 0x0100 ** 0x06;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x07)) | 0x00;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x08)) | ((0x26 << 0xf8) >> 0xf8) * 0x0100 ** 0x08;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x09)) | ((0x10 << 0xf8) >> 0xf8) * 0x0100 ** 0x09;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0a)) | ((0x32 << 0xf8) >> 0xf8) * 0x0100 ** 0x0a;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0b)) | ((0x0c << 0xf8) >> 0xf8) * 0x0100 ** 0x0b;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0c)) | ((0x06 << 0xf8) >> 0xf8) * 0x0100 ** 0x0c;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0d)) | ((0x16 << 0xf8) >> 0xf8) * 0x0100 ** 0x0d;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0e)) | ((0x1d << 0xf8) >> 0xf8) * 0x0100 ** 0x0e;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x0f)) | ((0x1a << 0xf8) >> 0xf8) * 0x0100 ** 0x0f;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x10)) | ((0x0a << 0xf8) >> 0xf8) * 0x0100 ** 0x10;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x11)) | ((0x31 << 0xf8) >> 0xf8) * 0x0100 ** 0x11;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x12)) | ((0x3c << 0xf8) >> 0xf8) * 0x0100 ** 0x12;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x13)) | ((0x2f << 0xf8) >> 0xf8) * 0x0100 ** 0x13;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x14)) | ((0x2c << 0xf8) >> 0xf8) * 0x0100 ** 0x14;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x15)) | ((0x1c << 0xf8) >> 0xf8) * 0x0100 ** 0x15;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x16)) | ((0x3e << 0xf8) >> 0xf8) * 0x0100 ** 0x16;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x17)) | ((0x27 << 0xf8) >> 0xf8) * 0x0100 ** 0x17;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x18)) | ((0x06 << 0xf8) >> 0xf8) * 0x0100 ** 0x18;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x19)) | ((0x03 << 0xf8) >> 0xf8) * 0x0100 ** 0x19;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1a)) | ((0x07 << 0xf8) >> 0xf8) * 0x0100 ** 0x1a;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1b)) | ((0x64 << 0xf8) >> 0xf8) * 0x0100 ** 0x1b;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1c)) | ((0x69 << 0xf8) >> 0xf8) * 0x0100 ** 0x1c;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1d)) | ((0x74 << 0xf8) >> 0xf8) * 0x0100 ** 0x1d;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1e)) | ((0x79 << 0xf8) >> 0xf8) * 0x0100 ** 0x1e;
storage[0x01] = (storage[0x01] & ~(0xff * 0x0100 ** 0x1f)) | ((0x7d << 0xf8) >> 0xf8) * 0x0100 ** 0x1f;
storage[0x02] = (((storage[0x00] << 0xf8) ~ (storage[0x01] << 0xf8)) >> 0xf8) | (storage[0x02] & ~0xff);
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x01 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x01 << 0xf8)) >> 0xf8) * 0x0100 ** 0x01 | (storage[0x02] & ~(0xff * 0x0100 ** 0x01));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x02 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x02 << 0xf8)) >> 0xf8) * 0x0100 ** 0x02 | (storage[0x02] & ~(0xff * 0x0100 ** 0x02));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x03 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x03 << 0xf8)) >> 0xf8) * 0x0100 ** 0x03 | (storage[0x02] & ~(0xff * 0x0100 ** 0x03));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x04 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x04 << 0xf8)) >> 0xf8) * 0x0100 ** 0x04 | (storage[0x02] & ~(0xff * 0x0100 ** 0x04));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x05 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x05 << 0xf8)) >> 0xf8) * 0x0100 ** 0x05 | (storage[0x02] & ~(0xff * 0x0100 ** 0x05));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x06 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x06 << 0xf8)) >> 0xf8) * 0x0100 ** 0x06 | (storage[0x02] & ~(0xff * 0x0100 ** 0x06));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x07 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x07 << 0xf8)) >> 0xf8) * 0x0100 ** 0x07 | (storage[0x02] & ~(0xff * 0x0100 ** 0x07));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x08 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x08 << 0xf8)) >> 0xf8) * 0x0100 ** 0x08 | (storage[0x02] & ~(0xff * 0x0100 ** 0x08));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x09 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x09 << 0xf8)) >> 0xf8) * 0x0100 ** 0x09 | (storage[0x02] & ~(0xff * 0x0100 ** 0x09));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0a << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0a << 0xf8)) >> 0xf8) * 0x0100 ** 0x0a | (storage[0x02] & ~(0xff * 0x0100 ** 0x0a));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0b << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0b << 0xf8)) >> 0xf8) * 0x0100 ** 0x0b | (storage[0x02] & ~(0xff * 0x0100 ** 0x0b));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0c << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0c << 0xf8)) >> 0xf8) * 0x0100 ** 0x0c | (storage[0x02] & ~(0xff * 0x0100 ** 0x0c));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0d << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0d << 0xf8)) >> 0xf8) * 0x0100 ** 0x0d | (storage[0x02] & ~(0xff * 0x0100 ** 0x0d));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0e << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0e << 0xf8)) >> 0xf8) * 0x0100 ** 0x0e | (storage[0x02] & ~(0xff * 0x0100 ** 0x0e));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x0f << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x0f << 0xf8)) >> 0xf8) * 0x0100 ** 0x0f | (storage[0x02] & ~(0xff * 0x0100 ** 0x0f));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x10 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x10 << 0xf8)) >> 0xf8) * 0x0100 ** 0x10 | (storage[0x02] & ~(0xff * 0x0100 ** 0x10));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x11 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x11 << 0xf8)) >> 0xf8) * 0x0100 ** 0x11 | (storage[0x02] & ~(0xff * 0x0100 ** 0x11));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x12 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x12 << 0xf8)) >> 0xf8) * 0x0100 ** 0x12 | (storage[0x02] & ~(0xff * 0x0100 ** 0x12));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x13 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x13 << 0xf8)) >> 0xf8) * 0x0100 ** 0x13 | (storage[0x02] & ~(0xff * 0x0100 ** 0x13));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x14 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x14 << 0xf8)) >> 0xf8) * 0x0100 ** 0x14 | (storage[0x02] & ~(0xff * 0x0100 ** 0x14));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x15 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x15 << 0xf8)) >> 0xf8) * 0x0100 ** 0x15 | (storage[0x02] & ~(0xff * 0x0100 ** 0x15));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x16 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x16 << 0xf8)) >> 0xf8) * 0x0100 ** 0x16 | (storage[0x02] & ~(0xff * 0x0100 ** 0x16));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x17 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x17 << 0xf8)) >> 0xf8) * 0x0100 ** 0x17 | (storage[0x02] & ~(0xff * 0x0100 ** 0x17));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x18 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x18 << 0xf8)) >> 0xf8) * 0x0100 ** 0x18 | (storage[0x02] & ~(0xff * 0x0100 ** 0x18));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x19 << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x19 << 0xf8)) >> 0xf8) * 0x0100 ** 0x19 | (storage[0x02] & ~(0xff * 0x0100 ** 0x19));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1a << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1a << 0xf8)) >> 0xf8) * 0x0100 ** 0x1a | (storage[0x02] & ~(0xff * 0x0100 ** 0x1a));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1b << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1b << 0xf8)) >> 0xf8) * 0x0100 ** 0x1b | (storage[0x02] & ~(0xff * 0x0100 ** 0x1b));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1c << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1c << 0xf8)) >> 0xf8) * 0x0100 ** 0x1c | (storage[0x02] & ~(0xff * 0x0100 ** 0x1c));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1d << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1d << 0xf8)) >> 0xf8) * 0x0100 ** 0x1d | (storage[0x02] & ~(0xff * 0x0100 ** 0x1d));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1e << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1e << 0xf8)) >> 0xf8) * 0x0100 ** 0x1e | (storage[0x02] & ~(0xff * 0x0100 ** 0x1e));
storage[0x02] = (((storage[0x00] / 0x0100 ** 0x1f << 0xf8) ~ (storage[0x01] / 0x0100 ** 0x1f << 0xf8)) >> 0xf8) * 0x0100 ** 0x1f | (storage[0x02] & ~(0xff * 0x0100 ** 0x1f));
var var0 = msg.value;

if (var0) { revert(memory[0x00:0x00]); }

memory[0x00:0x089f] = code[0x0f39:0x17d8];
return memory[0x00:0x089f];
}
}

analyse this code

1
2
3
4
5
6
7
8
9
10
storage=[0]
storage[0x00] = (storage[0x00] & ~0xff) | ((0x20 << 0xf8) >> 0xf8);
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x01)) | ((0x53 << 0xf8) >> 0xf8) * 0x0100 ** 0x01;
storage[0x00] = (storage[0x00] & ~(0xff * 0x0100 ** 0x02)) | ((0x6f << 0xf8) >> 0xf8) * 0x0100 ** 0x02;
print(hex(storage[0]))
#0x6f5320
#此类代码是在赋值按照小端序 最后s[0]和s[1]都变成了32字节大小的变量
#之后对s[0]和s[1]结果进行异或操作,存在s[2]中
storage[0x02] = (((storage[0x00] << 0xf8) ~ (storage[0x01] << 0xf8)) >> 0xf8) | (storage[0x02] & ~0xff);
#其中~表示xor

可以返回字节码查看 ~ 到底对应着什么操作 XOR 和 NOT的hex十分接近,可能反编译器内部出了点问题。

image-20220327232655873

exp

1
2
3
4
5
a=[0x20,0x53,0x6f,0x6c,0x69,0x64,0x60,0x74,0x79,0x5f,0x42,0x69,0x74,0x77,0x69,0x73,0x65,0x5f,0x4f,0x70,0x65,0x72,0x61,0x74,0x69,0x6f,0x6e,0,0,0,0,0]
b=[0x62,0x10,0x3b,0x2a,0x12,0x26,0x09,0x00,0x26,0x10,0x32,0x0c,0x06,0x16,0x1d,0x1a,0x0a,0x31,0x3c,0x2f,0x2c,0x1c,0x3e,0x27,0x06,0x03,0x07,0x64,0x69,0x74,0x79,0x7d]
for i,j in zip(a,b):
print(chr(i^j),end='')
#BCTF{Bit_Operations_In_Solidity}

The End

还有一些题记不太清了,先暂时整理这些。最近还遇到许多遍历和搜索的算法题,如走图和dfs等,能还原正向算法,解不出就很难受并且无从下手,手撸党表示没机会。

摸的RustIOT之后也会整理一下,Rust就是硬调,IOT要熟悉信号用到的协议和一些分析工具等。

博客由于大创(其实去是峡谷想当yewang)等原因好久没更了,最近许多硬核赛题还没有复现,真是长路漫漫,学无止境(越摆越烂)啊!

20220113025224690


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!