0%

noraml14-17

normal14-17

normal14

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
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, (__int64)v9);
__gmpz_init_set_str((__int64)v7, (__int64)"ad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35", 16LL);
__gmpz_init_set_str((__int64)v6, (__int64)v9, 16LL);
__gmpz_init_set_str(
(__int64)v4,
(__int64)"103461035900816914121390101299049044413950405173712170434161686539878160984549",
10LL);
__gmpz_init_set_str((__int64)v5, (__int64)"65537", 10LL);
__gmpz_powm((__int64)v6, (__int64)v6, (__int64)v5, (__int64)v4);
if ( (unsigned int)__gmpz_cmp((__int64)v6, (__int64)v7) )
puts("GG!");
else
puts("TTTTTTTTTTql!");
return 0LL;
}

这个题我想查看函数发现没东西,那么证明这个函数是c中自带的,但是这个我感觉好熟悉。看到65537,多半是RSA,当时极客大挑战做过几道,就直接给脚本了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from gmpy2 import*
from libnum import*
from Crypto.Util.number import long_to_bytes
import gmpy2
n=103461035900816914121390101299049044413950405173712170434161686539878160984549
c=0xad939ff59f6e70bcbfad406f2494993757eee98b91bc244184a377520d06fc35
e=65537
p=366669102002966856876605669837014229419
q=282164587459512124844245113950593348271
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
flag=pow(c,int(d),n)
print(n2s(flag))
#b'suctf{Pwn_@_hundred_years}'

我们将n分为两个质因数,则剩下的就是c,由此可以求flag

normal16

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
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
DWORD v3; // eax
DWORD v4; // eax
char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
int v7; // [esp+150h] [ebp-20Ch]
char String1[260]; // [esp+154h] [ebp-208h] BYREF
char Destination[260]; // [esp+258h] [ebp-104h] BYREF

memset(Destination, 0, sizeof(Destination));
memset(String1, 0, sizeof(String1));
v7 = 0;
printf("pls input the first passwd(1): ");
scanf("%s", Destination);
if ( strlen(Destination) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
v7 = atoi(Destination); //将Destination转成int类型说明Destination只能是数字,如果有字母,return 0
if ( v7 < 100000 )
ExitProcess(0);
strcat(Destination, "@DBApp"); //六位数字后连接字符串
v3 = strlen(Destination);
sub_40100A((BYTE *)Destination, v3, String1); //第一个函数
if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )
{
printf("continue...\n\n");
printf("pls input the first passwd(2): ");
memset(Str, 0, sizeof(Str));
scanf("%s", Str);
if ( strlen(Str) != 6 )
{
printf("Must be 6 characters!\n");
ExitProcess(0);
}
strcat(Str, Destination); //password2加上password1
memset(String1, 0, sizeof(String1));
v4 = strlen(Str);
sub_401019((BYTE *)Str, v4, String1);
if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )
{
if ( !(unsigned __int8)sub_40100F(Str) )
{
printf("Error!!\n");
ExitProcess(0);
}
printf("bye ~~\n");
}
}
return 0;
}

这道题只看主函数应该是很好理解的 。

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
int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
DWORD i; // [esp+4Ch] [ebp-28h]
CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF

if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
return 0;
}
}

这个里面全是api函数,根据参数来确定是哪种hash加密。0x8004u对应sha1加密,我学习了一下脚本的写法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
password1 = ""
arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
for i1 in arr:
for i2 in arr:
for i3 in arr:
for i4 in arr:
for i5 in arr:
for i6 in arr:
password1 = i1 + i2 + i3 + i4 + i5 + i6 + "@DBApp"
if(hashlib.sha1(password1.encode("utf-8")).hexdigest().upper() =="6E32D0943418C2C33385BC35A1470250DD8923A9"):
print(password1)
break
#hash引入hashlib库,用hashlib.sha1(a.encode("utf-8"))来编码,要指名编码的格式,不然会报错,hexdigest函数是转换成16进制,upper是将小写转换成大写。
#password1=123321@DBApp

从参数来看第二个函数:

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
int __cdecl sub_401040(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
DWORD i; // [esp+4Ch] [ebp-24h]
CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF

if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
return 0;
if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
{
if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
{
CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
*lpString1 = 0;
for ( i = 0; i < pdwDataLen; ++i )
{
wsprintfA(String2, "%02X", v6[i]);
lstrcatA(lpString1, String2);
}
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 1;
}
else
{
CryptDestroyHash(phHash);
CryptReleaseContext(phProv, 0);
return 0;
}
}
else
{
CryptReleaseContext(phProv, 0);
return 0;
}
}

参数为:0x8003u是个md5函数,但md5爆破的话是很慢的。所以我们找到最后对比时的函数:

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
char __cdecl sub_4014D0(LPCSTR lpString)
{
LPCVOID lpBuffer; // [esp+50h] [ebp-1Ch]
DWORD NumberOfBytesWritten; // [esp+58h] [ebp-14h] BYREF
DWORD nNumberOfBytesToWrite; // [esp+5Ch] [ebp-10h]
HGLOBAL hResData; // [esp+60h] [ebp-Ch]
HRSRC hResInfo; // [esp+64h] [ebp-8h]
HANDLE hFile; // [esp+68h] [ebp-4h]

hFile = 0;
hResData = 0;
nNumberOfBytesToWrite = 0;
NumberOfBytesWritten = 0;
hResInfo = FindResourceA(0, (LPCSTR)0x65, "AAA");
if ( !hResInfo )
return 0;
nNumberOfBytesToWrite = SizeofResource(0, hResInfo);
hResData = LoadResource(0, hResInfo);
if ( !hResData )
return 0;
lpBuffer = LockResource(hResData);
sub_401005(lpString, (int)lpBuffer, nNumberOfBytesToWrite);
hFile = CreateFileA("dbapp.rtf", 0x10000000u, 0, 0, 2u, 0x80u, 0);
if ( hFile == (HANDLE)-1 )
return 0;
if ( !WriteFile(hFile, lpBuffer, nNumberOfBytesToWrite, &NumberOfBytesWritten, 0) )
return 0;
CloseHandle(hFile);
return 1;
}

