2020-GKCTF
Check_1n
思路一:
查找字符串之后,点击"密码错误"发现了关键,上方的一个strcmp函数,发现了密码:
‘HelloWorld’
我们直接运行一下,然后输入密码开机,之后移动到"flag",结果给了base64编码的Why don’t you try the magic brick game
然后我们玩打砖块游戏,死了之后就出flag
思路二:
查找字符串的时候发现:
2i9Q8AtFJTfL3ahU2XGuemEqZJ2ensozjg1EjPJwCHy4RY1Nyvn1ZE1bZe
点过去之后交叉引用发现了base58的table,直接base58解密得到flag
Chelly’s ldentity
加密函数:
比较函数:
至于为什么知道哪个变量是哪个,动调来看就知道了
WP:
# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
arr = [0x00000002, 0x00000003, 0x00000005, 0x00000007, 0x0000000B, 0x0000000D, 0x00000011, 0x00000013, 0x00000017, 0x0000001D, 0x0000001F, 0x00000025, 0x00000029, 0x0000002B, 0x0000002F, 0x00000035, 0x0000003B, 0x0000003D, 0x00000043, 0x00000047, 0x00000049, 0x0000004F, 0x00000053, 0x00000059, 0x00000061, 0x00000065, 0x00000067, 0x0000006B, 0x0000006D, 0x00000071, 0x0000007F]
cmp = [0x000001B6, 0x00000498, 0x00000441, 0x00000179, 0x00000179, 0x00000640, 0x0000039C, 0x00000179, 0x0000064A, 0x0000039C, 0x0000027D, 0x0000027F, 0x00000178, 0x00000236, 0x00000344, 0x0000033E]
flag = ''
for i in range(len(cmp)):
for x in range(32, 127):
su_m = 0
for j in arr:
if j < x:
su_m += j
temp = x ^ su_m
if temp == cmp[i]:
flag += chr(x)
print('flag{'+flag+'}')
flag{Che11y_1s_EG0IST}
BabyDriver
是一道很经典的键盘过滤驱动,下面几张图是经典的键盘过滤驱动:
我们主要看的就是IRP读操作的回调函数(第二个函数),也就是CompletionRoutine的位置
分析我们的题目:
找到DriverEntry函数中的sub_1400011E0函数
__int64 __fastcall sub_1400011E0(PDRIVER_OBJECT DriverObject)
{
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_140001260;// 注册驱动卸载函数
KdDisableDebugger();
memset64(DriverObject->MajorFunction, (unsigned __int64)sub_1400012B0, 27ui64);// 注册通用的IRP分发函数
DriverObject->MajorFunction[3] = (PDRIVER_DISPATCH)sub_1400014E0;// 注册读IRP分发函数
DriverObject->MajorFunction[22] = (PDRIVER_DISPATCH)&sub_140001340;// 注册电源IRP分发函数
DriverObject->MajorFunction[27] = (PDRIVER_DISPATCH)sub_1400012D0;// 注册即插即用IRP分发函数
AttachDevice(DriverObject); // 绑定设备
return 0i64;
}
点击读IRP分发函数
NTSTATUS __fastcall sub_1400014E0(_QWORD *a1, IRP *a2)
{
NTSTATUS result; // eax
_DEVICE_OBJECT *v5; // rcx
_IO_STACK_LOCATION *v6; // rax
_IO_STACK_LOCATION *v7; // rax
if ( a2->CurrentLocation == 1 )
{
a2->IoStatus.Information = 0i64;
a2->IoStatus.Status = 0xC0000010;
IofCompleteRequest(a2, 0);
result = 0xC0000010;
}
else
{
v5 = *(_DEVICE_OBJECT **)(a1[8] + 24i64);
v6 = a2->Tail.Overlay.CurrentStackLocation;
*(_OWORD *)&v6[-1].MajorFunction = *(_OWORD *)&v6->MajorFunction;
*(_OWORD *)&v6[-1].Parameters.NotifyDirectoryEx.CompletionFilter = *(_OWORD *)&v6->Parameters.NotifyDirectoryEx.CompletionFilter;
*(_OWORD *)(&v6[-1].Parameters.SetQuota + 6) = *(_OWORD *)(&v6->Parameters.SetQuota + 6);
v6[-1].FileObject = v6->FileObject;
v6[-1].Control = 0;
v7 = a2->Tail.Overlay.CurrentStackLocation;
v7[-1].Context = a1;
v7[-1].CompletionRoutine = (PIO_COMPLETION_ROUTINE)sub_140001380;// 键盘过滤驱动,主要看下IRP读操作的回调函数(第二个函数),也就是CompletionRoutine的位置
v7[-1].Control = 0xE0;
result = IofCallDriver(v5, a2);
}
return result;
}
在其中找到CompletionRoutine对应的函数sub_140001380
在其中发现了迷宫
__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
__int64 v3; // rdi
__int64 v4; // rax
int direction; // ecx
__int16 *v6; // rsi
__int64 v7; // rbp
__int16 v8; // dx
char v9; // dl
const CHAR *v10; // rcx
if ( *(int *)(a2 + 48) >= 0 )
{
v3 = *(_QWORD *)(a2 + 24);
v4 = *(_QWORD *)(a2 + 56) >> 3;
if ( (_DWORD)v4 )
{
direction = dword_1400030E4; // 初值为16
v6 = (__int16 *)(v3 + 2);
v7 = (unsigned int)v4;
while ( *(_WORD *)(v3 + 4) )
{
LABEL_28:
v6 += 6;
if ( !--v7 )
goto LABEL_29;
}
aO[direction] = '.';
v8 = *v6;
if ( *v6 == 23 )
{
if ( (direction & 0xFFFFFFF0) != 0 )
{
direction -= 16; // 上移
goto LABEL_21;
}
direction += 0xD0;
dword_1400030E4 = direction;
}
if ( v8 == 37 )
{
if ( (direction & 0xFFFFFFF0) != 0xD0 )
{
direction += 16; // 下移,+16代表下移一行
goto LABEL_21;
}
direction -= 208;
dword_1400030E4 = direction;
}
if ( v8 == 36 )
{
if ( (direction & 0xF) != 0 )
{
--direction; // 左移
goto LABEL_21;
}
direction += 15;
dword_1400030E4 = direction;
}
if ( v8 != 38 )
goto final;
if ( (direction & 0xF) == 15 )
direction -= 15;
else
++direction; // 右移
LABEL_21:
dword_1400030E4 = direction;
final:
v9 = aO[direction];
if ( v9 == '*' ) // 撞墙
{
v10 = "failed!\n";
}
else
{
if ( v9 != '#' ) // 没走到目标'#'
{
LABEL_27:
aO[direction] = 'o'; // 就回到起点
goto LABEL_28;
}
v10 = "success! flag is flag{md5(input)}\n";
}
dword_1400030E4 = 16;
DbgPrint(v10);
direction = dword_1400030E4;
goto LABEL_27;
}
}
LABEL_29:
if ( *(_BYTE *)(a2 + 'A') )
*(_BYTE *)(*(_QWORD *)(a2 + 0xB8) + 3i64) |= 1u;
return *(unsigned int *)(a2 + '0');
}
导出迷宫并走出路线:
# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
maze = "****************o.*..*......*..**.**...**.*.*.***.****.**.*.*.***...**....*.*.*****..***.**.*..**.**.***.**.**.**.**.******.**.**.**....***.**.**.*****.***....**...***.**********..***......#****.*****************************"
for i in range(len(maze)):
if (i+1) % 16 == 0:
print(maze[i])
else:
if i != 0:
print(maze[i], end=' ')
else:
print(maze[i], end=' ')
import hashlib
line = "LKKKLLKLKKKLLLKKKLLLLLL"
flag = hashlib.md5(line.encode("utf-8")).hexdigest().lower()
print('flag{'+flag+'}')
flag{403950a6f64f7fc4b655dea696997851}
DbglsFun
打开之后发现了TLS回调函数,会在主线程开始的时候会调用,TLS回调函数的调用要先于EP代码的执行
在其中发现了smc自解密代码:
void __stdcall TlsCallback_0(int a1, int a2, int a3)
{
int i; // eax
DWORD flOldProtect; // [esp+0h] [ebp-8h] BYREF
if ( a2 == 1 )
{
VirtualProtect(sub_4010F0, 0x42Eu, 0x40u, &flOldProtect);
for ( i = 0; i < 1070; ++i )
*((_BYTE *)sub_4010F0 + i) ^= i;
CreateThread(0, 0, sub_4010F0, 0, 0, 0);
}
}
我们静态的话就写个脚本跑一下这个smc自解密:
addr = 0x004010F0
for i in range(1070):
ida_bytes.patch_byte((addr+i), idc.get_wide_byte(addr+i) ^ i)
然后将地址004010F0开始的重新定义为一个函数
很明显的能看出来是先将我们的flag和一个值异或,然后再进行了RC4
最后再进行比较
写出脚本:
/* 注意事项
* 1.明文密文还有状态数组s[i](0-255)的类型应都选择unsigned char类型,因为其取值范围为0-255,一字节
* 2.明文密文以及状态数组s[i]的类型应该全部一样,这样做异或运算时就不会发生内存溢出的问题
*/
#include <stdio.h>
#include <string.h>
void rc4_init(unsigned char*s,unsigned char*key,unsigned long len)
{
int i=0;
int j=0;
unsigned char k[256]={};
unsigned char temp = 0;
for(i=0;i<256;i++)
{
s[i]=i; //0-255赋给s
k[i]=key[i%len]; //将k重新计算
}
for(i=0;i<256;i++)
{
j=(j+s[i]+k[i])%256; //给j赋值
temp=s[i];
s[i]=s[j];
s[j]=temp; //s[i]和s[j]交换
}
}
void rc4_crypt(unsigned char*s,unsigned char*data,unsigned long len)
{
int i=0,j=0,t=0;
unsigned long k=0;
unsigned char temp;
for(k=0;k<len;k++)
{
i=(i+1)%256; //固定方式生成的i
j=(j+s[i])%256; //固定方式生成的j
temp=s[i];
s[i]=s[j];
s[j]=temp; //交换s[i]和s[j]
t=(s[i]+s[j])%256; //固定方式生成的t
data[k]^=s[t]; //来作为s的下标和data进行异或运算
}
}
int main()
{
unsigned char s[256]={0};
char key[256] = "GKCTF";
unsigned char data[512]={0x2D, 0xD4, 0x0F, 0xD0, 0x54, 0xEE, 0x75, 0xD0, 0xE0, 0x30, 0x96, 0xE1, 0x79, 0x8A, 0xE0, 0xFE, 0x18, 0x3A, 0x27, 0xE7, 0x2F, 0x86, 0xC9, 0xFE, 0x66, 0x43, 0xA7, 0x75};
unsigned long data_len = 28;
unsigned long key_len = strlen(key);
rc4_init(s,(unsigned char*)key,key_len);//初始化得到s
rc4_crypt(s,(unsigned char*)data,data_len);//解密
//printf("解密后为:%s",(unsigned char*)data);
for(int i=0;i<28;i++)
{
printf("%c",(data[i]^0xC9)&0xff);
}
return 0;
}
flag{5tay4wayFr0m8reakp0int}
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.
EzMachine
写过这个的WP,是一道非常经典的虚拟机,拿来入门很不错的
https://bbs.pediy.com/thread-267670.htm
WannaReverse
题目解压之后得到四个文件,点击clickme.exe会更改壁纸,一个模拟病毒
然后将WannaReverse.exe拖入ida进行分析:
char v9; // [esp-4h] [ebp-40h]
__int128 v10[2]; // [esp+8h] [ebp-34h] BYREF
char v11; // [esp+28h] [ebp-14h]
char string[12]; // [esp+2Ch] [ebp-10h] BYREF
v10[0] = 0i64;
v10[1] = 0i64;
WinExec("clickme.exe", 1u);
strcpy(string, "0123456789");
v3 = _time64(0);
srand(v3);
for ( i = 0; i < 32; ++i )
*((_BYTE *)v10 + i) = string[rand() % 10]; // rand() % 10
// [0-10)取随机数
v9 = v5;
v11 = 0;
v6 = encrypt(v5, v10); // 随机生成的长度为32的v9
v7 = "encrypto success!";
if ( !v6 )
v7 = "encrypto false!";
printf((int)v7, v9);
return 0;
}
它先运行了一下clickme.exe,然后在[0-10)取随机数用来当作下标,生成一个随机的长度为32的字符串(后面可以知道这个就是我们AES的密钥)
进入encrypt函数分析:
首先打开了flag.txt
然后中间一截就是用AES加密目标文件flag.txt之后加了个后缀.Encry
然后将使用RSA加密后的AES密钥放在了文件flag.txt.Encry
这就是那个flag.txt.Encry的由来
解题思路:
在WannaReverse.exe中找到RSA公钥:
然后在clickme.exe中找到私钥:
在线解密RSA
得到:30776159143604297789676442413079
然后写个脚本将flag.txt.Encry的剩下部分进行AES解密即可
# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
from Crypto.Cipher import AES
mode = AES.MODE_ECB # 规定是ECB的加密模式
key = b"30776159143604297789676442413079"
cryptos = AES.new(key, mode) # 传入key形成加解密函数
en_flag = [
0x5C, 0xBC, 0xEA, 0x89, 0xBA, 0x2B, 0x18, 0x27, 0x79,
0x3F, 0x13, 0x0A, 0x8A, 0x97, 0xB4, 0x9B, 0xCD, 0x78,
0x9B, 0xD8, 0x35, 0x92, 0x05, 0x45, 0x4C, 0x22, 0xA5,
0x69, 0x37, 0xEB, 0x6E, 0x2B, 0x0E, 0xBD, 0x84, 0x0F,
0x91, 0x61, 0x38, 0xF6, 0xF1, 0xBA, 0x99, 0x19, 0x41,
0x72, 0x07, 0x91, 0xF0, 0x26, 0x68, 0x06, 0x61, 0x26,
0x5C, 0x20, 0x35, 0xDD, 0xCF, 0xFC, 0x77, 0x57, 0x54,
0x81, 0xF2, 0xF2, 0xE4, 0xAF, 0xBF, 0xA2, 0x1D, 0x29,
0xAE, 0x6C, 0x08, 0x3B, 0x76, 0x1B, 0x66, 0xB8, 0xFE,
0x72, 0xCB, 0xD6, 0x94, 0xC3, 0xD5, 0x6A, 0xE7, 0x0C,
0x7A, 0x28, 0xDC, 0xBC, 0xAC, 0x80]
bytes_flag = cryptos.decrypt(bytes(en_flag)) # 直接进行解密
print(bytes_flag)
# b'\xff\xfef\x00l\x00a\x00g\x00{\x003\x008\x005\x00f\x00a\x008\x006\x009\x00-\x003\x000\x004\x006\x00-\x004\x004\x00e\x00e\x00-\x009\x00d\x003\x000\x00-\x00c\x000\x003\x005\x005\x001\x002\x007\x003\x008\x006\x007\x00}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# flag{385fa869-3046-44ee-9d30-c03551273867}