-
-
[原创]kctf2024题目提交-智慧中心登录器
-
发表于: 2024-6-13 11:59 1646
-
公开的用户名/密码:
Username: 5D39642871C061E7
key: 9FF4D2D192B6081530E2C792605EBC939763C5681D4E2AF9366880D4A15B0553
题目答案:
Username: KCTF
Key: 901AB07A40D87A7B96ED4B0420EA2B552C9E12C71F9E967E5BEA37A83BC79E1F
题目名称:智慧中心登录器.
题目背景:大概是我高中的时候经常偷玩电脑,被智慧中心抓了好几次.今天我搞到了他们的登录程序,看看能不能登录学校官网后台.
我混淆器的思路是,准备一个context栈,每次调用指令的时候进入虚拟机,把栈迁移至虚拟空间.调用指令后,退出虚拟机,退出前,将下一条指令的key压入栈,在dispatcher函数中解密下一条指令的opcode,跳往执行下一条指令.进入函数第一时间保存所有寄存器,依照x64调用约定拷贝参数,全部寄存器入context栈,退出时恢复到上一个函数的context.对于jmp使用直接修改opcode,call则不进入虚拟机,执行完毕第一时间保存返回值并返回到dispatcher,handler象征性地加了一条花指令,每个handler在程序中都分配一块128字节的空间,handler指令的开始位置在空间里是随机的,随机后剩余空间填满一些假的指令bytes.就这么简单.明年上大学我会加入一些表达式混淆和api混淆.(这版本像个sb一样)
不算难解.之前比赛看到一题类似思路的,他也是一个tea算法,但是他没有经过dispatcher分发指令.
我给出一种可能解法:
这是保存寄存器至context.
这是计算函数ctx的地址并压入第一条指令的密钥.
拷贝参数,如果这是第一次进入vm,则初始化虚拟栈和ctx栈.
进入dispatcher.
dispatcher代码(汇编和c++混编):
解析版:
进入vm:
退出vm.
恢复符号位,执行原指令,备份符号位,push密钥,再跳往退出虚拟机.
一条一条指令跑下来,就可以恢复原程序(除了jmp和call),unicorn啥的都行.(菜鸡没搞指令替换,上大学再搞).恢复源程序后,分块解密32轮tea,每块解密两次即可(tea的key来源于sum,参数也有变,不过大同小异),解密脚本见附件.也可以硬看,不难.知道看雪巨佬连真正的vmp都能手撕,我这个小虾米又算啥..
keygen脚本:
#include<iostream>
#include<windows.h>
#define getpos(X,N) (((X)>>((N)<<3))&0xFF)
const char
*
zx
=
"welcome_to_fzbz,my_name_is_sbzx!"
;
const char
*
sb
=
"0123456789ABCDEF"
;
void gen(BYTE
*
N,BYTE
*
O)
{
BYTE usn[
17
]
=
{
0
};
for
(
int
i
=
0
; i <
16
; i
+
+
)
{
usn[i]
=
N[i] ^ zx[i];
}
BYTE out[
0x100
]
=
{
0
};
for
(
int
s
=
0
; s <
32
; s
+
=
8
)
{
DWORD
sum
=
*
(DWORD
*
)(usn
+
8
);
DWORD delta
=
*
(DWORD
*
)(usn
+
12
);
DWORD v0, v1;
v0
=
*
(DWORD
*
)(zx
+
s);
v1
=
*
(DWORD
*
)(zx
+
s
+
4
);
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
sum
+
=
delta;
}
v0 ^
=
sum
;
v1 ^
=
sum
;
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
v1
-
=
((v0 <<
4
)
+
getpos(
sum
,
2
)) ^ (v0
+
sum
) ^ ((v0 >>
5
)
+
getpos(
sum
,
1
));
sum
-
=
delta;
v0
-
=
((v1 <<
4
)
+
getpos(
sum
,
3
)) ^ (v1
+
sum
) ^ ((v1 >>
5
)
+
getpos(
sum
,
0
));
}
sum
=
*
(DWORD
*
)usn;
delta
=
*
(DWORD
*
)(usn
+
4
);
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
sum
+
=
delta;
}
v0 ^
=
sum
;
v1 ^
=
sum
;
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
v1
-
=
((v0 <<
6
)
+
getpos(
sum
,
3
)) ^ (v0
+
sum
) ^ ((v0 >>
3
)
+
getpos(
sum
,
2
));
sum
-
=
delta;
v0
-
=
((v1 <<
6
)
+
getpos(
sum
,
0
)) ^ (v1
+
sum
) ^ ((v1 >>
3
)
+
getpos(
sum
,
1
));
}
*
(DWORD
*
)(out
+
s)
=
v0;
*
(DWORD
*
)(out
+
s
+
4
)
=
v1;
}
for
(
int
i
=
0
; i <
32
; i
+
+
)
{
BYTE M
=
out[i];
O[i
*
2
]
=
sb[M >>
4
];
O[i
*
2
+
1
]
=
sb[M &
0xf
];
}
}
/
/
keygen
int
main()
{
char out[
0x100
]
=
{
0
};
const char
*
username
=
"KCTF"
;
char h[
17
]
=
{
0
};
strcpy((char
*
)h, username);
gen((BYTE
*
)h, (BYTE
*
)out);
std::cout << out;
}
#include<iostream>
#include<windows.h>
#define getpos(X,N) (((X)>>((N)<<3))&0xFF)
const char
*
zx
=
"welcome_to_fzbz,my_name_is_sbzx!"
;
const char
*
sb
=
"0123456789ABCDEF"
;
void gen(BYTE
*
N,BYTE
*
O)
{
BYTE usn[
17
]
=
{
0
};
for
(
int
i
=
0
; i <
16
; i
+
+
)
{
usn[i]
=
N[i] ^ zx[i];
}
BYTE out[
0x100
]
=
{
0
};
for
(
int
s
=
0
; s <
32
; s
+
=
8
)
{
DWORD
sum
=
*
(DWORD
*
)(usn
+
8
);
DWORD delta
=
*
(DWORD
*
)(usn
+
12
);
DWORD v0, v1;
v0
=
*
(DWORD
*
)(zx
+
s);
v1
=
*
(DWORD
*
)(zx
+
s
+
4
);
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
sum
+
=
delta;
}
v0 ^
=
sum
;
v1 ^
=
sum
;
for
(
int
i
=
0
; i <
31
; i
+
+
)
{
v1
-
=
((v0 <<
4
)
+
getpos(
sum
,
2
)) ^ (v0
+
sum
) ^ ((v0 >>
5
)
+
getpos(
sum
,
1
));
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!