查阅了一下:FindResourceA:确定指定模块中指定类型和名称的资源的位置。

RTF格式:{\rtf1\ansiHello!\parThis is some {\b bold} text.\par}

我们分析一下,首先用ResourceHacker软件查找名为“AAA”的资源并取出数据,接着进入sub_401005,取出的数据跟

输入的密码2+输入的密码1+“@DBApp”进行了异或,最后生成了.rtf文件,最后的flag应该就在.rtf文件里。

那么如何求密码2呢,我们只需要找到rtf文件将其文件头和AAA前6位异或就行了

1
2
3
4
5
6
7
8
9
rtf="{\\rtf1"
AAA=[0x05,0x7D,0x41,0x15,0x26,0x01]
password2=""
for i in range(6):
password2 += chr(AAA[i] ^ ord(rtf[i]))
print(password2)
#~!3a@0


我们运行程序输入密码,回出现一个文件里面存有flag

Flag{N0_M0re_Free_Bugs}

normal17

刚开始没法反编译,

我们找到这里,是栈帧不平衡导致的,将pop处的改为0x0,即可。修改的原则是,只需在出现负数的那个地方的上一行,按alt+k,调整成跟这个数一摸一样的值就可以了。

我们找到main函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
HANDLE Thread; // [esp+D0h] [ebp-14h]
HANDLE hObject; // [esp+DCh] [ebp-8h]

sub_4110FF();
::hObject = CreateMutexW(0, 0, 0);
j_strcpy(Destination, &Source);
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
Thread = CreateThread(0, 0, sub_41119F, 0, 0, 0);
CloseHandle(hObject);
CloseHandle(Thread);
while ( dword_418008 != -1 )
;
sub_411190();
CloseHandle(::hObject);
return 0;
}

稍微能看懂一点,hObject一般代表句柄,CreateThread代表着创建线程,看来这个题创建了两个线程.

  1. 进程
    进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

  2. 线程
    线程是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

一个正在运行的软件(如迅雷)就是一个进程,一个进程可以同时运行多个任务( 迅雷软件可以同时下载多个文件,每个下载任务就是一个线程), 可以简单的认为进程是线程的集合。

  • 对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。
  • 对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的。

第一个线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char *__cdecl sub_411940(int a1, int a2)
{
char *result; // eax
char v3; // [esp+D3h] [ebp-5h]

v3 = *(_BYTE *)(a2 + a1);
if ( (v3 < 'a' || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);
if ( v3 < 97 || v3 > 122 )
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 38];
}
else
{
result = off_418000[0];
*(_BYTE *)(a2 + a1) = off_418000[0][*(char *)(a2 + a1) - 96];
}
return result;
}

这个线程的很清楚,就是加加减减。

第二个线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
void __stdcall __noreturn sub_411B10(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_418008 > -1 )
{
Sleep(0x64u);
--dword_418008;
}
ReleaseMutex(hObject);
}
}

CreateThread API 会创建新线程,这个题是一个典型的双线程问题.CreateMutex 创建一个互斥体,用于防止多线程中出现资源争用,即多个线程同时读写同一个资源的情况,所创建的互斥体的句柄会存到全局变量 hObject 中,WaitForSingleObject 等待互斥体的使用权空闲出来,并获取使用权,然后再访问和其他线程共享的资源,访问完后,用 ReleaseMutex 释放使用权,给其他线程使用的机会。那么这两个线程共享的数据是什么呢,看参数我们就知道是dword_418008。

这两个线程一前一后创建,理论上是 StartAddress 先获得使用权,后来的 sub_41119F 进入等待状态,前者执行一次循环后释放使用权,与此同时后者等待结束、获得使用权,进入循环,循环完后释放使用权,前者又获得使用权,如此循环往复。

所以就相当于我给出的字符串是第一位照抄,第二位进行线程一的变化。经过线程后与Source对比

1
2
3
4
5
6
7
8
9
10
int sub_411880()
{
int i; // [esp+D0h] [ebp-8h]
for ( i = 0; i < 29; ++i )
{
if ( Source[i] != off_418004[i] )
exit(0);
}
return printf("\nflag{%s}\n\n", Destination);
}

给出脚本:

1
2
3
4
5
6
7
8
9
10
11
12
data1="TOiZiZtOrYaToUwPnToBsOaOapsyS"
data2="QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"
flag=""
for i in range(len(data1)):
if i%2==0:
flag+=data1[i]
else:
if ord(data1[i])>ord('a') and ord(data1[i])<ord('z'):
flag+=chr(data2.find(data1[i])+38)
else:
flag+=chr(data2.find(data1[i])+96)
print(flag)

最后这个E是什么都行,因为dword_418008只有29位!

得到ThisisthreadofwindowshahaIsES