前言
摸pizza大佬!
-。-做这两题好想哭-。-
我好菜丫,大佬直接看伪汇编写脚本。我呢,脚本都快看不懂。
wasm
wasm虐我千百遍我却待她如初恋。-。
之前遇到过类似的,但是没有仔细的研究,只知道夜影师傅的blog有写过类似的文章,但是没写全。
首先是用wabt
项目将wasm
转化为c
代码,虽然效果感人,但至少比wat
要强一点。
一直想要调试它,但我真的好菜,网上找了好多demo
不过报了好多奇奇怪怪的错误,夜影师傅都被我气哭了-。-
所以我是真的不会,只是收集了一下相关的例题和脚本,以及一些套路。
0x1.easywasm
全场只有pizza
大佬一个人做出来了,ORZ!
菜鸡的我,虽然也看到了32组md5还有23333333...
,但是然并软。
伪c
代码太多了,我稍微整理了下。。其实我感觉找关键的api
能把大致的过程理出来,但是这具体的加密过程实在是看不出来丫。
i0 = _strlen(i0); //获取长度
i1 = 32u;
i0 = i0 == i1;
if (i0) { //判断是否为32
L2:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //取出输入的一个字节
i32_store8(Z_envZ_memory, (u64)(i0), i1); //store 到哪里去呢 Z_envZ_memory是之前初始化的变量
_md5(i0, i1, i2);
L3:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //中间做了一些变化,
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i32_store8(Z_envZ_memory, (u64)(i0), i1);
if (i0) {goto L3;}
_md5(i0, i1, i2); //再次md5加密
L4:
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i32_store8(Z_envZ_memory, (u64)(i0), i1);
if (i0) {goto L4;}
i1 = (*Z_envZ_memoryBaseZ_i); //取出固定数据 进行比较
i0 = _memcmp(i0, i1, i2);
if (i0) {
i0 = 0u;
p0 = i0;
goto B0;
}
i1 = 32u; //循环部分 循环32次
i0 = i0 < i1;
if (i0) {goto L2;}
i0 = 1u;
} else {
i0 = 0u;
}
B0:;
FUNC_EPILOGUE;
return i0;
大概就是个这么个东西吧。所以代码如下(基本上借用pizza师傅,莫怪,实在太菜-。-):
from hashlib import md5
a=['562fe3cc50014c260d9e8cf4ed38c77a',
'c022ad0cc0075a9ab14b412a1082d5f3',
'64c286cfc623aa8d7df7c088ebf7d718',
'83664bdee4b613b7e7a51b5213470a8d',
'b020bf598aaa2b3e03ed02c85436268a',
'4fdac5ac807506938103e775c50099ed',
'4fdac5ac807506938103e775c50099ed',
'c231d607b6823fd0a68e813760809754',
'd168c21d10371a5ab61bcfe6c759ef6e',
'f60d709ccf989d849028f97a03d2f3ba',
'a0184f8240e2fe46861dc8d15a819cb0',
'9dbec414336e741e9c73422df59de297',
'6fb5209d8fc8bb8507245bcfa24ae11f',
'6fb5209d8fc8bb8507245bcfa24ae11f',
'00c77fbc60a5bfc466d3d069876ec348',
'00c77fbc60a5bfc466d3d069876ec348',
'df33464fb471c46abaf691c000a0e30d',
'4fdac5ac807506938103e775c50099ed',
'f60d709ccf989d849028f97a03d2f3ba',
'fcc94a20596f2619868f3a4bf52eadf7',
'00c77fbc60a5bfc466d3d069876ec348',
'd168c21d10371a5ab61bcfe6c759ef6e',
'9dbec414336e741e9c73422df59de297',
'fcc94a20596f2619868f3a4bf52eadf7',
'9b37db091979bedf00a7095851ba6f59',
'00c77fbc60a5bfc466d3d069876ec348',
'f60d709ccf989d849028f97a03d2f3ba',
'fcc94a20596f2619868f3a4bf52eadf7',
'd168c21d10371a5ab61bcfe6c759ef6e',
'f60d709ccf989d849028f97a03d2f3ba',
'183342997ffed4b3189e977d077a60b4',
'f404a3368d2d8f57464f739d4ed01c0e']
flag = ""
for i in xrange(0, 32):
ci = 0
for cc in xrange(0x20, 0x7F):
ch = chr(cc)
x = md5("2333333333333333333333333333333" + ch).hexdigest()
x = md5(x).hexdigest()
if(x == a[i]):
ci = cc
break
assert(ci != 0)
flag += chr(ci)
print(flag)
0x2.wasm_3(sces60107)
之前的练习题,后来才发现这题原来是*ctf
的题目。
同样的先转化成伪c
代码,感觉是一个套路撒,又是一个check
函数,最后又是一段数据。-。-
static u32 _check(u32 p0) {
FUNC_PROLOGUE;
if (i0) {
i0 = 160u;
(*Z_envZ_abortStackOverflowZ_vi)(i0);
}
i0 = (*Z_envZ__strlenZ_ii)(i0); //获取长度
i1 = 24u;
i0 = i0 != i1;
if (i0) {
goto Bfunc;
}
i0 = (*Z_envZ_memoryBaseZ_i);
L2:
i1 = i32_load8_s(Z_envZ_memory, (u64)(i1)); //取出一个字节
i32_store8(Z_envZ_memory, (u64)(i0), i1);
i0 = (u32)((s32)i0 < (s32)i1); //这里有个判断。。不太清楚什么用处
if (i0) {goto L2;}
_EncryptCBC(i0, i1, i2, i3); //进行_EncryptCBC 加密
i0 = (*Z_envZ_memoryBaseZ_i); //后面一堆没怎么看懂。。太菜了我-。-
L3:
i1 = i32_load(Z_envZ_memory, (u64)(i1));
i32_store(Z_envZ_memory, (u64)(i0), i1);
if (i0) {goto L3;}
i0 = 0u;
L4:
if (i0) {
goto B5;
}
L7:
i0 = !(i0);
if (i0) {
goto B8;
}
i0 = i32_load8_s(Z_envZ_memory, (u64)(i0));
i0 = i32_load(Z_envZ_memory, (u64)(i0));
i0 = i0 != i1;
if (i0) {
i0 = 8u;
goto B5;
}
goto L7;
B8:;
i1 = 1u;
goto L4;
B5:;
i1 = 8u;
i0 = i0 == i1;
if (i0) {
i0 = 0u;
goto Bfunc;
} else {
i0 = i0 == i1;
if (i0) {
goto Bfunc;
}
}
i0 = 0u;
goto Bfunc;
Bfunc:;
FUNC_EPILOGUE;
return i0;
}
基本上的流程还是差不多。接下来看一下算法。
EncryptCBC(u32 p0, u32 p1, u32 p2, u32 p3)
其中还调用了f9
,f10
,f11
,然后在f10
这个函数中发现可疑的数据2654435769u==0x9e3779b9
,百度下,提示是tea
算法,于是找了找tea
算法的c
实现,对比一下基本上可以确定EncryptCBC
就是tea
算法,而且tea
是可逆的,这时我们结合着算法再来看一遍check
过程,应该会比较清晰。
cipher
是那一串数据,key
应该就是webasmintersting
,每四个对应一个32位无符号数。由于flag长度为24,每一轮加密需要2个32位数,因此总共加密了三轮,解密时注意小端序!!
但是我在写解密脚本时,怎么都不对-。-难道不是标准的tea
算法???所以我又仔细的瞅了瞅f10
函数,还真的不是标准的tea
算法。。
i1 = 3u;
i0 <<= (i1 & 31);
i0 ^= i1;
i0 ^= i1;
i1 = 5u;
i0 >>= (i1 & 31);
i0 ^= i1;
v0 += ((v1<<3) ^ k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<3) ^ k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
修改脚本如下:
//解密函数
void decrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i; /* set up */
uint32_t delta=0x9e3779b9; /* a key schedule constant */
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i<32; i++) { /* basic cycle start */
v1 -= ((v0<<3) ^ k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<3) ^ k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= delta;
} /* end cycle */
v[0]=v0; v[1]=v1;
}
void getflag(uint32_t v[2]){
unsigned int i=0;
for(i=0;i<4;i++){
printf("%c", (v[0]>>(8*i)&0xff));
}
for(i=0;i<4;i++){
printf("%c", (v[1]>>(8*i)&0xff));
}
printf("\n");
}
// *ctf{web4ss3mbly_1s_god}
int main()
{
uint32_t v[3][2]={{0x2f6c8735,0x997702bd},{0xf6482c8c,0x5522791d},{0xcbd831e3,0xb9d61393}},k[4]={0x61626577,0x6e696d73,0x73726574,0x676e6974};
// v为要加密的数据是两个32位无符号整数
// k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
decrypt(v[0], k);
printf("解密后的数据:%x %x\n",v[0][0],v[0][1]);
getflag(v[0]);
decrypt(v[1], k);
printf("解密后的数据:%x %x\n",v[1][0],v[1][1]);
getflag(v[1]);
decrypt(v[2], k);
printf("解密后的数据:%x %x\n",v[2][0],v[2][1]);
getflag(v[2]);
return 0;
}
最后也找到了wp
,看看人家,凭感觉就觉得是tea
算法
贴一下代码:
from pwn import *
A=[0x99, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0xbd, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00,
0x87, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00,
0x22, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00,
0xf6, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x8c, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0xd6, 0x00, 0x00, 0x00,
0x13, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00,
0xd8, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xe3, 0x00, 0x00, 0x00]
def decrypt(v,k):
v0=v[0]
v1=v[1]
asum=0xC6EF3720
delta=0x9e3779b9
k0=k[0]
k1=k[1]
k2=k[2]
k3=k[3]
for i in range(32) :
v1 -= ((v0<<3) ^ k2) ^ (v0 + asum) ^ ((v0>>5) + k3)
v1 %=(2**32)
v0 -= ((v1<<3) ^ k0) ^ (v1 + asum) ^ ((v1>>5) + k1)
v0 %=(2**32)
asum -= delta
asum %= (2**32)
return v0,v1
cipher=[A[i] for i in range(0,len(A),4)]
cipher="".join(map(chr,cipher))
key="webasmintersting"
k=[u32(key[i:i+4]) for i in range(0,len(key),4)]
flag=""
for i in range(3):
vv=cipher[i*8:(i+1)*8][::-1]
v=[u32(vv[i:i+4]) for i in range(0,len(vv),4)]
a,b=decrypt(v,k)
flag+=p32(a)+p32(b)
print flag
# *ctf{web4ss3mbly_1s_god}
vm
逆虚拟机是真的难,即使把bytecode
都写出来了,无奈实在逆不动汇编,,这里就尽量把bytecode
写出来好了,剩下的事情就交给师傅们操作了。(再次膜pizza师傅ORZ~)
补充:看了一天终于看出点头绪
再次补充:
vulnvulninit:
05 01 0B mov r1 rb lea_ch((&v25)[v8], *(&v25)[v7]);
13 03 03 xor r3 r3 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
13 00 00 xor r0 r0 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
13 04 04 xor r4 r4 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
//
for i in range(0x20):
r0+=0x33 % 0x20
push(input[r0])
loop one
28 enter loop
0C 00 33 add r0 33 add((int *)(&v25)[v8], v7); r0 +=0x33 % 0x20
14 00 20 mod r0 20 mod((int *)(&v25)[v8], v7);
05 09 01 mov r9 r1 lea_ch((&v25)[v8], *(&v25)[v7]); r1 来自 rb 应该是一个数组的指针
11 09 00 add_pch r9 r0 add_pch((&v25)[v8], *(&v25)[v7]); 进行指针的增减 index
0B 0A 09 ldr_ch ra r9 ldr_ch((&v25)[v8], *(&v25)[v7]);
01 04 0A mov r4 ra mov((&v25)[v8], *(&v25)[v7]);
1B 05 04 push r5 r4 push((&v25)[v8], *(&v25)[v7]); 进行压栈 也就是进行数组赋值
0C 03 01 add r3 r1 add((int *)(&v25)[v8], v7);
24 03 20 cmp r3 20 cmpl(*(&v25)[v8], v7);
28 jl
input[0xe0] & 0xe0 >> 5
13 00 00 xor r0 r0 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]); 初始化r0
07 08 05 mov r8 r5 lea_int((&v25)[v8], *(&v25)[v7]); r5是上一个loop得出的数组 取出
0E 08 E0 add_pint r8 e0 add_pint((&v25)[v8], v7) 进行下标的增减 类似的 r5[0+0xe0]==r5[31]
07 02 08 mov r2 r8 lea_int((&v25)[v8], *(&v25)[v7]); r5 & 0xe0 >> 5
09 0A 02 ldr_int ra r2 ldr_int((&v25)[v8], *(&v25)[v7]);
01 00 0A mov r0 ra mov((&v25)[v8], *(&v25)[v7]);
18 00 E0 and r0 e0 my_and((&v25)[v8], v7);
1E 00 05 shr r0 5 shr((&v25)[v8], v7);
01 04 00 mov r4 r0 mov((&v25)[v8], *(&v25)[v7]); 结果在r4上 通过查看r4 的交叉引用 可以看到r4 是什么
13 03 03 xor r3 r3 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
for i in range(0x1f):
push r0 & 0x1f << 3
push r0 & 0xe0 >> 5
loop two
28 entry loop
09 0A 02 ldr_int ra r2 ldr_int((int *)(&v25)[v8], (int *)*(&v25)[v7]);
01 00 0A mov r0 ra mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7
18 00 1F and r0 1f my_and((int *)(&v25)[v8], v7); r0 & 0x1f << 3
20 00 03 shl r0 3 shl((int *)(&v25)[v8], v7);
1B 05 00 push r5 r0 push((int **)(&v25)[v8], (int)*(&v25)[v7]); 将结果压栈
07 08 05 mov r8 r5 lea_int((int **)(&v25)[v8], (int *)*(&v25)[v7])
0E 08 E0 add_point r8 e0 add_pint((int **)(&v25)[v8], v7); 将数组指针的下标加 0xe0=224 ==> 256-224 = 32
07 02 08 mov r2 r8 lea_int((int **)(&v25)[v8], (int *)*(&v25)[v7]
09 0A 02 ldr_int ra r2 ldr_int((int *)(&v25)[v8], (int *)*(&v25)[v7]); r0 & 0xe0 >> 5
01 00 0A mov r0 ra mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7
18 00 E0 and r0 e0 my_and((int *)(&v25)[v8], v7)
1E 00 05 shr r0 5 shr((int *)(&v25)[v8], v7);
1D 05 0A pop r5 ra pop((int **)(&v25)[v8], (int *)(&v25)[v7]); pop出ra 也就是数组的下一个 stack 是FIFO的
0D 0A 00 add ra r0 add((int *)(&v25)[v8], (int)*(&v25)[v7]); 将上一步得出的结果 add 到ra中 并压栈
1B 05 0A push r5 ra push((int **)(&v25)[v8], (int)*(&v25)[v7]);
0C 03 01 add r3 1 add((int *)(&v25)[v8], v7);
24 03 1F cmp r3 1f cmpl((int)*(&v25)[v8], v7);
28 jl
r0 & 0x1f >> 3 + r4
09 0A 02 ldr_int ra r2 ldr_int((int *)(&v25)[v8], (int *)*(&v25)[v7]); 获取数组指针
01 00 0A mov r0 ra mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7
18 00 1F and r0 1f my_and((int *)(&v25)[v8], v7) r0 & 0x1f >> 3
20 00 03 shl r0 3 shl((int *)(&v25)[v8], v7);
0D 00 04 add r0 r4 add((int *)(&v25)[v8], (int)*(&v25)[v7]); 加上r4
1B 05 00 push r5 r0 push((int **)(&v25)[v8], (int)*(&v25)[v7]);
13 03 03 xor r3 r3 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
03 04 0D mov32 r4 rd mov32((&v25)[v8], *(&v25)[v7]); 这一行应该是将 固定数组 给取出 mov32 个字节
for i in range(0x20):
ror((r0+i)^ra,8)
这里关键进行了循环xor操作
loop three
28 entry loop
07 08 05 lea_int r8 r5 lea_int((int **)(&v25)[v8], (int *)*(&v25)[v7]) r5是将以上的两个结果相加之后获得的数组指针
0E 08 E0 add_pint r8 e0 add_pint((int **)(&v25)[v8], v7); 将r8的指针偏移加0xe0 可以认为是
07 02 08 lea_int r2 r8 lea_int((int **)(&v25)[v8], (int *)*(&v25)[v7]
09 0A 02 ldr_int ra r2 ldr_int((int *)(&v25)[v8], (int *)*(&v25)[v7]);
01 00 0A mov r0 ra mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7
1B 05 00 push r5 r0 push((int **)(&v25)[v8], (int)*(&v25)[v7]); 压栈
01 00 04 mov r0 r4 mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7 mov的实现 是移动了1个字节 也就是相当于 取出了 r4 的第8位
0D 00 03 add r0 r3 add((int *)(&v25)[v8], (int)*(&v25)[v7]); r3是索引 index
1D 05 0A pop r5 ra pop((int **)(&v25)[v8], (int *)(&v25)[v7]); 取出r5
13 0A 00 xor ra r0 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]); ror((r0+index)^ra,8) 将结果扩展为8位
1B 05 0A push r5 ra push((int **)(&v25)[v8], (int)*(&v25)[v7]);
22 04 08 ror r4 8 ror((&v25)[v8], v7); 这是什么操作?? 自己用c写了测试代码 好像是进行符号扩展 说的不是很准确 因为我测试的数据太小了
0C 03 01 add r3 1 add((int *)(&v25)[v8], v7);
24 03 20 cmp r3 20 cmpl((int)*(&v25)[v8], v7);
28 jl
初始化
13 03 03 xor r3 r3 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
13 04 04 xor r4 r4 my_xor((int *)(&v25)[v8], (int)*(&v25)[v7]);
05 01 0C lea_ch r1 rc lea_ch((&v25)[v8], *(&v25)[v7]);
loop four
28 entry loop
05 09 01 lea_ch r9 r1 lea_ch((&v25)[v8], *(&v25)[v7])
11 09 03 add_pch r9 r3 add_pch((&v25)[v8], *(&v25)[v7]); r3 是index
0B 0A 09 ldr_ch ra r9 ldr_ch((&v25)[v8], *(&v25)[v7]);
01 00 0A mov r0 ra mov((int *)(&v25)[v8], (unsigned int)*(&v25)[v7
1B 05 00 push r5 r0 push((int **)(&v25)[v8], (int)*(&v25)[v7]); 这是最后一个循环那么基本上可以确定rc就是需要比对的正确字符
07 08 05 lea_int r8 r5 lea_int((int **)(&v25)[v8], (int *)*(&v25)[v7])
0E 08 DF add_pint r8 df add_pint((int **)(&v25)[v8], v7); 变化下标
09 0A 08 ldr_int ra r8 ldr_int((int *)(&v25)[v8], (int *)*(&v25)[v7]);
1D 05 00 pop r5 r0 pop((int **)(&v25)[v8], (int *)(&v25)[v7]);
1B 05 00 push r5 r0 push((int **)(&v25)[v8], (int)*(&v25)[v7]);
27 00 0A cmpeq r0 ra cmpeq(*(&v25)[v8], *(&v25)[v7]);
17 04 07 or r4 r7 my_or((&v25)[v8], *(&v25)[v7]); or操作
0C 03 01 add r3 1 add((int *)(&v25)[v8], v7);
24 03 20 cmp r3 20 cmpl((int)*(&v25)[v8], v7);
28 jl
2A is right?
key = 0xEFBEADDE
flag 的长度为0x20 32
cmp=[0x75, 0x85, 0xD1, 0x39, 0x0B, 0x29, 0xCD, 0x77, 0x6D, 0x9F, 0x73, 0x23, 0x61, 0x8B, 0x4D, 0x45, 0x9D, 0x8F, 0x5B, 0x11, 0xC1, 0xC9, 0xE5, 0xCF, 0x45, 0xE5, 0xB1, 0xB3, 0x41, 0xD9, 0xCF, 0xCF]
我在做的时候主要是((a & 0xE0) >> 5) , ((b & 0x1F) << 3)
这两个处理没看懂,其实就是0xE0 = 0b11100000 0x1F = 0b00011111
相当于是取出了一个字节的前5位和后三位,然后重新拼接成一个8位的数。
在分析代码的过程中,会遇到一些不明来历的变量,这些一般就是程序中固定的数据,key是在ida中找到的,小端序。
加密过程如下(以下参考pizza的代码):
secret = [0x75, 0x85, 0xD1, 0x39, 0x0B, 0x29, 0xCD, 0x77, 0x6D, 0x9F, 0x73, 0x23, 0x61, 0x8B, 0x4D,
0x45, 0x9D, 0x8F, 0x5B, 0x11, 0xC1, 0xC9, 0xE5, 0xCF, 0x45, 0xE5, 0xB1, 0xB3, 0x41, 0xD9, 0xCF, 0xCF]
input = [ord(i) for i in 'xman{ae791f19bdf77357ff10bb6b0e}']
key = [0xDE, 0xAD, 0xBE, 0xEF]
y = [i for i in range(0x20)]
x = [i for i in range(0x20)]
z = [i for i in range(0x20)]
k = 0
for i in range(0x20):
k += 0x33
k = k%0x20
y[i] = input[k]
def fn(a,b):
return ((a & 0xE0) >> 5) | ((b & 0x1F) << 3)
for i in range(0x20):
x[i]=fn(y[(i+1)%0x20],y[i%0x20])
for i in range(0x20):
z[i] = x[i]^((key[i%4]+i)&0xff)
assert(z==secret)
print 'ok'
对我来说主要是fn
比较难处理。
def fn(a,b):
return ((a & 0xE0) >> 5) | ((b & 0x1F) << 3)
不过如果从位的角度去考虑,就不会那么难。。。
贴上pizza师傅的脚本。
secret = [0x75, 0x85, 0xD1, 0x39, 0x0B, 0x29, 0xCD, 0x77, 0x6D, 0x9F, 0x73, 0x23, 0x61, 0x8B, 0x4D, 0x45, 0x9D, 0x8F, 0x5B, 0x11, 0xC1, 0xC9, 0xE5, 0xCF, 0x45, 0xE5, 0xB1, 0xB3, 0x41, 0xD9, 0xCF, 0xCF]
key = [0xDE, 0xAD, 0xBE, 0xEF]
print len(secret)
x = [secret[i] ^ ((key[i % 4] + i) & 0xFF) for i in xrange(0, 32)]
# --------------------------
y = [0 for i in xrange(0, 32)]
def fn(a, b):
return ((a << 5) & 0xE0) | ((b >> 3) & 0x1F)
for i in xrange(1, 32):
y[i] = fn(x[i-1], x[i])
y[0] = fn(x[31], x[0])
# --------------------------
print y
z = [0 for i in xrange(0, 32)]
k = 0
for i in xrange(0, 32):
k += 0x33
print k % 0x20
z[k % 0x20] = y[i]
flag = ""
for c in z:
flag += chr(c)
print flag
自己用c
也写了一个
#include<stdio.h>
#include<stdint.h>
void vuln(){
char sec[32]={0x75, 0x85, 0xD1, 0x39, 0x0B, 0x29, 0xCD, 0x77, 0x6D, 0x9F, 0x73, 0x23, 0x61, 0x8B, 0x4D, 0x45, 0x9D, 0x8F, 0x5B, 0x11, 0xC1, 0xC9, 0xE5, 0xCF, 0x45, 0xE5, 0xB1, 0xB3, 0x41, 0xD9, 0xCF, 0xCF};
char key[4]={0xde,0xad,0xbe,0xef};
int i=0;
uint8_t a[32];
uint8_t b[32];
uint8_t c[32];
int k=0;
for(i=0;i<32;i++){
a[i]=sec[i]^((key[i%4]+i)&0xff);
// printf("0x%02x\n",a[i]);
}
for(i=1;i<32;i++){
b[i]=(((a[i-1]<<5)&0xe0) | ((a[i]>>3)&0x1f));
// printf("0x%02x\n",b[i]);
}
b[0]=((a[31]<<5)&0xe0) | ((a[0]>>3)&0x1f);
for(i=0;i<32;i++){
k+=0x33;
c[k%0x20]=b[i];
}
for(i=0;i<32;i++){
printf("%c",c[i]);
}
}
int main(void){
vuln();
return 0;
}
本来想拿来丢到IDA里看下的,不过收获不是很多,gcc
在编译时做了一些优化。
另外再贴上一份ror.c
的代码:
#include<stdio.h>
#include<stdint.h>
int ror(int *a1, int a2)
{
char v2; // bh
signed int v3; // esi
signed int v4; // eax
int v5; // edx
int result; // eax
printf("a1 %d\n",*a1);
v2 = 32 - a2 % 32;
v3 = 1 << (32 - a2 % 32);
v4 = 1 << a2 % 32;
if ( a2 % 32 & 0x20 )
v4 = 0;
v5 = ((v4 - 1) & *a1) << v2;
result = 0;
if ( v2 & 0x20 )
v3 = 0;
if ( v2 & 0x20 )
v5 = 0;
*a1 = v5 + ((v3 - 1) & (*a1 >> a2 % 32));
printf("v5 %x\n",v5);
printf("a1 %x\n",*a1);
printf("a2 %d\n",a2);
return result;
}
int main(void){
int a1=30;
int a2=8;
ror(&a1,a2);
return 0;
}
Wo。。太菜了,没什么捷径就是头铁!-。-
话说这两题的题目都是easy-
,我真的是太菜了啊啊啊~~~
分析都是瞎写的随便看看就好,主要是想膜一下pizza师傅
总结
我好菜啊!