一.[ACTF新生赛2020]usualCrypt1
查壳ida分析shitf+f12字符串查找
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // esi
int v5[3]; // [esp+8h] [ebp-74h] BYREF
__int16 v6; // [esp+14h] [ebp-68h]
char v7; // [esp+16h] [ebp-66h]
char v8[100]; // [esp+18h] [ebp-64h] BYREF
sub_403CF8(&unk_40E140);
scanf("%s", v8);
memset(v5, 0, sizeof(v5));
v6 = 0;
v7 = 0;
sub_401080(v8, strlen(v8), v5);
v3 = 0;
while ( *((_BYTE *)v5 + v3) == byte_40E0E4[v3] )
{
if ( ++v3 > strlen((const char *)v5) )
goto LABEL_6;
}
sub_403CF8(aError);
LABEL_6:
if ( v3 - 1 == strlen(byte_40E0E4) )
return sub_403CF8(aAreYouHappyYes);
else
return sub_403CF8(aAreYouHappyNo);
}
进入主函数,输入v8进行sub_401080函数操作让后和byte_40E0E4相同
ida中按"a"是生成字符串的快捷键,
所以是字符串zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9
查看sub_401080函数
int __cdecl sub_401080(int a1, int a2, int a3)
{
int v3; // edi
int v4; // esi
int v5; // edx
int v6; // eax
int v7; // ecx
int v8; // esi
int v9; // esi
int v10; // esi
int v11; // esi
_BYTE *v12; // ecx
int v13; // esi
int v15; // [esp+18h] [ebp+8h]
v3 = 0;
v4 = 0;
sub_401000();
v5 = a2 % 3;
v6 = a1;
v7 = a2 - a2 % 3;
v15 = a2 % 3;
if ( v7 > 0 )
{
do
{
LOBYTE(v5) = *(_BYTE *)(a1 + v3);
v3 += 3;
v8 = v4 + 1;
*(_BYTE *)(v8 + a3 - 1) = byte_40E0A0[(v5 >> 2) & 0x3F];
*(_BYTE *)(++v8 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(a1 + v3 - 3) & 3)
+ (((int)*(unsigned __int8 *)(a1 + v3 - 2) >> 4) & 0xF)];
*(_BYTE *)(++v8 + a3 - 1) = byte_40E0A0[4 * (*(_BYTE *)(a1 + v3 - 2) & 0xF)
+ (((int)*(unsigned __int8 *)(a1 + v3 - 1) >> 6) & 3)];
v5 = *(_BYTE *)(a1 + v3 - 1) & 0x3F;
v4 = v8 + 1;
*(_BYTE *)(v4 + a3 - 1) = byte_40E0A0[v5];
}
while ( v3 < v7 );
v5 = v15;
}
if ( v5 == 1 )
{
LOBYTE(v7) = *(_BYTE *)(v3 + a1);
v9 = v4 + 1;
*(_BYTE *)(v9 + a3 - 1) = byte_40E0A0[(v7 >> 2) & 0x3F];
v10 = v9 + 1;
*(_BYTE *)(v10 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(v3 + a1) & 3)];
*(_BYTE *)(v10 + a3) = 61;
LABEL_8:
v13 = v10 + 1;
*(_BYTE *)(v13 + a3) = 61;
v4 = v13 + 1;
goto LABEL_9;
}
if ( v5 == 2 )
{
v11 = v4 + 1;
*(_BYTE *)(v11 + a3 - 1) = byte_40E0A0[((int)*(unsigned __int8 *)(v3 + a1) >> 2) & 0x3F];
v12 = (_BYTE *)(v3 + a1 + 1);
LOBYTE(v6) = *v12;
v10 = v11 + 1;
*(_BYTE *)(v10 + a3 - 1) = byte_40E0A0[16 * (*(_BYTE *)(v3 + a1) & 3) + ((v6 >> 4) & 0xF)];
*(_BYTE *)(v10 + a3) = byte_40E0A0[4 * (*v12 & 0xF)];
goto LABEL_8;
}
LABEL_9:
*(_BYTE *)(v4 + a3) = 0;
return sub_401030(a3);
}
byte_40E0A0查看
同样生成字符串
可知是base64加密,找在线网站解密即可,
但是结果是错误的,我们再仔细看看哪里出了问题,一般主函数分析的没有遗漏,我们看看base64这个函数有没有改动,
这里有一个函数,点进去看看
int sub_401000()
{
int result; // eax
char v1; // cl
for ( result = 6; result < 15; ++result )
{
v1 = aAbcdefghijklmn[result + 10];
aAbcdefghijklmn[result + 10] = aAbcdefghijklmn[result];
aAbcdefghijklmn[result] = v1;
}
return result;
}
猜测和base64变表有关,直接c语言运行
#include<stdio.h>
#include<stdlib.h>
int main()
{
int i;
int result;
char v1;
char base64code[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for ( result = 6; result < 15; ++result )
{
v1 = base64code[result + 10];
base64code[result + 10] = base64code[result];
base64code[result] = v1;
}
for(i=0;i<64;i++){
printf("%c",base64code[i]);
}
return 0;
}
ABCDEFQRSTUVWXYPGHIJKLMNOZabcdefghijklmnopqrstuvwxyz0123456789+/
在线自定义base64编解码、在线二进制转可打印字符、在线base2、base4、base8、base16、base32、base64--查错网
自定义base64加密答案也不正确,我们再仔细看
发现
返回处也有一个函数操作
int __cdecl sub_401030(const char *a1)
{
__int64 v1; // rax
char v2; // al
v1 = 0i64;
if ( strlen(a1) )
{
do
{
v2 = a1[HIDWORD(v1)];
if ( v2 < 97 || v2 > 122 )
{
if ( v2 < 65 || v2 > 90 )
goto LABEL_9;
LOBYTE(v1) = v2 + 32;
}
else
{
LOBYTE(v1) = v2 - 32;
}
a1[HIDWORD(v1)] = v1;
LABEL_9:
LODWORD(v1) = 0;
++HIDWORD(v1);
}
while ( HIDWORD(v1) < strlen(a1) );
}
return v1;
}
可知是把大小写字母转换,我们写代码实现
#include<stdio.h>
#include<stdlib.h>
int main()
{
char a[]="zMXHz3TIgnxLxJhFAdtZn2fFk3lYCrtPC2l9";
int i;
for(i=0;a[i]!='\0';i++)
{
if(a[i]>='A'&&a[i]<='Z'){
a[i]+=32;
}else if(a[i]>='a'&&a[i]<='z'){
a[i]-=32;
}
}
for(i=0;a[i]!='\0';i++)
{
printf("%c",a[i]);
}
return 0;
}
ZmxhZ3tiGNXlXjHfaDTzN2FfK3LycRTpc2L9
之后在线网站解密
flag{bAse64_h2s_a_Surprise}
二.[HDCTF2019]Maze1
https://files.buuoj.cn/files/23766843c5b14f1bcc1e9e00e3a761db/attachment.zip
先查壳,发现是upx 32bit,先脱壳,之后ida分析
再看看发现是花指令,nop掉call处的函数,对主函数u c p
进入主函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // [esp+10h] [ebp-14h]
char v5[16]; // [esp+14h] [ebp-10h] BYREF
sub_401140(aGoThroughTheMa);
scanf("%14s", v5);
for ( i = 0; i <= 13; ++i )
{
switch ( v5[i] )
{
case 'a':
--*(_DWORD *)asc_408078;
break;
case 'd':
++*(_DWORD *)asc_408078;
break;
case 's':
--dword_40807C;
break;
case 'w':
++dword_40807C;
break;
default:
continue;
}
}
if ( *(_DWORD *)asc_408078 == 5 && dword_40807C == -4 )
{
sub_401140(aCongratulation);
sub_401140(aHereIsTheFlagF);
}
else
{
sub_401140(aTryAgain);
}
return 0;
}
猜测和迷宫问题有关,这时候要找地图,shitf+f12查找字符串
这一个是地图
注意下面还有一部分
*******+********* ****** **** ******* **F****** **************
一共是70个,地图一般是矩形的,所以试试10*7
可知正确,其实数一共多少个的时候就是按照10个一行这样数出来的
flag{ssaaasaassdddw}
三.[WUSTCTF2020]level3 1
查壳,ida分析,进入主函数
int __fastcall main(int argc, const char **argv, const char **envp)
{
char *v3; // rax
char v5; // [rsp+Fh] [rbp-41h]
char v6[56]; // [rsp+10h] [rbp-40h] BYREF
unsigned __int64 v7; // [rsp+48h] [rbp-8h]
v7 = __readfsqword(0x28u);
printf("Try my base64 program?.....\n>");
__isoc99_scanf("%20s", v6);
v5 = time(0LL);
srand(v5);
if ( (rand() & 1) != 0 )
{
v3 = base64_encode(v6);
puts(v3);
puts("Is there something wrong?");
}
else
{
puts("Sorry I think it's not prepared yet....");
puts("And I get a strange string from my program which is different from the standard base64:");
puts("d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==");
puts("What's wrong??");
}
return 0;
}
char *__fastcall base64_encode(char *a1)
{
int v1; // eax
int v2; // eax
int v4; // [rsp+1Ch] [rbp-54h]
int v5; // [rsp+20h] [rbp-50h]
int v6; // [rsp+24h] [rbp-4Ch]
int v7; // [rsp+28h] [rbp-48h]
int v8; // [rsp+2Ch] [rbp-44h]
char src[56]; // [rsp+30h] [rbp-40h] BYREF
unsigned __int64 v10; // [rsp+68h] [rbp-8h]
v10 = __readfsqword(0x28u);
v1 = strlen(a1);
v8 = v1 % 3;
v7 = v1 / 3;
memset(src, 0, 0x30uLL);
v6 = 0;
v4 = 0;
v5 = 0;
while ( v4 < v7 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
src[v6 + 2] = base64_table[(4 * (a1[v5 + 1] & 0xF)) | (a1[v5 + 2] >> 6)];
v2 = v6 + 3;
v6 += 4;
src[v2] = base64_table[a1[v5 + 2] & 0x3F];
v5 += 3;
++v4;
}
if ( v8 == 1 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[16 * (a1[v5] & 3)];
strcat(src, "==");
}
else if ( v8 == 2 )
{
src[v6] = base64_table[a1[v5] >> 2];
src[v6 + 1] = base64_table[(16 * (a1[v5] & 3)) | (a1[v5 + 1] >> 4)];
src[v6 + 2] = base64_table[4 * (a1[v5 + 1] & 0xF)];
src[v6 + 3] = 61;
}
strcpy(a1, src);
return a1;
}
是base64加密,因为加密之后一般都有有填充符"=",所以猜测d2G0ZjLwHjS7DmOzZAY0X2lzX3CoZV9zdNOydO9vZl9yZXZlcnGlfD==
是密文在线自定义base64编解码、在线二进制转可打印字符、在线base2、base4、base8、base16、base32、base64--查错网
结果不对,再看原题
猜测和base64变表有关,但是也没找到,要相信直觉,一点一点分析不同处,
之后我查看了base64_table的引用处,
一个一个看,发现了不同的函数,点进去
找到了,写代码实现
#include<stdio.h>
#include<stdlib.h>
int main()
{
char v1,result;
int i;
char base64_table[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for ( i = 0; i <= 9; ++i )
{
v1 = base64_table[i];
base64_table[i] = base64_table[19 - i];
result = 19 - i;
base64_table[result] = v1;
}
printf("%s",base64_table);
return 0;
}
TSRQPONMLKJIHGFEDCBAUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
wctf2020{Base64_is_the_start_of_reverse}
四.BUUCTF-re-Youngter-drive
多学习与此
re学习笔记(10)BUUCTF-re-Youngter-drive_buuctf youngter-drive-CSDN博客
查壳
分析有upx壳,同样脱壳处理,
进入主函数之后分析
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;
}
一个一个看
sub_4110FF
int sub_411BD0()
{
printf(
"1111111111111111111111111111111111111111111111111111111111111111111111111111111\n"
"*******************************************************************************\n"
"************** ****************************************************\n"
"************** ******** ********************* *************\n"
"************** ********* ********************* ***************************\n"
"************** ********* ********************* ***************************\n"
"************** ********* ********************* ***************************\n"
"************** ******* ********************** ***************************\n"
"************** **** ************************* ***************************\n"
"************** * *************************** **************\n"
"************** *** ************************* ***************************\n"
"************** ****** *********************** ***************************\n"
"************** ******** ********************* ***************************\n"
"************** ********** ******************* ***************************\n"
"************** *********** ***************** *************\n"
"*******************************************************************************\n"
"1111111111111111111111111111111111111111111111111111111111111111111111111111111\n");
printf("input flag:\n");
return scanf("%36s", &Source);
}
可知是输入Source为flag
hObject = CreateMutexW(0, 0, 0);
`CreateMutexW` 是 Windows API 中用于创建一个命名或未命名的互斥对象的函数。互斥对象可以用来保护对共享资源的访问,确保一次只有一个线程可以访问该资源。
`CreateMutexW` 函数创建的互斥对象可以跨越多个进程,只要它们知道互斥对象的名称。这使得它成为实现进程间同步的有用工具。
所以互斥对象的名字是hObject,之后可能会用上.
j_strcpy(Destination, &Source);
跟进查看可知是把Source复制给Des
hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
Thread = CreateThread(0, 0, sub_41119F, 0, 0, 0);
`CreateThread` 是 Windows 操作系统 API 中的一个函数,它用于在 Windows 系统中创建一个新线程。线程是程序执行的最小单元,一个进程可以包含多个并发执行的线程。
创建线程后,可以使用返回的句柄来控制线程(例如等待线程结束、终止线程等)。线程句柄必须通过 `CloseHandle` 函数关闭,以避免资源泄露。
其中包含了两个函数StartAddress和sub_41119F,相当于新学习的指向线程函数的指针;
我们逐个跟进StartAddress
void __stdcall __noreturn StartAddress_0(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_418008 > -1 )
{
sub_41112C(Source, dword_418008);
--dword_418008;
Sleep(0x64u);
}
ReleaseMutex(hObject);
}
}
sub_41119F
void __stdcall __noreturn sub_411B10(int a1)
{
while ( 1 )
{
WaitForSingleObject(hObject, 0xFFFFFFFF);
if ( dword_418008 > -1 )
{
Sleep(0x64u);
--dword_418008;
}
ReleaseMutex(hObject);
}
}
两个函数的结构相同,只是查了一个sub_41112C(Source, dword_418008);操作,我们先学习结构
WaitForSingleObject
函数用于等待一个单一的内核对象(如互斥锁、信号量、事件等)变为有信号状态。如果指定的对象在调用时已经处于有信号状态,函数会立即返回;否则,函数会阻塞当前线程,直到对象变为有信号状态或达到超时。
Sleep
函数是 Windows API 提供的一个简单的延迟函数,它使调用它的线程暂停执行指定的时间。在这段时间内,线程不会消耗任何处理时间,也不会响应任何消息或中断。
ReleaseMutex
是 Windows API 中的一个函数,它用于释放由调用线程拥有的指定互斥对象。互斥对象用于同步对共享资源的访问,确保一次只有一个线程可以访问该资源。当一个线程完成了对共享资源的访问后,它会调用 ReleaseMutex
来释放互斥对象,允许其他线程获取互斥对象并访问共享资源。
其中的hObject和一开始创建的互斥对象的函数名称相同,所以可知是两个线程是同时对一个数组进行操作的
CloseHandle(hObject);
CloseHandle(Thread);
`CloseHandle` 是 Windows API 中的一个函数,用于关闭对象句柄,释放与该句柄相关联的系统资源。在 Windows 编程中,许多内核对象(如进程、线程、互斥锁、信号量、文件等)都是通过句柄来管理和访问的。当你完成对这些对象的操作后,应该调用 `CloseHandle` 来关闭句柄,这是一个很好的编程实践,可以避免资源泄露。
在这个示例中,`CreateThread` 用于创建一个新线程,并返回线程的句柄。在完成线程相关的操作后,使用 `CloseHandle` 来关闭线程句柄,释放系统资源。这对于管理操作系统资源和避免内存泄漏非常重要。
sub_411190
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);
}
如果Source和off_418004相同,则输出Des,因为Des是一开始的flag,所以Des是明文,而其中的Sourse已经进入函数进行加密是其中的一个线程
// positive sp value has been detected, the output may be wrong!
char *__cdecl sub_411940(int a1, int a2)
{
char *result; // eax
char v3; // [esp+D3h] [ebp-5h]
v3 = *(a2 + a1);
if ( (v3 < 97 || v3 > 122) && (v3 < 65 || v3 > 90) )
exit(0);
if ( v3 < 97 || v3 > 122 )
{
result = off_418000[0];
*(a2 + a1) = off_418000[0][*(a2 + a1) - 38];
}
else
{
result = off_418000[0];
*(a2 + a1) = off_418000[0][*(a2 + a1) - 96];
}
return result;
}
所以我们获取相应的字符串,写代码即可;
#include<stdio.h>
#include<stdlib.h>
int main()
{
char a[]="TOiZiZtOrYaToUwPnToBsOaOapsyS";//29
char b[]="QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";//52
char flag[29];
int i,j;
for(i=28;i>-1;i--)
{
if(i%2==0)
{
flag[i]=a[i];
continue;
}
for(j=0;j<52;j++)
{
if(a[i]==b[j])
{
flag[i]=j+38;
if(flag[i] < 65 || flag[i] > 90)
{
flag[i]=j+96;
}
}
}
}
puts(flag);
return 0;
}
需要注意的是
*(a2 + a1) = off_418000[0][*(a2 + a1) - 38];
先转化成正确数组形式a[i]=b[a[i]-38]
我们程序的输入流程是先读入a[i]判断是不是字母,如果不是就不操作,
如果是大写字母 就进行 a[i] = b[a[i]-38] 操作
如果是小写字母 就进行 a[i] = b[a[i]-96] 操作
所以我们先爆破寻找a[i]==b[j],之后讨论大小写字母
1.当j+38即为a[i]是大写字母时,可知套a[i]=b[a[i]-38]公式没有错误,不用调整
2.当j+38即为a[i]不是大写字母时,可知套a[i]=b[a[i]-38]公式错误,flag[i]是j+96;
flag{ThisisthreadofwindowshahaIsES}
但是因为在主函数最后的对比的时候只对比了29次,er循环是从29到0是三十次,少对比了一次,所以算是一个bug,在buuctf中最后记上E就行了,不需要在意
第四题收获
1.CreateMutexW互斥的使用,如果是n个互相使用就i%n,要问我是==1对还是==0对,我只能说都试试,我也不会.
2.c语言的爆破,要知道条件的充分还是必要,比如这道题知道结果我们要先找到一开始的b下标再进行一次判断,而不是在一开始就进行分类讨论写代码;要提高逻辑能力
有一点神志不清;咢..之后多加回顾吧!!!
标签:11,总结,char,reverse,int,a1,v3,v5,sub From: https://blog.csdn.net/2403_87862296/article/details/144145950