第九周
0x00 Hacking with Google: Beginner
做了一半卡住了,偷看了一下官方题解,然后复现一下
ida很容易定位到主函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // er12
__m128i v5; // [rsp+0h] [rbp-38h] BYREF
__m128i s2[2]; // [rsp+10h] [rbp-28h] BYREF
printf("Flag: ");
__isoc99_scanf("%15s", &v5);
s2[0] = _mm_xor_si128(
_mm_add_epi32(_mm_shuffle_epi8(_mm_load_si128(&v5), (__m128i)SHUFFLE), (__m128i)ADD32),
(__m128i)XOR);
if ( !strncmp(v5.m128i_i8, (const char *)s2, 0x10uLL) && (v3 = strncmp((const char *)s2, EXPECTED_PREFIX, 4uLL)) == 0 )
{
puts("SUCCESS");
}
else
{
v3 = 1;
puts("FAILURE");
}
return v3;
}
|
可以看到各种SSE指令
这里要注意add是把每四字节打包(pack)然后进行求和,而异或是逐字节进行异或
_mm_shuffle_epi8 无法见名知义,官方描述加查找各种解释终于搞懂,见注释
1
2
3
4
5
6
7
8
9
|
FOR j := 0 to 15 //遍历16个字节
i := j*8 //遍历每个字节中的8个bit
IF b[i+7] == 1 //当前字节最高位为1
dst[i+7:i] := 0 //整个字节8bit全置为0
ELSE
index[3:0] := b[i+3:i] //相当于以int形式取出b中的值存入index
dst[i+7:i] := a[index*8+7:index*8] //把a数组下标为index[i]的值放入dst数组
FI
ENDFOR
|
由于可见字符为32-126,不可能最高位为1,所以全置为0的情况不会发生
即shuffle就是把原数组a按b的顺序重新排列了一遍,其中值没有改变
题目验证逻辑
flag->shuffle->add->xor == flag
&& flag前四位为CTF{
(这里注意输入的第16位是字符串结束标志\0
显然我们可以根据前四位推出一些东西
从后往前推
1
2
3
4
5
6
7
8
9
10
|
flag 0x43 0x54 0x46 0x7b
xornum 0x76 0x58 0xB4 0x49
xor_pre 0x35 0x0c 0xf2 0x32
xor_pre 0x32f20c35
addnum 0x0DEADBEEF
add_pre 0x54444d46
add_pre 0x46 0x4d 0x44 0x54
shufflenum 2 6 7 1
|
于是我们知道了第6、7位分别为M、D,即CTF{--MD------}\0
这时我们想到,最低位(字节)的值进行add操作时不会受其他字节的影响(不会受进位的影响
要确保已知字符是shuffle后的最低位,我们想到了\0,并且这次需要正着推
为省事写个脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include<stdio.h>
int xornum[] = {0x76, 0x58, 0xB4, 0x49, 0x8D, 0x1A, 0x5F, 0x38, 0xD4, 0x23, 0xF8, 0x34, 0xEB, 0x86, 0xF9, 0xAA};
int addnum[] = {0xEF, 0xBE, 0xAD, 0xDE, 0xAD, 0xDE, 0xE1, 0xFE, 0x37, 0x13, 0x37, 0x13, 0x66, 0x74, 0x63, 0x67};
int shufflenum[] = {2,6,7,1,5,11,9,14,3,15,4,8,10,12,13,0};
int main(){
int alpha,index,pre_index; //index是shuffle之后的下标,pre_index是shuffle之前的下标
scanf("%x%d",&alpha,&index);
for (int i = 0; i < 16; i++){
if(index==shufflenum[i])
pre_index = i;
}
unsigned char newalpha = ((alpha+addnum[pre_index]))^xornum[pre_index];
printf("0x%x %c %d",newalpha,newalpha,pre_index);
}
// input:0 15
// output:0x30 0 9
// input:0x7b 3
// output:0x66 f 8
|
于是得到了flag第九位为0,即CTF{--MD-0----}\0
同样用 { 可以得到第8位为 f,即CTF{--MDf0----}\0
但我们已经用完了所有的最低字节
我们如果使用其他字节就会面临进位的问题,但我们也不排除还有其他没有使用的,没有进位的字符
如果去考虑一遍所有的位是否存在进位问题是一个浩大的工程
所以我们的想法是碰碰运气,如果推出的字符与已知矛盾就舍弃,如果不矛盾就保留(这真是官方方法
在把所有位试过一遍之后,我们会逐渐得到flag
1
2
3
4
5
6
7
|
CTF{--MDf0-M--}\0
CTF{-1MDf0-M--}\0
CTF{S1MDf0-M--}\0
CTF{S1MDf0rM--}\0
CTF{S1MDf0rM3-}\0
CTF{S1MDf0rM3!}\0
CTF{S1MDf0rM3!}\0
|
0x01 V&N 公开赛 CSRe
die查,发现protector: Eazfuscator
查了一下,是一种.Net代码混淆方法,可以用de4dot反混淆,然后dnspy反编译
这道题需要耐心,尝试搜索flag字符串,翻了十多分钟终于找到Main
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
|
using System;
using System.Security.Cryptography;
using System.Text;
// Token: 0x02000006 RID: 6
internal sealed class Class3
{
// Token: 0x0600000D RID: 13 RVA: 0x000022C8 File Offset: 0x000004C8
public string method_0(string string_0, string string_1)
{
string text = string.Empty;
char[] array = string_0.ToCharArray();
char[] array2 = string_1.ToCharArray();
int num = (array.Length < array2.Length) ? array.Length : array2.Length;
for (int i = 0; i < num; i++)
{
text += (int)(array[i] ^ array2[i]);
}
return text;
}
// Token: 0x0600000E RID: 14 RVA: 0x0000231C File Offset: 0x0000051C
public static string smethod_0(string string_0)
{
byte[] bytes = Encoding.UTF8.GetBytes(string_0);
byte[] array = SHA1.Create().ComputeHash(bytes);
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in array)
{
stringBuilder.Append(b.ToString("X2"));
}
return stringBuilder.ToString();
}
// Token: 0x0600000F RID: 15 RVA: 0x00002374 File Offset: 0x00000574
private static void Main(string[] args)
{
if (!Class1.smethod_1())
{
return;
}
bool flag = true;
Class3 @class = new Class3();
string str = Console.ReadLine();
if (Class3.smethod_0("3" + str + "9") != "B498BFA2498E21325D1178417BEA459EB2CD28F8")
{
flag = false;
}
string text = Console.ReadLine();
string string_ = Class3.smethod_0("re" + text);
string text2 = @class.method_0(string_, "63143B6F8007B98C53CA2149822777B3566F9241");
for (int i = 0; i < text2.Length; i++)
{
if (text2[i] != '0')
{
flag = false;
}
}
if (flag)
{
Console.WriteLine("flag{" + str + text + "}");
}
}
}
|
可以看到flag由两部分组成,即str和text
class。method_0像是个加密算法,得到两串字符串
直接丢到Cmd5网站解密,原来是Sha1加密
得到"3" + str + "9"
==314159,"re" + text
==return
故flag{1415turn}
0x02 Zer0pts2020 easy-strcmp
ida载入,main函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
__int64 __fastcall main(int a1, char **a2, char **a3)
{
if ( a1 > 1 )
{
if ( !strcmp(a2[1], "zer0pts{********CENSORED********}") )
puts("Correct!");
else
puts("Wrong!");
}
else
{
printf("Usage: %s <FLAG>\n", *a2);
}
return 0LL;
}
|
醒目的strcmp和一串字符串,难道这就是flag?肯定不是
翻函数列表发现两个特别的函数
1
2
3
4
5
6
7
8
9
|
int (**sub_563477E00795())(const char *s1, const char *s2)
{
int (**result)(const char *, const char *); // rax
result = &strcmp;
qword_563478001090 = (__int64 (__fastcall *)(_QWORD, _QWORD))&strcmp;
off_563478001028 = sub_563477E006EA;
return result;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
__int64 __fastcall sub_563477E006EA(__int64 a1, __int64 a2)
{
int i; // [rsp+18h] [rbp-8h]
int v4; // [rsp+18h] [rbp-8h]
int j; // [rsp+1Ch] [rbp-4h]
for ( i = 0; *(_BYTE *)(i + a1); ++i )
;
v4 = (i >> 3) + 1;
for ( j = 0; j < v4; ++j )
*(_QWORD *)(8 * j + a1) -= qword_563478001060[j];
return qword_563478001090(a1, a2);
}
|
可以看到第一个函数sub_563477E00795记录了strcmp函数的地址,然后调用了函数sub_563477E006EA
相当于把strcmp函数给改了,实际执行的是第二个函数sub_563477E006EA
即把真正的flag加密了,然后与main函数里的字符串比较
这里注意main函数里传的是 a2[1],即跳过了zer0pts
qword_563478001060刚好32个字节非空,对应修改 * 及其中间字符共32个字节
于是写脚本拿flag
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include<stdio.h>
unsigned char key[] =
{
0x42, 0x09, 0x4A, 0x49, 0x35, 0x43, 0x0A, 0x41, 0xF0, 0x19,
0xE6, 0x0B, 0xF5, 0xF2, 0x0E, 0x0B, 0x2B, 0x28, 0x35, 0x4A,
0x06, 0x3A, 0x0A, 0x4F
};
char en_flag[]="********CENSORED********";
unsigned long long flag[5];
int main(){
for(int i=0;i<3;i++){
flag[i]=*((unsigned long long *)key+i)+*((unsigned long long *)en_flag+i);
}
for(int i=0;i<24;i++){
printf("%c",*((unsigned char *)flag+i));
}
}
//zer0pts{l3ts_m4k3_4_DETOUR_t0d4y}
|
刚开始还犯了错误,逐字节解密得到错误flag:l3ts_m4k3^4^DDSOUR_t0d4y
因为题目是将每8字节打包进行加法,其中有进位,而逐字节加法不会产生进位,所以出了问题
第八周
0x00 大佬喝茶~
偷懒没有管花指令
ida载入,进main函数
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
|
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int j; // [rsp+14h] [rbp-7Ch]
int i; // [rsp+18h] [rbp-78h]
char input[48]; // [rsp+20h] [rbp-70h] BYREF
char v7[32]; // [rsp+50h] [rbp-40h] BYREF
__int64 v8[4]; // [rsp+70h] [rbp-20h] BYREF
v8[3] = __readfsqword(0x28u);
v8[0] = 0x4837F6D54BAA4D13LL;
v8[1] = 0x203F4E88752F3489LL;
printf("Hello there. Plz Input your flag: ");
__isoc99_scanf("%40s", input);
if ( strlen(input) != 32 )
{
printf("nope\n");
exit(0);
}
for ( i = 0; i < 32; ++i )
v7[i] = input[i];
((void (__fastcall *)(__int64 *, char *, __int64))sub_1180)(v8, v7, 4LL);
for ( j = 0; j < 32; ++j )
{
if ( (unsigned __int8)v7[j] != en_flag[j] )
{
printf("nope\n");
exit(0);
}
}
printf("you are right!\n");
return 0LL;
}
|
可以看出就是把input复制给v7然后经sub_1180函数加密然后和en_flag比较
跟进sub_1180
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
|
__int64 __fastcall sub_1180(unsigned int *a1, __int64 a2, unsigned int a3)
{
unsigned int v4; // [rsp+0h] [rbp-4Ch]
unsigned int v5; // [rsp+4h] [rbp-48h]
unsigned int v6; // [rsp+8h] [rbp-44h]
unsigned int v7; // [rsp+Ch] [rbp-40h]
unsigned int j; // [rsp+14h] [rbp-38h]
unsigned int i; // [rsp+18h] [rbp-34h]
int v10; // [rsp+1Ch] [rbp-30h]
unsigned int v11; // [rsp+20h] [rbp-2Ch]
unsigned int v12; // [rsp+24h] [rbp-28h]
v7 = _byteswap_ulong(*a1);
v6 = _byteswap_ulong(a1[1]);
v5 = _byteswap_ulong(a1[2]);
v4 = _byteswap_ulong(a1[3]);
for ( i = 0; i < a3; ++i )
{
v10 = 0;
v12 = *(unsigned __int8 *)(a2 + 8 * i + 3) | (*(unsigned __int8 *)(a2 + 8 * i + 2) << 8) | (*(unsigned __int8 *)(a2 + 8 * i + 1) << 16) | (*(unsigned __int8 *)(a2 + 8 * i) << 24);
v11 = *(unsigned __int8 *)(a2 + 8 * i + 7) | (*(unsigned __int8 *)(a2 + 8 * i + 6) << 8) | (*(unsigned __int8 *)(a2 + 8 * i + 5) << 16) | (*(unsigned __int8 *)(a2 + 8 * i + 4) << 24);
for ( j = 0; j < 0x20; ++j )
{
v10 -= 1640531527;
v12 += (v6 + (v11 >> 5)) ^ (v10 + v11) ^ (v7 + 16 * v11);
v11 += (v4 + (v12 >> 5)) ^ (v10 + v12) ^ (v5 + 16 * v12);
}
*(_BYTE *)(a2 + 8 * i) = HIBYTE(v12);
*(_BYTE *)(a2 + 8 * i + 1) = BYTE2(v12);
*(_BYTE *)(a2 + 8 * i + 2) = BYTE1(v12);
*(_BYTE *)(a2 + 8 * i + 3) = v12;
*(_BYTE *)(a2 + 8 * i + 4) = HIBYTE(v11);
*(_BYTE *)(a2 + 8 * i + 5) = BYTE2(v11);
*(_BYTE *)(a2 + 8 * i + 6) = BYTE1(v11);
*(_BYTE *)(a2 + 8 * i + 7) = v11;
}
return 0LL;
}
|
能看出来就是tea加密
_byteswap_ulong这个细节需要注意,用处是把参数值以字节为单位颠倒前后顺序,也可以直接动调得到,即为key密钥
写脚本拿flag
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
|
#include<stdio.h>
unsigned char en_flag[32] ={104,16,10,183,126,253,224,41,184,177,19,193,252,91,54,195,103,70,92,25,222,185,88,154,107,135,19,206,70,106,56,107};
// int key[]={0x4BAA4D13,0x4837F6D5,0x752F3489,0x203F4E88};
unsigned int key[]={0x134daa4b,0xd5f63748,0x89342f75,0x884e3f20};
int delta =0x61C88647,v9;
unsigned char flag[100];
unsigned int v10,v11;
int main(){
for(int i=0;i<4;i++){
*((char *)&v11+3)=*(en_flag + 8 * i);
*((char *)&v11+2)=*(en_flag + 8 * i+1);
*((char *)&v11+1)=*(en_flag + 8 * i+2);
*((char *)&v11)=*(en_flag + 8 * i+3);
*((char *)&v10+3)=*(en_flag + 8 * i+4);
*((char *)&v10+2)=*(en_flag + 8 * i+5);
*((char *)&v10+1)=*(en_flag + 8 * i+6);
*((char *)&v10)=*(en_flag + 8 * i+7);
for(int j=0x20-1;j>=0;j--){
v9=(j+1)*delta*(-1);
v10 -= (key[3] + (v11 >> 5)) ^ (v9 + v11) ^ (key[2] + 16 * v11);
v11 -= (key[1] + (v10 >> 5)) ^ (v9 + v10) ^ (key[0] + 16 * v10);
}
*(flag + 8 * i) = *((char *)&v11+3);
*(flag + 8 * i + 1) = *((char *)&v11+2);
*(flag + 8 * i + 2) = *((char *)&v11+1);
*(flag + 8 * i + 3) = *((char *)&v11);
*(flag + 8 * i + 4) = *((char *)&v10+3);
*(flag + 8 * i + 5) = *((char *)&v10+2);
*(flag + 8 * i + 6) = *((char *)&v10+1);
*(flag + 8 * i + 7) = *((char *)&v10);
}
puts(flag);
}
//19d9346d-410a-441f-b14e-9a60bb05
|
0x01 N1CTF oflo
太难啦~ 根据RX的wp复现还遇到了好多问题((
ida打开只能看到start函数,点开offset main看到main函数全是红的,分析错误
400BB1 jmp那一行明显有花指令,loc_400BB1自己跳到自己,先按u取消定义,BB2那一行按c转换为code,BB1patch成nop
BB7跳转到loc_400BBF,然后胡乱操作一通就retn了
因为return以后要执行BBC这一行,所以BBC这一行应该也是花指令
按u取消定义,把BBC patch掉,剩下两行转换成code,400CBA那一行同理
400D04和上面BB1同理,一样搞掉
D14下面这几行一堆乱数据,不知道有没有用,先不管
回到main函数开头按p创建函数,成功,F5却出现报错
是sp指针出了问题,可以通过Options->General->Disassembly右边勾上stack pointer
可以明显看到loc_400BBF函数中的sp指针值出了问题
这样看来,这个函数就是为了扰乱sp存在的,直接从BB7到BD0全patch掉
同理CB5到CCE也全patch掉
但这时ida把main函数断在了CB9这一行
可以回到main函数开始按u取消定义,然后按c重新转换为code
然后终于可以快乐地F5了,main函数真容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
int i; // [rsp+4h] [rbp-23Ch]
__int64 input[4]; // [rsp+10h] [rbp-230h] BYREF
char v5[520]; // [rsp+30h] [rbp-210h] BYREF
unsigned __int64 v6; // [rsp+238h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(v5, 0, 512uLL);
input[0] = 0LL;
input[1] = 0LL;
input[2] = 0LL;
input[3] = 0LL;
if ( (unsigned int)sub_4008B9((__int64)v5) == -1 )
exit(0LL);
read(0LL, input, 19LL);
qword_602048 = (__int64)sub_400A69;
mprotect((unsigned int)main & 0xFFFFC000, 0x10uLL, 7uLL);
for ( i = 0; i <= 9; ++i )
*(_BYTE *)(qword_602048 + i) ^= *((_BYTE *)input + i % 5);
if ( (unsigned int)sub_400A69((int)v5, (__int64)input + 5, (unsigned __int16)v5) )
write(1LL, "Cong!\n", 6LL);
exit(0LL);
}
|
17行创建了一个sub_400A69函数段的指针
18行mprotect函数将sub_400A69段代码的权限改为7(即rwx)
然后20行修改了sub_400A69的代码,与input前5位(n1ctf)异或,即SMC,先写个脚本还原一下正确的代码
1
2
3
4
|
from ida_bytes import *
key = "n1ctf"
for i in range(10):
patch_byte(0x400A69+i,get_byte(0x400A69+i)^ord(key[i%5]))
|
还原之后F5还是失败,原因是有和main函数一样的花指令和扰乱sp的代码,一样处理后得到函数
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
|
__int64 __fastcall sub_400A69(__int64 a1, __int64 a2)
{
__int64 v2; // rbp
int i; // [rsp+14h] [rbp-2Ch]
char v5[14]; // [rsp+18h] [rbp-28h] BYREF
unsigned __int64 v6; // [rsp+30h] [rbp-10h]
__int64 v7; // [rsp+38h] [rbp-8h]
v7 = v2;
v6 = __readfsqword(0x28u);
v5[0] = 53;
v5[1] = 45;
v5[2] = 17;
v5[3] = 26;
v5[4] = 73;
v5[5] = 125;
v5[6] = 17;
v5[7] = 20;
qmemcpy(&v5[8], "+;>=<_", 6);
for ( i = 0; i <= 13; ++i )
{
if ( v5[i] != ((*(char *)(i + a1) + 2) ^ *(char *)(i + a2)) )
return 0LL;
}
return 1LL;
}
|
重新进main函数看一下传进来的参数,这里需要再按一下F5,让ida根据修复后的代码重新分析一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
void __fastcall __noreturn main(int a1, char **a2, char **a3)
{
int i; // [rsp+4h] [rbp-23Ch]
__int64 input[4]; // [rsp+10h] [rbp-230h] BYREF
char v5[520]; // [rsp+30h] [rbp-210h] BYREF
unsigned __int64 v6; // [rsp+238h] [rbp-8h]
v6 = __readfsqword(0x28u);
memset(v5, 0, 0x200uLL);
input[0] = 0LL;
input[1] = 0LL;
input[2] = 0LL;
input[3] = 0LL;
if ( (unsigned int)sub_4008B9((__int64)v5) == -1 )
exit(0LL);
read(0LL, input, 19LL);
qword_602048 = (__int64)sub_400A69;
mprotect((unsigned int)main & 0xFFFFC000, 16LL, 7LL);
for ( i = 0; i <= 9; ++i )
*(_BYTE *)(qword_602048 + i) ^= *((_BYTE *)input + i % 5);
if ( (unsigned int)sub_400A69((__int64)v5, (__int64)input + 5) )
write(1LL, "Cong!\n", 6LL);
exit(0LL);
}
|
现在只需求出main函数里的v5即sub_400A69里的a1,即可解出flag
sub_4008B9里面实现太复杂了,想法是动调得到v5,但这里面有ptrace反调试
又因为反调试在输入之前,所以可以先运行到输出,然后再attach上去
这里我做的时候只有root用户运行可以,普通用户会报错权限不足什么的,具体原因不清楚
attach上之后main函数点进去v5,可以看到需要的前14位就是Linux Version
最后写脚本解flag
1
2
3
4
5
6
7
8
9
10
11
|
#include<stdio.h>
char en_flag[]={53,45,17,26,73,125,17,20,'+',';','>','=','<','_'};
char v5[]="Linux Version ";
char flag[100];
int main(){
for(int i=0;i<=13;i++){
flag[i]=(v5[i]+2)^en_flag[i];
}
puts(flag);
}
// {Fam3_Is_NULL}
|
0x02 XNUCA 2020 Unravel MFC
没有最难,只有更难,这周已经做得心态爆炸了((
开始连有用的函数都找不到,官方wp写得也有亿点简略,搞了几个小时也进展不大
先放放吧,后面再填坑
第七周
0×00 GUETCTF 2019 number game
拒 绝 暴 力
die查,Elf64,进ida
main函数东西有点多,一个一个看
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
|
unsigned __int64 __fastcall main(int a1, char **a2, char **a3)
{
_QWORD *v4; // [rsp+8h] [rbp-38h]
__int64 v5; // [rsp+10h] [rbp-30h] BYREF
__int16 v6; // [rsp+18h] [rbp-28h]
__int64 v7; // [rsp+20h] [rbp-20h] BYREF
__int16 v8; // [rsp+28h] [rbp-18h]
char v9; // [rsp+2Ah] [rbp-16h]
unsigned __int64 v10; // [rsp+38h] [rbp-8h]
v10 = __readfsqword(0x28u);
v5 = 0LL;
v6 = 0;
v7 = 0LL;
v8 = 0;
v9 = 0;
__isoc99_scanf("%s", &v5);
if ( (unsigned int)sub_4006D6((const char *)&v5) )
{
v4 = sub_400758((__int64)&v5, 0, 10u);
sub_400807((__int64)v4, (__int64)&v7);
v9 = 0;
sub_400881(&v7);
if ( (unsigned int)sub_400917() )
{
puts("TQL!");
printf("flag{");
printf("%s", (const char *)&v5);
puts("}");
}
else
{
puts("your are cxk!!"); //cai xu kun???
}
}
return __readfsqword(0x28u) ^ v10;
}
|
sub_4006D6()判断输入是否为数字0-4,且共10位
剩下几个函数可以倒着看
第二个if里面sub_4006D6()的判断是一个5*5的数独逻辑
1
2
3
4
5
|
1 4 # 2 3
3 0 # 1 #
0 # 2 3 #
# 3 # # 0
4 2 # # 1
|
手动填一下是0421421430
sub_400881(&v7)就是用上面的值填数独待检验
sub_400807()应该只是复制了一下,v4->v7,动调验证确实啥也没干
主要加密函数是sub_400758()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
_QWORD *__fastcall sub_400758(__int64 a1, int a2, unsigned int a3)
{
char v5; // [rsp+1Fh] [rbp-11h]
_QWORD *v6; // [rsp+28h] [rbp-8h]
v5 = *(_BYTE *)(a2 + a1);
if ( v5 == ' ' || v5 == '\n' || a2 >= (int)a3 )
return 0LL;
v6 = malloc(24uLL);
*(_BYTE *)v6 = v5;
v6[1] = sub_400758(a1, 2 * a2 + 1, a3);
v6[2] = sub_400758(a1, 2 * (a2 + 1), a3);
return v6;
}
|
递归,2 * a2 + 1
,2 * (a2 + 1)
,第一感觉是二叉树访问左右子树,没点OI基础都做不了逆向((
动调验证一下,实际是通过对input按数组下标建立二叉树,然后输出中序遍历
于是可以根据0421421430手动建树,这里因为默认二叉树从左到右生长,所以保证了答案的唯一性
1
2
3
4
5
|
1
1 3
4 2 4 0
0 2 4
//flag:1134240024
|
0x01 HITCTF 2020 Node
没想法,没思路,verify函数上千行代码劝退((
只能拜读RX的wp
原来只要找到345行一逆就行while ( (char)(*((_BYTE *)v124 + v37) ^ 'r') + 'h' == key[v37] )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include<stdio.h>
unsigned char key[] =
{
0x93, 0x85, 0x69, 0x82, 0x83, 0x84, 0x85, 0xC7, 0x69, 0xBA,
0x6D, 0x7B, 0x84, 0x6E, 0xBA, 0x7B, 0xBA, 0x7D, 0x83, 0x68,
0x86, 0x7C, 0x68, 0x83, 0x7F, 0x84, 0x7E, 0xC6, 0x6D, 0x6F,
0x6D, 0x6F
};
char flag[100];
int main(){
for(int i=0;i<32;i++){
flag[i]=(key[i]-'h')^'r';
}
puts(flag);
}
//Yoshino-s want a girlfriend,wuwu
|
难点在于根本找不到
根据bb的wp获得思路,既然逆出来的东西要用来做web,那么一定在export窗口能找到
打开export窗口很显眼地就能看到key
查交叉引用就能找到verify函数,就能定位345行,问题解决,又长见识了
0×02 GKCTF 2020 BabyDriver
die查,PE64,进ida
翻string窗口看到了
.data:0000000140003000 000000E1 C ****************o.*..*......*..**.**...**.*.*.***.****.**.*.*.***.......*.*.*****..***..*..**..***....**....**....***..**.***.***....**...***.**********..***......#****.*****************************
打开果然是个地图,共225byte
刚开始还以为是15*5
,后面看代码觉得不对,最后确定是14*16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
****************
o.*..*......*..*
*.**...**.*.*.**
*.****.**.*.*.**
*...**....*.*.**
***..***.**.*..*
*.**.***.**.**.*
*.**.******.**.*
*.**....***.**.*
*.*****.***....*
*...***.********
**..***......#**
**.*************
****************
|
根据地图查交叉引用定位走地图逻辑函数
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
|
__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
__int64 v3; // rdi
__int64 v4; // rax
int v5; // 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 )
{
v5 = dword_1400030E4;
v6 = (__int16 *)(v3 + 2);
v7 = (unsigned int)v4;
while ( *(_WORD *)(v3 + 4) )
{
LABEL_28:
v6 += 6;
if ( !--v7 )
goto LABEL_29;
}
byte_140003000[v5] = 46;
v8 = *v6;
if ( *v6 == 0x17 )
{
if ( (v5 & 0xFFFFFFF0) != 0 )
{
v5 -= 16;
goto LABEL_21;
}
v5 += 208;
dword_1400030E4 = v5;
}
if ( v8 == 0x25 )
{
if ( (v5 & 0xFFFFFFF0) != 208 )
{
v5 += 16;
goto LABEL_21;
}
v5 -= 208;
dword_1400030E4 = v5;
}
if ( v8 == 0x24 )
{
if ( (v5 & 0xF) != 0 )
{
--v5;
goto LABEL_21;
}
v5 += 15;
dword_1400030E4 = v5;
}
if ( v8 != 0x26 )
goto LABEL_22;
if ( (v5 & 0xF) == 15 )
v5 -= 15;
else
++v5;
LABEL_21:
dword_1400030E4 = v5;
LABEL_22:
v9 = byte_140003000[v5];
if ( v9 == 42 )
{
v10 = "failed!\n";
}
else
{
if ( v9 != 35 )
{
LABEL_27:
byte_140003000[v5] = 111;
goto LABEL_28;
}
v10 = "success! flag is flag{md5(input)}\n";
}
dword_1400030E4 = 16;
DbgPrint(v10);
v5 = dword_1400030E4;
goto LABEL_27;
}
}
LABEL_29:
if ( *(_BYTE *)(a2 + 65) )
*(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u;
return *(unsigned int *)(a2 + 48);
}
|
上下左右逻辑容易看出来,但值0x17,0x25什么的太奇怪了
偷看师傅们的wp知道了原来是键盘扫描码,粘个链接
1
2
3
4
5
6
7
|
0x17-i-up
0x24-j-left
0x25-k-down
0x26-l-right
|
手动走一下:lkkkllklkkklllkkklllll
结果不对??好吧得全大写再求md5
最终flag:flag{403950a6f64f7fc4b655dea696997851}
第六周
0×00 PyDis
附件是pyc,先想到uncompyle6
1
2
3
|
C:\Users\DX3906\Desktop\t1>uncompyle6 pyre.cpython-39.pyc>2.py
# Unsupported bytecode in file pyre.cpython-39.pyc
# Unsupported Python version, 3.9.0, for decompilation
|
3.9不支持,痛苦开始
1
2
3
4
|
import dis,marshal
f=open("pyre.cpython-39.pyc","rb").read()
code = marshal.loads(f[16:])
dis.dis(code)
|
先抄含树师傅的代码得字节码
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
1 0 BUILD_LIST 0
2 LOAD_CONST 0 ((178, 184, 185, 191, 182, 165, 174, 191, 129, 183, 187, 176, 129, 169, 191, 167, 163))
4 LIST_EXTEND 1
6 STORE_NAME 0 (magic)
2 8 LOAD_NAME 1 (input)
10 LOAD_CONST 1 ('flag >>> ')
12 CALL_FUNCTION 1
14 STORE_NAME 2 (inp)
4 16 LOAD_NAME 3 (list)
18 LOAD_NAME 2 (inp)
20 CALL_FUNCTION 1
22 STORE_NAME 4 (flag)
5 24 LOAD_NAME 5 (len)
26 LOAD_NAME 4 (flag)
28 CALL_FUNCTION 1
30 LOAD_NAME 5 (len)
32 LOAD_NAME 0 (magic)
34 CALL_FUNCTION 1
36 COMPARE_OP 3 (!=)
38 POP_JUMP_IF_FALSE 54
6 40 LOAD_NAME 6 (print)
42 LOAD_CONST 2 ('qwq')
44 CALL_FUNCTION 1
46 POP_TOP
7 48 LOAD_NAME 7 (exit)
50 CALL_FUNCTION 0
52 POP_TOP
9 >> 54 LOAD_NAME 8 (range)
56 LOAD_NAME 5 (len)
58 LOAD_NAME 4 (flag)
60 CALL_FUNCTION 1
62 LOAD_CONST 3 (2)
64 BINARY_FLOOR_DIVIDE
66 CALL_FUNCTION 1
68 GET_ITER
>> 70 FOR_ITER 54 (to 126)
72 STORE_NAME 9 (i)
10 74 LOAD_NAME 4 (flag)
76 LOAD_CONST 3 (2)
78 LOAD_NAME 9 (i)
80 BINARY_MULTIPLY
82 LOAD_CONST 4 (1)
84 BINARY_ADD
86 BINARY_SUBSCR
88 LOAD_NAME 4 (flag)
90 LOAD_CONST 3 (2)
92 LOAD_NAME 9 (i)
94 BINARY_MULTIPLY
96 BINARY_SUBSCR
98 ROT_TWO
100 LOAD_NAME 4 (flag)
102 LOAD_CONST 3 (2)
104 LOAD_NAME 9 (i)
106 BINARY_MULTIPLY
108 STORE_SUBSCR
110 LOAD_NAME 4 (flag)
112 LOAD_CONST 3 (2)
114 LOAD_NAME 9 (i)
116 BINARY_MULTIPLY
118 LOAD_CONST 4 (1)
120 BINARY_ADD
122 STORE_SUBSCR
124 JUMP_ABSOLUTE 70
12 >> 126 BUILD_LIST 0
128 STORE_NAME 10 (check)
14 130 LOAD_NAME 8 (range)
132 LOAD_NAME 5 (len)
134 LOAD_NAME 4 (flag)
136 CALL_FUNCTION 1
138 CALL_FUNCTION 1
140 GET_ITER
>> 142 FOR_ITER 26 (to 170)
144 STORE_NAME 9 (i)
15 146 LOAD_NAME 10 (check)
148 LOAD_METHOD 11 (append)
150 LOAD_NAME 12 (ord)
152 LOAD_NAME 4 (flag)
154 LOAD_NAME 9 (i)
156 BINARY_SUBSCR
158 CALL_FUNCTION 1
160 LOAD_CONST 5 (222)
162 BINARY_XOR
164 CALL_METHOD 1
166 POP_TOP
168 JUMP_ABSOLUTE 142
17 >> 170 LOAD_NAME 8 (range)
172 LOAD_NAME 5 (len)
174 LOAD_NAME 0 (magic)
176 CALL_FUNCTION 1
178 CALL_FUNCTION 1
180 GET_ITER
>> 182 FOR_ITER 34 (to 218)
184 STORE_NAME 9 (i)
18 186 LOAD_NAME 10 (check)
188 LOAD_NAME 9 (i)
190 BINARY_SUBSCR
192 LOAD_NAME 0 (magic)
194 LOAD_NAME 9 (i)
196 BINARY_SUBSCR
198 COMPARE_OP 3 (!=)
200 POP_JUMP_IF_FALSE 182
19 202 LOAD_NAME 6 (print)
204 LOAD_CONST 2 ('qwq')
206 CALL_FUNCTION 1
208 POP_TOP
20 210 LOAD_NAME 7 (exit)
212 CALL_FUNCTION 0
214 POP_TOP
216 JUMP_ABSOLUTE 182
22 >> 218 LOAD_NAME 6 (print)
220 LOAD_CONST 6 ('happy new year!')
222 CALL_FUNCTION 1
224 POP_TOP
226 LOAD_CONST 7 (None)
228 RETURN_VALUE
|
参考:死磕字节码 python官方文档 分析逻辑
大概是这个意思(还是习惯写c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#include<stdio.h>
char magic[]={178, 184, 185, 191, 182, 165, 174, 191, 129, 183, 187, 176, 129, 169, 191, 167, 163};
char flag[100];
int main(){
scanf("%s",flag);
for(int i=0;i<8;i++){
char x=flag[2*i];
flag[2*i]=flag[2*i+1];
flag[2*i+1]=x;
}
for(int i=0;i<17;i++){
flag[i]^=222;
if(flag[i]!=magic[i]){
puts("wrong");
return 0;
}
}
puts("write");
}
|
写脚本解密
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include<stdio.h>
char en_flag[]={178, 184, 185, 191, 182, 165, 174, 191, 129, 183, 187, 176, 129, 169, 191, 167, 163};
int main(){
for(int i=0;i<17;i++){
en_flag[i]^=222;
}
for(int i=0;i<8;i++){
char x=en_flag[2*i];
en_flag[2*i]=en_flag[2*i+1];
en_flag[2*i+1]=x;
}
puts(en_flag);
}
//flag{hapi_new_ya}
|
0×01 FlareOn4 IgniteMe
die查,无壳,PE32,进ida
主函数长这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
void __noreturn start()
{
DWORD NumberOfBytesWritten; // [esp+0h] [ebp-4h] BYREF
NumberOfBytesWritten = 0;
hFile = GetStdHandle(0xFFFFFFF6);
dword_403074 = GetStdHandle(0xFFFFFFF5);
WriteFile(dword_403074, aG1v3M3T3hFl4g, 0x13u, &NumberOfBytesWritten, 0);
sub_4010F0(NumberOfBytesWritten);
if ( sub_401050() )
WriteFile(dword_403074, aG00dJ0b, 0xAu, &NumberOfBytesWritten, 0);
else
WriteFile(dword_403074, aN0tT00H0tRWe7r, 0x24u, &NumberOfBytesWritten, 0);
ExitProcess(0);
}
|
sub_4010F0()这个函数好像什么也没干
主要加密在sub_401050()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int sub_401050()
{
int v1; // [esp+0h] [ebp-Ch]
int i; // [esp+4h] [ebp-8h]
unsigned int j; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]
v1 = sub_401020(byte_403078);
v4 = sub_401000();
for ( i = v1 - 1; i >= 0; --i )
{
byte_403180[i] = v4 ^ byte_403078[i];
v4 = byte_403078[i];
}
for ( j = 0; j < 0x27; ++j )
{
if ( byte_403180[j] != (unsigned __int8)byte_403000[j] )
return 0;
}
return 1;
}
|
看一下里面代码能猜出来sub_401020()就是strlen()
v4直接动调就是0x4
写脚本解密
1
2
3
4
5
6
7
8
9
10
11
12
|
#include<stdio.h>
char en_flag[]={13, 38,73, 69, 42, 23, 120, 68, 43, 108,93, 94, 69, 18, 47, 23, 43, 68, 111, 110,86, 9, 95, 69, 71, 115, 38, 10, 13, 19,23, 72, 66, 1, 64, 77, 12, 2, 105,0};
char flag[1000];
int main(){
char V4=4;
for(int i=39;i>=0;i--){
flag[i]=en_flag[i]^V4;
V4=flag[i];
}
puts(flag);
}
//flag;{[email protected]}
|
0x02 BUUCTF Firmware
这题难点在于工具的安装((
跟着含树师傅的wp走
windows比较麻烦,直接开虚拟机先装binwalk,觉得慢可以换个源
1
2
3
|
git clone https://github.com/devttys0/binwalk
cd binwalk
python setup.py install
|
先binwalk -e 拆包,四个文件,挨个file命令查看
有用的就是120200.squashfs,这里要用 firmware-mod-kit 的 unsquashfs_all.sh工具
然后就遇到了和bb师傅一样的问题,持续痛苦面具
幸好RX神是万能的
发现问题是github仓库的源代码有问题
换一个仓库一切正常了
1
2
3
4
|
git clone https://github.com/rampageX/firmware-mod-kit.git
cd firmware-mod-kit/src
./configure
make
|
利用uncramfs_all.sh拆120200.squashfs
发现/tmp里有个backdoor文件,这也太明显了,不过要是藏起来可能还真找不着
拖ida分析
先看string窗口直接就看到了 echo.byethost51.com
盲猜就是题目要的服务器地址,点进去看到变量名commServer,确信
查交叉引用找到了ininConnextion()函数
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
|
bool initConnection()
{
char *v0; // r0
char s[512]; // [sp+4h] [bp-208h] BYREF
int v3; // [sp+204h] [bp-8h]
memset(s, 0, sizeof(s));
if ( mainCommSock )
{
close(mainCommSock);
mainCommSock = 0;
}
if ( currentServer )
++currentServer;
else
currentServer = 0;
strcpy(s, (&commServer)[currentServer]);
v3 = 36667;
if ( strchr(s, 58) )
{
v0 = strchr(s, 58);
v3 = atoi(v0 + 1);
*strchr(s, 58) = 0;
}
mainCommSock = socket(2, 1, 0);
return connectTimeout(mainCommSock, s, v3, 30) == 0;
}
|
盲猜端口号就是v3=36667,实在太像了
于是flag{MD5(echo.byethost51.com:36667)}=flag{33a422c45d551ac6e4756f59812a954b}
correct!
第五周
2021-11-29
补上周,虽迟但到
0x00 DDCTF Android Easy
下载附件是个zip
后缀改为apk用jeb打开
打开FlagActivity文件看java
甚至都不用算((
flag{[email protected]}
0x01 WELCOME TO JNI
1
2
|
【维基百科】
JNI (Java Native Interface,Java本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/或库,也可以被其他程序调用。 本地程序一般是用其它语言(C、C++或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序。
|
下载附件是apk,直接进jeb,打开MainActivity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native boolean loginUtils(String arg1) {
}
@Override // androidx.appcompat.app.AppCompatActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F0B001C); // layout:activity_main
this.findViewById(0x7F080057).setOnClickListener(new View.OnClickListener() { // id:button
@Override // android.view.View$OnClickListener
public void onClick(View v) {
boolean ok = MainActivity.this.loginUtils(((EditText)MainActivity.this.findViewById(0x7F0800B5)).getText().toString()); // id:inputBox
Toast.makeText(MainActivity.this.getApplicationContext(), ok ? "RIGHT!!!!" : "WRONG!!!!", 1).show();
}
});
}
}
|
经含树师傅指点得知stem.loadLibrary("native-lib");
就是JNI的标志
把附件后缀改为zip,打开lib文件夹,里面即native-lib
四个文件夹内容基本一样,只是架构不同
随便拖一个进ida(一定要先解压。。。)
查string就能看到flag{welcome_to_naive_lib!}
0x02 Codegate CTF Redvelvet
die查,elf64,进ida,函数列表找main
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
|
v40 = __readfsqword(40u);
strcpy(s2, "0a435f46288bb5a764d13fca6c901d3750cee73fd7689ce79ef6dc0ff8f380e5");
v36 = 0LL;
v37 = 0LL;
v38 = 0LL;
v39 = 0;
printf("Your flag : ");
fgets(&s, 27, edata);
func1(s, v8);
func2((unsigned int)v8, (unsigned int)v9);
func3((unsigned int)v9, (unsigned int)v10);
func4((unsigned int)v10, (unsigned int)v11);
func5((unsigned int)v11, (unsigned int)v12);
func6((unsigned int)v12, (unsigned int)v13, (unsigned int)v14);
func7((unsigned int)v14, (unsigned int)v15, (unsigned int)v16);
func8((unsigned int)v16, (unsigned int)v17, (unsigned int)v18);
func9((unsigned int)v18, (unsigned int)v19, (unsigned int)v20);
func10((unsigned int)v20, (unsigned int)v21, (unsigned int)v22);
func11((unsigned int)v22, (unsigned int)v23, (unsigned int)v24);
func12((unsigned int)v24, (unsigned int)v25, (unsigned int)v26);
func13((unsigned int)v26, (unsigned int)v27, (unsigned int)v28);
func14((unsigned int)v28, (unsigned int)v29, (unsigned int)v30);
func15((unsigned int)v30, (unsigned int)v31, (unsigned int)v32);
SHA256_Init((__int64)v6);
v3 = strlen(&s);
SHA256_Update(v6, &s, v3);
SHA256_Final(v33, v6);
for ( i = 0; i <= 31; ++i )
sprintf(&s1[2 * i], "%02x", (unsigned __int8)v33[i]);
if ( strcmp(s1, s2) )
exit(1);
printf("flag : {\" %s \"}\n", &s);
return 0;
|
基本逻辑是对输入走15个function,然后SHA256加密和s2比较
直接上angr
但这里需要注意,SHA256不可逆,所以find直接设在cmp处angr算不出来
而从SHA256之前到cmp只有一条路,所以find设在加密之前就行了,而avoid设在exit()函数处就行
1
2
3
4
5
6
7
8
9
|
In [1]: import angr
In [2]: proj = angr.Project('./RedVelvet',auto_load_libs=False)
In [3]: state = proj.factory.entry_state()
In [4]: simgr = proj.factory.simgr(state)
In [5]: simgr.explore(find=0x401537,avoid=0x4007D0)
|
提示Out[5]: <SimulationManager with 1 found, 63 avoid>
输出
1
2
|
In [6]: print(simgr.found[0].posix.dumps(0))
b'What_You_Wanna_Be?:)_la_la'
|
这里说明一下,上面的是正确答案
但其实我第一次跑出来是b'What_You_Wanna_Be?:)_lc_la
,差了一个字母
当我写这篇wp时又跑了一遍,答案却是对的
两次脚本完全一样,angr也都没提示多解,但输出却不同
这才印证了RX师傅所说,粘几张聊天记录
用我的话来说就是:
1.angr对于已经走通的路不会再走第二遍
2.每一次先走哪条路(得到哪个答案)是随机的
所以出问题时(angr计算不准确)大可以多来两遍(
第四周
2021-11-20
0x00 DMCTF2020 re3
一种常见的算法。
die查,ELF64,进ida
结合tips使用Findcrypt插件
函数列表定位main函数
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
|
__int64 __fastcall main(int a1, char **a2, char **a3)
{
unsigned int v3; // eax
int i; // [rsp+1Ch] [rbp-C4h]
char v6[96]; // [rsp+20h] [rbp-C0h] BYREF
char v7[16]; // [rsp+80h] [rbp-60h] BYREF
char s[32]; // [rsp+90h] [rbp-50h] BYREF
char s1[40]; // [rsp+B0h] [rbp-30h] BYREF
unsigned __int64 v10; // [rsp+D8h] [rbp-8h]
v10 = __readfsqword(0x28u);
puts(::s);
memset(s, 0, 0x14uLL);
read(0, s, 5uLL);
sub_90A(v6);
v3 = strlen(s);
sub_956(v6, s, v3);
sub_AA4(v6, v7);
for ( i = 0; i <= 15; ++i )
sprintf(&s1[2 * i], "%02x", (unsigned __int8)v7[i]);
if ( !strcmp(s1, Big_Numbers1_203060) )
printf("right");
getchar();
return 0LL;
}
|
Findcrypt结果
1
|
.data:0000000000203060 global Big_Numbers1_203060 $c0 b'21232f297a57a5a743894a0e4a801fc3'
|
难道有种算法叫bignumbers?
百度无果,于是尝试搞懂加密逻辑,手动逆(天真.jpg)
然后就没有然后了(
偷看师傅们的题解,原来bignumbers是MD5的密文???
网站撞出flag{admin}
0x01 ACTF2020 Oruga
又是熟悉的迷宫……等下这是什么东西?
die查,Elf64,进ida
迷宫逻辑函数
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
|
_BOOL8 __fastcall sub_78A(__int64 a1)
{
int v2; // [rsp+Ch] [rbp-Ch]
int v3; // [rsp+10h] [rbp-8h]
int v4; // [rsp+14h] [rbp-4h]
v2 = 0;
v3 = 5;
v4 = 0;
while ( byte_201020[v2] != 33 )
{
v2 -= v4;
if ( *(_BYTE *)(v3 + a1) != 'W' || v4 == -16 )
{
if ( *(_BYTE *)(v3 + a1) != 'E' || v4 == 1 )
{
if ( *(_BYTE *)(v3 + a1) != 'M' || v4 == 16 )
{
if ( *(_BYTE *)(v3 + a1) != 'J' || v4 == -1 )
return 0LL;
v4 = -1;
}
else
{
v4 = 16;
}
}
else
{
v4 = 1;
}
}
else
{
v4 = -16;
}
++v3;
while ( !byte_201020[v2] )
{
if ( v4 == -1 && (v2 & 0xF) == 0 )
return 0LL;
if ( v4 == 1 && v2 % 16 == 15 )
return 0LL;
if ( v4 == 16 && (unsigned int)(v2 - 240) <= 0xF )
return 0LL;
if ( v4 == -16 && (unsigned int)(v2 + 15) <= 0x1E )
return 0LL;
v2 += v4;
}
}
return *(_BYTE *)(v3 + a1) == 125;
}
|
可以看出byte_201020是地图,共256byte,即16*16的地图
v2是当前所在的位置,v4是偏移量,1-right,-1-left,16-down,-16-up
上面的while里四个if可以直接对应else给v4赋值,即W-up E-right M-down J-left
下面的while函数里四个if应该是边界判断
然后循环一直往一个方向走,直到碰到字符
知道了这些,才能搞懂上面四个if里||v4==xx这个条件是在干啥
可以发现这个条件和下面else里给v4的赋值是一样的
即如果下一步和上一步是一样的,但上一步已经走到了这个方向所能走的最远处,这一步就是无效的
如果出现无效的重复命令,if判断会一路畅通,直接return 0
至此,手动走地图得flag:actf{MEWEMEWJMEWJM}
0×02 网鼎杯2020 signal
尝试一下某个自动化逆向工具?
感谢RX细心又耐心讲解符号执行
angr直接搞他
1
2
3
4
5
|
project = angr.Project('./signal.exe',auto_load_libs = False)
state = project.factory.entry_state()
simgr = project.factory.simgr(state)
simgr.explore(find = 0x40179E,avoid =0x401539)
print(simgr.found[0].posix.dumps(0))
|
得flag{757515121f3d478},真香
第三周
2021-11-13
0×00 SUCTF2019 Signin
你认识这个算法嘛
不认识,然后根据题解先去学RSA和gmpy2库,断断续续学了几天((
贴个链接吧,便于以后复习
RSA gmpy2库常见函数
die查壳,无,然后进ida,直接根据函数列表定位main函数
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
|
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char v4[16]; // [rsp+0h] [rbp-4A0h] BYREF
char v5[16]; // [rsp+10h] [rbp-490h] BYREF
char v6[16]; // [rsp+20h] [rbp-480h] BYREF
char v7[16]; // [rsp+30h] [rbp-470h] BYREF
char v8[112]; // [rsp+40h] [rbp-460h] BYREF
char v9[1000]; // [rsp+B0h] [rbp-3F0h] BYREF
unsigned __int64 v10; // [rsp+498h] [rbp-8h]
v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ");
__isoc99_scanf("%99s", v8);
sub_96A(v8, v9);
__gmpz_init_set_str(v7, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str(v6, v9, 16LL);
__gmpz_init_set_str(v4, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
__gmpz_init_set_str(v5, "65537", 10LL);
__gmpz_powm(v6, v6, v5, v4);
if ( (unsigned int)__gmpz_cmp(v6, v7) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}
|
根据RSA的形式稍微重命名一下变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
v10 = __readfsqword(0x28u);
puts("[sign in]");
printf("[input your flag]: ");
__isoc99_scanf("%99s", input);
sub_96A(input, v9);
__gmpz_init_set_str(c, "ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str(calc, v9, 16LL);
__gmpz_init_set_str(n, "103461035900816914121390101299049044413950405173712170434161686539878160984549", 10LL);
__gmpz_init_set_str(e, "65537", 10LL);
__gmpz_powm(calc, calc, e, n);
if ( (unsigned int)__gmpz_cmp(calc, c) ) // calc是根据input计算出的密文,c是实际的密文
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
|
先用yafu分解n,由于写这篇文章时是第二次分解n所以比较快
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
C:\Users\DX3906>D:\reserve\yafu\yafu-x64.exe
factor(103461035900816914121390101299049044413950405173712170434161686539878160984549)
fac: factoring 103461035900816914121390101299049044413950405173712170434161686539878160984549
fac: using pretesting plan: normal
fac: no tune info: using qs/gnfs crossover of 95 digits
starting SIQS on c78: 103461035900816914121390101299049044413950405173712170434161686539878160984549
==== sieving in progress (1 thread): 36224 relations needed ====
==== Press ctrl-c to abort and save state ====
SIQS elapsed time = 1.0985 seconds.
Total factoring time = 1.1195 seconds
***factors found***
P39 = 282164587459512124844245113950593348271
P39 = 366669102002966856876605669837014229419
ans = 1
|
得到 p=282164587459512124844245113950593348271 q=366669102002966856876605669837014229419
已知n,p,q,e,先求d
1
2
3
4
5
6
7
|
import gmpy2
n=103461035900816914121390101299049044413950405173712170434161686539878160984549
p=282164587459512124844245113950593348271
q=366669102002966856876605669837014229419
e=65537
l=(p-1)*(q-1)
d=gmpy2.invert(e,l)
|
已知n,d,c,求明文
1
2
3
4
5
|
c=0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
flag=gmpy2.powmod(c,d,n)
from Crypto.Util.number import *
print(long_to_bytes(flag))
# b'suctf{Pwn_@_hundred_years}'
|
0×01 FlareOn6 Overlang
die查看文件信息,PE32,拖进ida
竟然就三个函数,start函数应该是主要的
1
2
3
4
5
6
7
8
9
10
|
int __stdcall start(int a1, int a2, int a3, int a4)
{
CHAR Text[128]; // [esp+0h] [ebp-84h] BYREF
int v6; // [esp+80h] [ebp-4h]
v6 = sub_401160(Text, &unk_402008, 28);
Text[v6] = 0;
MessageBoxA(0, Text, Caption, 0);
return 0;
}
|
sub_401160函数像是把unk_402008的前28位复制给Text,然后通过MessageBoxA输出Text和Caption
这里我们可以打开unk_402008看看
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
.rdata:00402008 unk_402008 db 0E0h ; DATA XREF: start+B↑o
.rdata:00402009 db 81h
.rdata:0040200A db 89h
.rdata:0040200B db 0C0h
.rdata:0040200C db 0A0h
.rdata:0040200D db 0C1h
.rdata:0040200E db 0AEh
.rdata:0040200F db 0E0h
.rdata:00402010 db 81h
.rdata:00402011 db 0A5h
.rdata:00402012 db 0C1h
.rdata:00402013 db 0B6h
.rdata:00402014 db 0F0h
.rdata:00402015 db 80h ; €
.rdata:00402016 db 81h
.rdata:00402017 db 0A5h
.rdata:00402018 db 0E0h
.rdata:00402019 db 81h
.rdata:0040201A db 0B2h
.rdata:0040201B db 0F0h
.rdata:0040201C db 80h ; €
.rdata:0040201D db 80h ; €
.rdata:0040201E db 0A0h
.rdata:0040201F db 0E0h
.rdata:00402020 db 81h
.rdata:00402021 db 0A2h
.rdata:00402022 db 72h ; r
.rdata:00402023 db 6Fh ; o
.rdata:00402024 db 0C1h
.rdata:00402025 db 0ABh
.rdata:00402026 db 65h ; e
.rdata:00402027 db 0E0h
.rdata:00402028 db 80h ; €
.rdata:00402029 db 0A0h
.rdata:0040202A db 0E0h
.rdata:0040202B db 81h
.rdata:0040202C db 0B4h
.rdata:0040202D db 0E0h
.rdata:0040202E db 81h
.rdata:0040202F db 0A8h
.rdata:00402030 db 0C1h
.rdata:00402031 db 0A5h
.rdata:00402032 db 20h
.rdata:00402033 db 0C1h
.rdata:00402034 db 0A5h
.rdata:00402035 db 0E0h
.rdata:00402036 db 81h
.rdata:00402037 db 0AEh
.rdata:00402038 db 63h ; c
.rdata:00402039 db 0C1h
.rdata:0040203A db 0AFh
.rdata:0040203B db 0E0h
.rdata:0040203C db 81h
.rdata:0040203D db 0A4h
.rdata:0040203E db 0F0h
.rdata:0040203F db 80h ; €
.rdata:00402040 db 81h
.rdata:00402041 db 0A9h
.rdata:00402042 db 6Eh ; n
.rdata:00402043 db 0C1h
.rdata:00402044 db 0A7h
.rdata:00402045 db 0C0h
.rdata:00402046 db 0BAh
.rdata:00402047 db 20h
.rdata:00402048 db 49h ; I
.rdata:00402049 db 0F0h
.rdata:0040204A db 80h ; €
.rdata:0040204B db 81h
.rdata:0040204C db 9Fh
.rdata:0040204D db 0C1h
.rdata:0040204E db 0A1h
.rdata:0040204F db 0C1h
.rdata:00402050 db 9Fh
.rdata:00402051 db 0C1h
.rdata:00402052 db 8Dh
.rdata:00402053 db 0E0h
.rdata:00402054 db 81h
.rdata:00402055 db 9Fh
.rdata:00402056 db 0C1h
.rdata:00402057 db 0B4h
.rdata:00402058 db 0F0h
.rdata:00402059 db 80h ; €
.rdata:0040205A db 81h
.rdata:0040205B db 9Fh
.rdata:0040205C db 0F0h
.rdata:0040205D db 80h ; €
.rdata:0040205E db 81h
.rdata:0040205F db 0A8h
.rdata:00402060 db 0C1h
.rdata:00402061 db 9Fh
.rdata:00402062 db 0F0h
.rdata:00402063 db 80h ; €
.rdata:00402064 db 81h
.rdata:00402065 db 0A5h
.rdata:00402066 db 0E0h
.rdata:00402067 db 81h
.rdata:00402068 db 9Fh
.rdata:00402069 db 0C1h
.rdata:0040206A db 0A5h
.rdata:0040206B db 0E0h
.rdata:0040206C db 81h
.rdata:0040206D db 9Fh
.rdata:0040206E db 0F0h
.rdata:0040206F db 80h ; €
.rdata:00402070 db 81h
.rdata:00402071 db 0AEh
.rdata:00402072 db 0C1h
.rdata:00402073 db 9Fh
.rdata:00402074 db 0F0h
.rdata:00402075 db 80h ; €
.rdata:00402076 db 81h
.rdata:00402077 db 83h
.rdata:00402078 db 0C1h
.rdata:00402079 db 9Fh
.rdata:0040207A db 0E0h
.rdata:0040207B db 81h
.rdata:0040207C db 0AFh
.rdata:0040207D db 0E0h
.rdata:0040207E db 81h
.rdata:0040207F db 9Fh
.rdata:00402080 db 0C1h
.rdata:00402081 db 84h
.rdata:00402082 db 5Fh ; _
.rdata:00402083 db 0E0h
.rdata:00402084 db 81h
.rdata:00402085 db 0A9h
.rdata:00402086 db 0F0h
.rdata:00402087 db 80h ; €
.rdata:00402088 db 81h
.rdata:00402089 db 9Fh
.rdata:0040208A db 6Eh ; n
.rdata:0040208B db 0E0h
.rdata:0040208C db 81h
.rdata:0040208D db 9Fh
.rdata:0040208E db 0E0h
.rdata:0040208F db 81h
.rdata:00402090 db 0A7h
.rdata:00402091 db 0E0h
.rdata:00402092 db 81h
.rdata:00402093 db 80h ; €
.rdata:00402094 db 0F0h
.rdata:00402095 db 80h ; €
.rdata:00402096 db 81h
.rdata:00402097 db 0A6h
.rdata:00402098 db 0F0h
.rdata:00402099 db 80h ; €
.rdata:0040209A db 81h
.rdata:0040209B db 0ACh
.rdata:0040209C db 0E0h
.rdata:0040209D db 81h
.rdata:0040209E db 0A1h
.rdata:0040209F db 0C1h
.rdata:004020A0 db 0B2h
.rdata:004020A1 db 0C1h
.rdata:004020A2 db 0A5h
.rdata:004020A3 db 0F0h
.rdata:004020A4 db 80h ; €
.rdata:004020A5 db 80h ; €
.rdata:004020A6 db 0ADh
.rdata:004020A7 db 0F0h
.rdata:004020A8 db 80h ; €
.rdata:004020A9 db 81h
.rdata:004020AA db 0AFh
.rdata:004020AB db 6Eh ; n
.rdata:004020AC db 0C0h
.rdata:004020AD db 0AEh
.rdata:004020AE db 0F0h
.rdata:004020AF db 80h ; €
.rdata:004020B0 db 81h
.rdata:004020B1 db 0A3h
.rdata:004020B2 db 6Fh ; o
.rdata:004020B3 db 0F0h
.rdata:004020B4 db 80h ; €
.rdata:004020B5 db 81h
.rdata:004020B6 db 0ADh
.rdata:004020B7 db 0
|
发现问题,这一串数据远不止28位,结合程序运行时的输出后面有个冒号
推测这串数据后半段就是flag,只是没有输出而已
于是找到函数sub_401160的汇编代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
.text:004011C0 push ebp
.text:004011C1 mov ebp, esp
.text:004011C3 sub esp, 84h
.text:004011C9 push 1Ch
.text:004011CB push offset unk_402008
.text:004011D0 lea eax, [ebp+Text]
.text:004011D6 push eax
.text:004011D7 call sub_401160
.text:004011DC add esp, 0Ch
.text:004011DF mov [ebp+var_4], eax
.text:004011E2 mov ecx, [ebp+var_4]
.text:004011E5 mov [ebp+ecx+Text], 0
.text:004011ED push 0 ; uType
.text:004011EF push offset Caption ; "Output"
.text:004011F4 lea edx, [ebp+Text]
.text:004011FA push edx ; lpText
.text:004011FB push 0 ; hWnd
.text:004011FD call ds:MessageBoxA
.text:00401203 xor eax, eax
.text:00401205 mov esp, ebp
.text:00401207 pop ebp
.text:00401208 retn 10h
|
把第四行push 1ch patch成一个大点的数,这里我写成push 5ch
然后apply一下,重新打开exe,大功告成!
0×02 BJDCTF2020 easy
和上道题有异曲同工之处?
die查,结果PE32,进ida
直接定位main函数
1
2
3
4
5
6
7
8
9
10
11
12
|
int __cdecl main(int argc, const char **argv, const char **envp)
{
__time32_t Time; // [esp+10h] [ebp-3F0h] BYREF
struct tm *v5; // [esp+3FCh] [ebp-4h]
__main();
time(&Time);
v5 = localtime(&Time);
puts("Can you find me?\n");
system("pause");
return 0;
}
|
各种time给我整蒙了,特意百度查了一下发现好像真没什么用
和上一题有异曲同工之妙?直接去翻参数列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.text:00401725 push ebp
.text:00401726 mov ebp, esp
.text:00401728 and esp, 0FFFFFFF0h
.text:0040172B sub esp, 400h
.text:00401731 call ___main
.text:00401736 lea eax, [esp+400h+var_3F0]
.text:0040173A mov [esp+400h+Time], eax ; Time
.text:0040173D call _time
.text:00401742 lea eax, [esp+400h+var_3F0]
.text:00401746 mov [esp+400h+Time], eax ; Time
.text:00401749 call _localtime
.text:0040174E mov [esp+400h+var_4], eax
.text:00401755 mov [esp+400h+Time], offset Buffer ; "Can you find me?\n"
.text:0040175C call _puts
.text:00401761 mov [esp+400h+Time], offset Command ; "pause"
.text:00401768 call _system
.text:0040176D mov eax, 0
.text:00401772 leave
.text:00401773 retn
|
也没发现什么有用的,毫无头绪
翻函数列表的时候发现了问题,就是这个_ques函数,
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
|
int ques()
{
int v0; // edx
int result; // eax
int v2[50]; // [esp+20h] [ebp-128h] BYREF
int v3; // [esp+E8h] [ebp-60h]
int v4[10]; // [esp+ECh] [ebp-5Ch]
int j; // [esp+114h] [ebp-34h]
__int64 v6; // [esp+118h] [ebp-30h]
int v7; // [esp+124h] [ebp-24h]
int v8; // [esp+128h] [ebp-20h]
int i; // [esp+12Ch] [ebp-1Ch]
v3 = 2147122737;
v4[0] = 140540;
v4[1] = -2008399303;
v4[2] = 141956;
v4[3] = 139457077;
v4[4] = 262023;
v4[5] = -2008923597;
v4[6] = 143749;
v4[7] = 2118271985;
v4[8] = 143868;
for ( i = 0; i <= 4; ++i )
{
memset(v2, 0, sizeof(v2));
v8 = 0;
v7 = 0;
v0 = v4[2 * i];
LODWORD(v6) = *(&v3 + 2 * i);
HIDWORD(v6) = v0;
while ( v6 > 0 )
{
v2[v8++] = v6 % 2;
v6 /= 2i64;
}
for ( j = 50; j >= 0; --j )
{
if ( v2[j] )
{
if ( v2[j] == 1 )
{
putchar(42);
++v7;
}
}
else
{
putchar(32);
++v7;
}
if ( !(v7 % 5) )
putchar(32);
}
result = putchar(10);
}
return result;
}
|
从感觉上来说一般能看懂的都是有用的,然而按x查交叉引用的时候却显示没有地方调用它
于是盲猜前面没啥用的各种time函数就是让我patch的,搞他(我这里改了第11行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.text:00401725 push ebp
.text:00401726 mov ebp, esp
.text:00401728 and esp, 0FFFFFFF0h
.text:0040172B sub esp, 400h
.text:00401731 call ___main
.text:00401736 lea eax, [esp+400h+var_3F0]
.text:0040173A mov [esp+400h+Time], eax ; Time
.text:0040173D call _time
.text:00401742 lea eax, [esp+400h+var_3F0]
.text:00401746 mov [esp+400h+Time], eax ; Time
.text:00401749 call _ques ; Keypatch modified this from:
.text:00401749 ; call _localtime
.text:0040174E mov [esp+400h+var_4], eax
.text:00401755 mov [esp+400h+Time], offset Buffer ; "Can you find me?\n"
.text:0040175C call _puts
.text:00401761 mov [esp+400h+Time], offset Command ; "pause"
.text:00401768 call _system
.text:0040176D mov eax, 0
.text:00401772 leave
.text:00401773 retn
|
apply一下,然后重新运行exe,成功!
第二周
2021-11-05
0×00 从 CNSS 偷来的 SMC
SMC是什么?
看完tips尝试一下
string窗口定位主函数
1
2
3
4
5
6
7
8
9
10
11
12
13
|
.text:00408ACE push offset aPleaseInputYou ; "Please input your code(less than 30 cha"...
.text:00408AD3 call sub_404629
.text:00408AD8 add esp, 4
.text:00408ADB push 64h ; 'd'
.text:00408ADD push offset unk_4DFEF8
.text:00408AE2 push offset aS ; "%s"
.text:00408AE7 call sub_4023EC
.text:00408AEC add esp, 0Ch
.text:00408AEF push offset loc_408B06
.text:00408AF4 push 138h
.text:00408AF9 push offset loc_408B06
.text:00408AFE call sub_4022AC
.text:00408B03 add esp, 0Ch
|
定位问题函数sub_4022AC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
int __cdecl sub_4088D0(int a1, int a2, int a3)
{
int result; // eax
int i; // [esp+D0h] [ebp-8h]
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a2 )
break;
*(_BYTE *)(i + a3) = byte_4DF000 ^ *(_BYTE *)(i + a1);
}
return result;
}
|
idaPython 搞他
1
2
3
|
from ida_bytes import *
for i in range(0x138):
patch_byte(0x408B06+i,get_byte(0x408B06+i)^74)
|
按照步骤来到函数头CreatFonction,结果就出问题了
1
2
3
|
报错
.text:00408B19: The function has undefined instruction/data at the specified address.
Your request has been put in the autoanalysis queue.
|
开始以为是前面哪里做错了,又重复了几遍,一样的结果
请教RX大神发现问题
1
2
3
4
5
6
7
8
|
.text:00408B06 mov dword ptr [ebp-0Ch], 1
.text:00408B0D mov dword ptr [ebp-8], 1
.text:00408B14 push 4DFEF8h
.text:00408B14 ; ---------------------------------------------------------------------------
.text:00408B19 db 0E8h
.text:00408B1A retn 0FF95h
.text:00408B1A ; ---------------------------------------------------------------------------
.text:00408B1D db 0FFh
|
这里ida识别错了
按c重新识别为code,一切正常了
重新CreateFunction接F5终于看到主函数
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
|
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
signed int j; // [esp+D4h] [ebp-3Ch]
int i; // [esp+E0h] [ebp-30h]
char v6; // [esp+EFh] [ebp-21h]
signed int v7; // [esp+F8h] [ebp-18h]
int v8; // [esp+104h] [ebp-Ch]
int v9; // [esp+108h] [ebp-8h]
sub_404629("Please input your code(less than 30 characters): ");
sub_4023EC("%s", Str);
sub_4022AC((int)&loc_408B06, 312, (int)&loc_408B06);
v8 = 1;
v9 = 1;
v7 = j__strlen(Str);
v6 = 0;
if ( v7 == 23 )
{
for ( i = 0; i < 23; ++i )
{
switch ( Str[i] )
{
case 'a':
--v9;
break;
case 'd':
++v9;
break;
case 's':
++v8;
break;
case 'w':
--v8;
break;
default:
break;
}
if ( aSE[8 * v8 + v9] == 35 )
break;
if ( aSE[8 * v8 + v9] == 69 )
{
v6 = 1;
break;
}
}
}
if ( v6 )
{
for ( j = 0; j < v7; ++j )
byte_4DF04C[j] ^= Str[j];
sub_404629("%s\n");
}
j__system("pause");
return 0;
}
|
经典地图题,aSE数组存地图,加回车得地图
1
2
3
4
5
6
7
8
|
########
#S # #
## # #
##### #
# ##
# ####E#
# #
########
|
w s a d十分人性化,好评
第一次用py写脚本
1
2
3
4
5
|
key = "dsddwddssasaaaassdddddw"
magic = [39, 61, 55, 55, 12, 33, 5, 70, 10, 62, 32, 44, 34, 62,71, 44, 18, 59, 41, 5, 86, 1, 10]
for i in range(0,23):
print( chr( magic[i] ^ ord(key[i]) ), end="")
# CNSS{Ea5y_SMC_&_a_Ma2e}
|
0×01 ByteCTF 2020 AWD
TikTokAdmin 简单花指令
题目劝退,学习了一波花指令是啥再来做题
根据题目,在string窗口查找Delete,找到了发生错误的部分
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
|
.text:00000000000080D4 loc_80D4: ; CODE XREF: .text:000000000000805C↑j
.text:00000000000080D4 lea rsi, aWeNeedToReconf ; "We need to reconfirm the authority!"
.text:00000000000080DB lea rdi, _ZSt4cout ; std::cout
.text:00000000000080E2 call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:00000000000080E7 mov rdx, rax
.text:00000000000080EA mov rax, cs:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6__ptr
.text:00000000000080F1 mov rsi, rax
.text:00000000000080F4 mov rdi, rdx
.text:00000000000080F7 call __ZNSolsEPFRSoS_E ; std::ostream::operator<<(std::ostream & (*)(std::ostream &))
.text:00000000000080FC lea rsi, aPleaseInputThe ; "Please input the super root's password:"...
.text:0000000000008103 mov rdi, rax
.text:0000000000008106 call __ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc ; std::operator<<<std::char_traits<char>>(std::ostream &,char const*)
.text:000000000000810B lea rax, [rbp-90h]
.text:0000000000008112 mov rsi, rax
.text:0000000000008115 lea rdi, _ZSt3cin ; std::cin
.text:000000000000811C call __ZStrsIcSt11char_traitsIcESaIcEERSt13basic_istreamIT_T0_ES7_RNSt7__cxx1112basic_stringIS4_S5_T1_EE ; std::operator>><char>(std::istream &,std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> &)
.text:0000000000008121 jz short near ptr loc_8125+1
.text:0000000000008123 jnz short near ptr loc_8125+1
.text:0000000000008125
.text:0000000000008125 loc_8125: ; CODE XREF: .text:0000000000008121↑j
.text:0000000000008125 ; .text:0000000000008123↑j
.text:0000000000008125 call near ptr 70860E72h
.text:0000000000008125 ; ---------------------------------------------------------------------------
.text:000000000000812A dw 0FFFFh, 48FFh, 0C789h
.text:0000000000008130 dq 0C38948FFFFB56BE8h, 48FFFFFF70858D48h, 48FFFFB489E8C789h
.text:0000000000008130 dq 8948B0458D48C189h, 0E8C78948CE8948DAh, 0B0458D48FFFFB584h
.text:0000000000008130 dq 358D480000000DBAh, 0E8C7894800007B1Dh, 840FC085FFFFB48Ch
.text:0000000000008130 dq 14358D48000000F6h, 0DEF63D8D4800007Bh, 48FFFFB5E1E80000h
.text:0000000000008130 dq 8948B0458D48C289h, 0FFB5CFE8D78948C6h, 7AFD358D48FFh
|
问题出在了jz(为0跳转)和jnz(不为0跳转)同时存在,并插入了0xE8(call的机器码)使ida识别出错
解决方法先把call按u改成未定义,效果如图
1
2
3
4
5
6
7
|
.text:0000000000008125 db 0E8h
.text:0000000000008126 unk_8126 db 48h ; H ; CODE XREF: .text:0000000000008121↑j
.text:0000000000008126 ; .text:0000000000008123↑j
.text:0000000000008127 db 8Dh
.text:0000000000008128 db 85h
.text:0000000000008129 db 70h ; p
.text:000000000000812A dw 0FFFFh, 48FFh, 0C789h
|
然后把0xE8 patch成nop(空),其他数据按c重新分析成代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.text:0000000000008123 ; ---------------------------------------------------------------------------
.text:0000000000008125 db 90h ; Keypatch modified this from:
.text:0000000000008125 ; db 0E8h
.text:0000000000008125 ; Keypatch padded NOP to next boundary: 1 bytes
.text:0000000000008126 ; ---------------------------------------------------------------------------
.text:0000000000008126
.text:0000000000008126 loc_8126: ; CODE XREF: .text:0000000000008121↑j
.text:0000000000008126 ; .text:0000000000008123↑j
.text:0000000000008126 lea rax, [rbp-90h]
.text:000000000000812D mov rdi, rax
.text:0000000000008130 call __ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE4sizeEv ; std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::size(void)
.text:0000000000008135 mov rbx, rax
.text:0000000000008138 lea rax, [rbp-90h]
.text:000000000000813F mov rdi, rax
.text:0000000000008142 call __ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE5c_strEv ; std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(void)
.text:0000000000008147 mov rcx, rax
.text:000000000000814A lea rax, [rbp-50h]
.text:000000000000814E mov rdx, rbx
.text:0000000000008151 mov rsi, rcx
|
然后发现下面还有一个一样的错误,相同办法解决
最后来到函数头Create Function,
完事后到虚拟机上看一眼
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
|
dx3906@ubuntu:~/Desktop$ '/home/dx3906/pwn.so'
'--''' '
l__++??_''
;??JCLCQj(
;?{BB%@@p0,^'
I-{&B@@B@q+'-
;?}8@%$@@Bb;- '
;?}8@$$@$$B&r^-''
;?}%@@@B$$B@B8O?~:-
';?}8@B@@$@$$$$$BBpO{
' ''' ';?}8@B@BBB@@@@@B@pw(
^l+?][I' ;?}8@@%MwOa%@@$@Bdq1
'',?-_?_)uZ0c~';_}%@@@MqJ^IcOwmbqq)
''^:?_+]w&B@@@am( ;-[8@@@MqJ^ '">[|i
^<--}MB@@@@@@am( ;-[8@$$WqJ-' ''' ''
'?]+C%@@$@8#aobq)':_[%@@@WqU-'
'-i]}m@BB%*qd0Q0cU}^I+[%@@@MwU-
'^[?n%B@@aqwL:-^'-"-I+[%@@@MwU-
^-_#@$$%wpx- ';+[B@@@MwU-
^]-B$@@%pq"'' '<_{8@@BMmC-
-^??8$$@@kO, '---1%@$@aqU-
'->-#@$@BBo>'''-'^:?-?h$$$8mwt
'+YBB@@$8&Q-!_]+_[]*$BBBqwqI
''"[&B@@$@@%8WJ/npMB%B@%qwq}
'';M@@@@@$@@B@@@@@@@MmpO]-'
'^;_88@@@@B@B@$B&oqqqC" ^'
-l}mbo&8&&oqmdwZQ:-'
-^~nOwqpZz[I"''
' ''''-'''
████████╗██╗██╗ ██╗████████╗ ██████╗ ██╗ ██╗
╚══██╔══╝██║██║ ██╔╝╚══██╔══╝██╔═══██╗██║ ██╔╝
██║ ██║█████╔╝ ██║ ██║ ██║█████╔╝
██║ ██║██╔═██╗ ██║ ██║ ██║██╔═██╗
██║ ██║██║ ██╗ ██║ ╚██████╔╝██║ ██╗
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
This is the background management system for TikTok.
PassWord:
|
还挺不错,可惜要password
原本是想找一找的,结果看到代码发现我想多了,就先到这吧
0×02 BJDCTF 2020 老八的小汉堡
游戏真不错,但题实话说属于没题解不会做系列(
下了个dnspy,根据题解打开Data/Managed目录下的Assembly-CSharp.dll
搜索字符串“美汁汁”定位主函数
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
|
public class ButtonSpawnFruit : MonoBehaviour
{
// Token: 0x0600000A RID: 10 RVA: 0x00002110 File Offset: 0x00000310
public static string Md5(string str)
{
byte[] bytes = Encoding.UTF8.GetBytes(str);
byte[] array = MD5.Create().ComputeHash(bytes);
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in array)
{
stringBuilder.Append(b.ToString("X2"));
}
return stringBuilder.ToString().Substring(0, 20);
}
// Token: 0x0600000B RID: 11 RVA: 0x00002170 File Offset: 0x00000370
public static string Sha1(string str)
{
byte[] bytes = Encoding.UTF8.GetBytes(str);
byte[] array = SHA1.Create().ComputeHash(bytes);
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in array)
{
stringBuilder.Append(b.ToString("X2"));
}
return stringBuilder.ToString();
}
// Token: 0x0600000C RID: 12 RVA: 0x000021C8 File Offset: 0x000003C8
public void Spawn()
{
FruitSpawner component = GameObject.FindWithTag("GameController").GetComponent<FruitSpawner>();
if (component)
{
if (this.audioSources.Length != 0)
{
this.audioSources[Random.Range(0, this.audioSources.Length)].Play();
}
component.Spawn(this.toSpawn);
string name = this.toSpawn.name;
if (name == "汉堡底" && Init.spawnCount == 0)
{
Init.secret += 997;
}
else if (name == "鸭屁股")
{
Init.secret -= 127;
}
else if (name == "胡罗贝")
{
Init.secret *= 3;
}
else if (name == "臭豆腐")
{
Init.secret ^= 18;
}
else if (name == "俘虏")
{
Init.secret += 29;
}
else if (name == "白拆")
{
Init.secret -= 47;
}
else if (name == "美汁汁")
{
Init.secret *= 5;
}
else if (name == "柠檬")
{
Init.secret ^= 87;
}
else if (name == "汉堡顶" && Init.spawnCount == 5)
{
Init.secret ^= 127;
string str = Init.secret.ToString();
if (ButtonSpawnFruit.Sha1(str) == "DD01903921EA24941C26A48F2CEC24E0BB0E8CC7")
{
this.result = "BJDCTF{" + ButtonSpawnFruit.Md5(str) + "}";
Debug.Log(this.result);
}
}
Init.spawnCount++;
Debug.Log(Init.secret);
Debug.Log(Init.spawnCount);
}
}
|
基本思路是已知sha1(str)==DD01903921EA24941C26A48F2CEC24E0BB0E8CC7,求md5(str)
还是根据题解上cmd5.com得str==1001
然后md5加密,注意这一句,取加密后的0-20(不包括20)即前20位(前人之鉴属于是
1
|
return stringBuilder.ToString().Substring(0, 20)
|
ipython跑一遍得flag
1
2
3
4
5
|
In [1]: from hashlib import md5
In [2]: a=md5()
In [3]: a.update(b"1001")
In [4]: print("BJDCTF{"+a.hexdigest()[:20]+"}")
BJDCTF{b8c37e33defde51cf91e}
|
总结:这周题真是给我开眼界的(
第一周
2021-10-31
写在前面:
-
新人第一次发帖,多多指教
-
长篇幅警告,写得比较繁琐
-
主要记录自己的做题过程,当然还有水水水
0x00 DMCTF 2020 re1
来点简单的算法甜点
解压后只有一个exe文件,先运行看看
提示输入flag,乱输提示 wrong flag!
1
2
3
|
flag:134123421
wrong flag!
请按任意键继续. . .
|
拖进die,无壳,进ida
看不懂汇编,直接F5,进main_0函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
char Str; // [esp+DCh] [ebp-40h] BYREF
char v5[54]; // [esp+DDh] [ebp-3Fh] BYREF
__CheckForDebuggerJustMyCode(&unk_5E0029);
Str = 0;
j__memset(v5, 0, sizeof(v5));
sub_485257("flag:");
sub_484CF3(&dword_5DD268, &Str);
if ( sub_485D1A(&Str) )
sub_4849EC(&Str);
else
sub_485257("wrong flag!\n");
sub_488209("pause");
return 0;
}
|
发现有用的主要在if else
先进if判断条件函数sub_485D1A
1
2
3
4
5
|
BOOL __cdecl sub_49BE80(char *Str)
{
__CheckForDebuggerJustMyCode(&unk_5E0029);
return j__strlen(Str) == 20;
}
|
第一行看不懂也感觉没用,直接忽略,第二行限制输入为20个字符
回来再进if下函数sub_4849EC
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
|
int __cdecl sub_49AE10(char *Str1)
{
int result; // eax
int i; // [esp+D0h] [ebp-2Ch]
char Str2[28]; // [esp+DCh] [ebp-20h] BYREF
__CheckForDebuggerJustMyCode(&unk_5E0029);
strcpy(Str2, "fmesh{umkc_vlrn_glh}");
for ( i = 0; i <= 19; ++i )
{
if ( Str1[i] < 97 || Str1[i] >= 108 )
{
if ( Str1[i] > 110 && Str1[i] <= 122 )
--Str1[i];
}
else
{
Str1[i] += 2;
}
}
if ( !j__strcmp(Str1, Str2) )
result = sub_485257("congratulations!\n");
else
result = sub_485257("wrong flag!\n");
return result;
}
|
发现是个简单的加密
加改成减,减改成加,逆写得到flag,粘个代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#include<cstdio>
char Str1[]="fmesh{umkc_vlrn_glh}";
int main(){
for(int i=0;i<=19;i++){
if ( Str1[i] < 97 || Str1[i] >= 108 ){
if ( Str1[i] > 110 && Str1[i] <= 122 )
Str1[i]++;
}
else{
Str1[i] -= 2;
}
}
puts(Str1);
}
//flag:dmctf{vmia_wlsn_elf}
|
然而据说这题多解,咱也不知道,咱也不敢问
0x01 DMCTF 2020 re4
“最短路径”
直接打开exe,输入key闪退差评
cmd打开
1
2
3
4
5
6
7
8
9
10
|
C:\Users\DX3906>D:\DX3906\下载\12345\12345.exe
key:1234123
failed.
failed.
failed.
failed.
failed.
failed.
failed.
failed.
|
8个failed直呼好狠
die查壳:无,拖进ida
直接F5,找不到主函数
1
2
3
4
5
|
void __noreturn start()
{
_set_app_type(_crt_console_app);
sub_4011B0();
}
|
打开string窗口
1
2
3
4
5
6
7
8
9
10
11
|
.rdata:00405000 00000013 C libgcc_s_dw2-1.dll
.rdata:00405013 00000016 C __register_frame_info
.rdata:00405029 00000018 C __deregister_frame_info
.rdata:00405041 0000000E C libgcj-16.dll
.rdata:0040504F 00000014 C _Jv_RegisterClasses
.rdata:0040506A 00000008 C failed.
.rdata:00405072 0000000A C flag{%s}\n
.rdata:00405080 00000018 C Mingw runtime failure:\n
.rdata:00405098 00000031 C VirtualQuery failed for %d bytes at address %p
.rdata:004050CC 00000032 C Unknown pseudo relocation protocol version %d.\n
.rdata:00405100 0000002A C Unknown pseudo relocation bit size %d.\n
|
映入眼帘就是failed
点他——
rdata:0040506A Buffer db 'failed.',0 ; DATA XREF: sub_401460:loc_4015A3↑o
发现函数sub_401460,点他弹出来汇编,再F5,终于进了真的主函数
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
|
int sub_401460()
{
int v0; // eax
char Buffer[51]; // [esp+3Dh] [ebp-43h] BYREF
int v3; // [esp+70h] [ebp-10h]
int i; // [esp+74h] [ebp-Ch]
int v5; // [esp+78h] [ebp-8h]
int v6; // [esp+7Ch] [ebp-4h]
sub_401BD0();
printf("key:");
fgets(Buffer, 50, (FILE *)iob[0]._ptr);
v3 = strlen(Buffer);
if ( Buffer[v3 - 1] == 10 )
Buffer[v3 - 1] = 0;
--v3;
v6 = 0;
v5 = 0;
for ( i = 0; i < v3; ++i )
{
v0 = Buffer[i];
if ( v0 == 0x31 )
{
--v5;
}
else if ( v0 > 49 )
{
if ( v0 == 50 )
{
++v6;
}
else
{
if ( v0 != 51 )
{
LABEL_16:
puts("failed.");
goto LABEL_17;
}
--v6;
}
}
else
{
if ( v0 != 48 )
goto LABEL_16;
++v5;
}
LABEL_17:
if ( !byte_404020[10 * v6 + v5] || v6 < 0 || v5 < 0 || v6 > 9 || v5 > 9 )
puts("failed.");
}
if ( v6 == 9 && v5 == 9 )
printf("flag{%s}\n", Buffer);
return 0;
}
|
观察LABEL_17中if的判断条件,咋看咋像迷宫题,v6表示行,v5表示列,0-9共10行10列
进数组byte_404020,数据都是0和1,更加确信迷宫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
.data:00404020 byte_404020 db 1 ; DATA XREF: sub_401460+164↑r
.data:00404021 db 0
.data:00404022 db 0
.data:00404023 db 0
.data:00404024 db 0
.data:00404025 db 1
.data:00404026 db 1
.data:00404027 db 0
.data:00404028 db 0
.data:00404029 db 0
.data:0040402A db 1
.data:0040402B db 1
.data:0040402C db 0
.data:0040402D db 0
.data:0040402E db 0
.data:0040402F db 1
.data:00404030 db 1
.data:00404031 db 1
.data:00404032 db 0
|
但,这要我一个个扣出来??
右键选择Array
然后把size改成100(10行10列共100个数
舒服~
1
2
3
4
5
6
7
|
.data:00404020 byte_404020 db 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1; 0
.data:00404020 ; DATA XREF: sub_401460+164↑r
.data:00404020 db 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1; 18
.data:00404020 db 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0; 36
.data:00404020 db 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0; 54
.data:00404020 db 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1; 72
.data:00404020 db 0, 0, 0, 0, 0, 0, 0, 0, 0, 1; 90
|
复制粘贴出迷宫
1
2
3
4
5
6
7
8
9
10
|
1, 0, 0, 0, 0, 1, 1, 0, 0, 0,
1, 1, 0, 0, 0, 1, 1, 1, 0, 0,
0, 1, 0, 0, 0, 1, 0, 1, 0, 0,
0, 1, 1, 1, 1, 1, 0, 1, 0, 0,
0, 0, 0, 0, 1, 1, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
|
回头再看函数
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
|
for ( i = 0; i < v3; ++i )
{
v0 = Buffer[i];
if ( v0 == 0x31 )
{
--v5;
}
else if ( v0 > 49 )
{
if ( v0 == 50 )
{
++v6;
}
else
{
if ( v0 != 51 )
{
LABEL_16:
puts("failed.");
goto LABEL_17;
}
--v6;
}
}
else
{
if ( v0 != 48 )
goto LABEL_16;
++v5;
}
LABEL_17:
if ( !byte_404020[10 * v6 + v5] || v6 < 0 || v5 < 0 || v6 > 9 || v5 > 9 )
puts("failed.");
}
|
易得
1
2
3
4
|
0 ++v5 right
1 --v5 left
2 ++v6 down
3 --v6 up
|
手动走迷宫得 key:20220002033330202222202202
回cmd运行得flag
1
2
|
key:20220002033330202222202202
flag{20220002033330202222202202}
|
0x02 GWCTF 2019 xxor
快乐的解方程题目
解压打开发现不是exe,无法打开直接拖die
发现是elf64(linux可执行文件),马上去开虚拟机
ubuntu里先运行一下
1
2
3
4
5
6
7
8
9
10
11
12
|
dx3906@ubuntu:~/Desktop$ '/home/dx3906/attachment'
Let us play a game?
you have six chances to input
Come on!
input: 1231212
input: 3
input: 12
input: 3
input: 12
input: 3
Wrong!
NO NO NO~
|
发现题目需要输入六次,拖ida F5
在左边Functions Window找到了main函数,竟然是真的
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
|
__int64 __fastcall main(int a1, char **a2, char **a3)
{
int i; // [rsp+8h] [rbp-68h]
int j; // [rsp+Ch] [rbp-64h]
__int64 v6[6]; // [rsp+10h] [rbp-60h] BYREF
__int64 v7[6]; // [rsp+40h] [rbp-30h] BYREF
v7[5] = __readfsqword(0x28u);
puts("Let us play a game?");
puts("you have six chances to input");
puts("Come on!");
v6[0] = 0LL;
v6[1] = 0LL;
v6[2] = 0LL;
v6[3] = 0LL;
v6[4] = 0LL;
for ( i = 0; i <= 5; ++i )
{
printf("%s", "input: ");
__isoc99_scanf("%d", (char *)v6 + 4 * i);
}
v7[0] = 0LL;
v7[1] = 0LL;
v7[2] = 0LL;
v7[3] = 0LL;
v7[4] = 0LL;
for ( j = 0; j <= 2; ++j )
{
dword_601078 = v6[j];
dword_60107C = HIDWORD(v6[j]);
sub_400686(&dword_601078, &unk_601060);
LODWORD(v7[j]) = dword_601078;
HIDWORD(v7[j]) = dword_60107C;
}
if ( (unsigned int)sub_400770(v7) != 1 )
{
puts("NO NO NO~ ");
exit(0);
}
puts("Congratulation!\n");
puts("You seccess half\n");
puts("Do not forget to change input to hex and combine~\n");
puts("ByeBye");
return 0LL;
}
|
倒着看,先进if里面的判断函数sub_400770(v7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
__int64 __fastcall sub_400770(_DWORD *a1)
{
__int64 result; // rax
if ( a1[2] - a1[3] == 0x84A236FFLL
&& a1[3] + a1[4] == 0xFA6CB703LL
&& a1[2] - a1[4] == 0x42D731A8LL
&& *a1 == 0xDF48EF7E
&& a1[5] == 0x84F30420
&& a1[1] == 0x20CAACF4 )
{
puts("good!");
result = 1LL;
}
else
{
puts("Wrong!");
result = 0LL;
}
return result;
}
|
果然如题目解方程,z3搞它
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from z3 import *
a = Int('a')
b = Int('b')
c = Int('c')
d = Int('d')
e = Int('e')
f = Int('f')
s = Solver()
s.add(a == 0xDF48EF7E)
s.add(b == 0x20CAACF4)
s.add(f == 0x84F30420)
s.add(c-d == 0x84A236FF)
s.add(d+e == 0xFA6CB703)
s.add(c-e == 0x42D731A8)
print(s.check())
print(s.model())
|
a-f表示a1的0-5,z3里不会定义数组,干脆用字母,会的佬教一下孩子
得到结果
1
2
3
4
5
6
7
|
sat
[c = 3774025685,
d = 1548802262,
e = 2652626477,
f = 2230518816,
b = 550153460,
a = 3746099070]
|
但这还没完(要是光解方程可能也做不了第三题
解出来的是加密后的input,加密算法如下
1
2
3
4
5
6
7
8
|
for ( j = 0; j <= 2; ++j )
{
dword_601078 = v6[j];
dword_60107C = HIDWORD(v6[j]);
sub_400686(&dword_601078, &unk_601060);
LODWORD(v7[j]) = dword_601078;
HIDWORD(v7[j]) = dword_60107C;
}
|
基本是把高四字节(HIDWORD)和低四字节(LODWORD)分开一顿操作,进函数sub_400686
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
__int64 __fastcall sub_400686(unsigned int *a1, _DWORD *a2)
{
__int64 result; // rax
unsigned int v3; // [rsp+1Ch] [rbp-24h]
unsigned int v4; // [rsp+20h] [rbp-20h]
int v5; // [rsp+24h] [rbp-1Ch]
unsigned int i; // [rsp+28h] [rbp-18h]
v3 = *a1;
v4 = a1[1];
v5 = 0;
for ( i = 0; i <= 0x3F; ++i )
{
v5 += 0x458BCD42;
v3 += (v4 + v5 + 11) ^ ((v4 << 6) + *a2) ^ ((v4 >> 9) + a2[1]) ^ 0x20;
v4 += (v3 + v5 + 20) ^ ((v3 << 6) + a2[2]) ^ ((v3 >> 9) + a2[3]) ^ 0x10;
}
*a1 = v3;
result = v4;
a1[1] = v4;
return result;
}
|
感觉好像我的二面题,tea加密?
变量只有v3,v4,且求v3时v4已知,求v4时v3已知
结论:可逆,难点在于各种数据类型容易把自己搞晕
注意主函数input格式控制为%d,故用int存flag
1
|
__isoc99_scanf("%d", (char *)v6 + 4 * i);
|
完整代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
#include<cstdio>
unsigned int flag[100],a1[]={3746099070,550153460,3774025685,1548802262,2652626477,2230518816};
unsigned long long a2[10];
unsigned int key[5]={2,2,3,4};
int v5;
int main(){
for(int i=0;i<3;i++){
a2[i]=*((unsigned long long*)a1+i);
}
for(int i=0;i<3;i++){
unsigned int xl=*((unsigned int*)&a2[i]);//低位
unsigned int xh=*((unsigned int*)&a2[i]+1);//高位
for(int j=0x3F;j>=0;j--){
v5=(j+1)*0x458BCD42;
xh-=(xl+v5+20)^((xl<<6)+key[2])^((xl>>9)+key[3])^0x10;
xl-=(xh+v5+11)^((xh<<6)+key[0])^((xh>>9)+key[1])^0x20;
}
flag[2*i]=xl;
flag[2*i+1]=xh;
}
for(int i=0;i<6;i++){
printf("%d\n",flag[i]);
}
}
|
输出6个数字
1
2
3
4
5
6
|
6712417
6781810
6643561
7561063
7497057
7610749
|
回 Ubuntu input
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
dx3906@ubuntu:~/Desktop$ '/home/dx3906/attachment'
Let us play a game?
you have six chances to input
Come on!
input: 6712417
input: 6781810
input: 6643561
input: 7561063
input: 7497057
input: 7610749
good!
Congratulation!
You seccess half
Do not forget to change input to hex and combine~
ByeBye
|
成功了,但还没有完全成功
hex???百度先
偷懒得flag,真香
当然还是要认真学一学(
Base64与Hex编码
最后,手写解码奉上(使用上一段代码的flag数组)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
bool f=0;
int t,cnt=0,ans[10];
for(int i=0;i<6;i++){
cnt=0;
while(flag[i]){
if(!f){
t=flag[i]%16;
flag[i]/=16;
f=1;
}
if(f){
t+=flag[i]%16<<4;
flag[i]/=16;
ans[++cnt]=t;
f=0;
}
}
for(int j=cnt;j>=1;j--){
printf("%c",ans[j]);
}
}
|