BUUCTFYoungter-drive
考点:多线程
查壳脱壳
进入ida,直接进入main函数
sub_4110FF是输入flag的部分,flag有36个字符
main函数8-13行:有两个线程hObject、Thread
这段代码是使用C语言风格的Windows API函数来创建互斥体(Mutex)、线程以及关闭句柄的操作。下面是对每行代码的详细解释:
- ::hObject = CreateMutexW(0, 0, 0);
这行代码在全局命名空间中创建一个互斥体(Mutex)。CreateMutexW是Windows API中的一个函数,用于创建一个内核对象,用于同步多个线程对共享资源的访问,确保同一时间只有一个线程可以访问资源。参数说明:
第一个参数为LPSECURITY_ATTRIBUTES lpMutexAttributes,指定了互斥体的安全属性,默认设为0,表示使用默认安全属性。
第二个参数为BOOL bInitialOwner,指定创建线程是否立即拥有该互斥体。设为0意味着调用者不立即拥有互斥体。
第三个参数为LPCWSTR lpName,为互斥体指定一个名称,这里也是0,表示创建一个未命名的本地互斥体。::hObject是一个全局或静态变量,用于存储新创建的互斥体的句柄。
- j_strcpy(Destination, &Source);
这行代码似乎是用来复制字符串的,但j_strcpy并不是标准库中的函数,可能是自定义的或特定库中的函数,其作用类似于strcpy,将Source指向的字符串复制到Destination指向的缓冲区中。
- hObject = CreateThread(0, 0, StartAddress, 0, 0, 0);
创建一个新的线程。CreateThread是Windows API中的函数,用于创建一个新的线程并执行指定的函数。参数说明:
第一个参数为LPSECURITY_ATTRIBUTES lpThreadAttributes,同上,指定线程的安全属性,这里为0,使用默认设置。
第二个参数为SIZE_T dwStackSize,指定线程栈的大小,设为0表示使用系统默认大小。
第三个参数为LPTHREAD_START_ROUTINE lpStartAddress,是指向线程开始执行的函数地址,这里是StartAddress。
第四个参数为LPVOID lpParameter,传递给线程函数的参数,这里为0,没有传递参数。
第五个参数为DWORD dwCreationFlags,控制线程创建的标志,设为0表示无特殊标志。
第六个参数为LPDWORD lpThreadId,输出参数,返回新线程的ID,这里没有使用,所以传入了0。hObject被重新赋值为新创建线程的句柄。
- Thread = CreateThread(0, 0, sub_DA119F, 0, 0, 0);
同样是创建一个线程,但这次执行的函数是sub_DA119F。Thread变量用于存储这个新线程的句柄。
- CloseHandle(hObject);
关闭之前创建的互斥体或线程的句柄。CloseHandle是Windows API中的函数,用于释放与句柄关联的系统资源。这里关闭的是第一个线程的句柄(尽管变量名是hObject,但根据上下文应该是线程句柄)。
- CloseHandle(Thread);
关闭第二个线程的句柄,释放与之关联的系统资源。总结来说,这段代码首先创建了一个互斥体,然后创建了两个线程,每个线程都去执行不同的函数(StartAddress和sub_DA119F),最后关闭了这两个线程的句柄以及(可能误标为)互斥体的句柄。注意,互斥体创建后并未显示地在代码中使用,也没有进行线程间的同步操作,这可能是个疏忽或者示例代码片段的一部分。此外,字符串复制操作与线程创建看起来是独立的操作,没有直接关联。
sub_411190函数:
off_418004可查看是”TOiZiZtOrYaToUwPnToBsOaOapsyS“,Destination就是原始flag,flag经加密后得”TOiZiZtOrYaToUwPnToBsOaOapsyS“
回到main函数10-11行,分析加密过程
线程1,进入StartAddress:
sub_41112C:
总结线程1:
处理了flag中的29位,从后往前逐个进行sub_41112C处理。对照键盘标QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm,flag字符为大写时候新字符 = 键盘表[ 原大写字母 - 38 ],小写时新字符 = 键盘表[ 原大写字母 - 96 ]
线程2:进入sub_41119F
线程2就是纯干扰线程,没有加密,只是递减dword_418008
线程1中也有Sleep(100u)休眠100ms和--dword_418008,所以两个线程是交替进行的
猜测下标为奇数或者偶数时加密,验证:
#include <Stdio.h> #include <String.h> //参数一:目标字符 ; 参数二:目标字符串 int find(char yuan,char Aim_array[]){ for(int i = 0;i < strlen(Aim_array);i++){ if(Aim_array[i] == yuan) return i; } } int main(){ char source[] = "TOiZiZtOrYaToUwPnToBsOaOapsyS"; //flag 加密后的结果 char off_418000[] = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; //替换表 //在 off_418000 中找到与 source[i] 相同的字符,取出下标 与 96 或 38 进行加减操作 char flag[29] = ""; for(int i = 0;i < 29;i++){ //先执行哪个线程我们不知道,所以就都试一遍(i % 2 == 1)或(i % 2 == 0) if(i % 2 == 1){ if(source[i] >= 'A' && source[i] <= 'Z'){ //大写执行 //因为原先的小写字符执行完变成大写了 ,所以不能像IDA中的代码 flag[i] = find(source[i],off_418000) + 96; }else{ //小写执行 flag[i] = find(source[i],off_418000) + 38; } }else{ flag[i] = source[i]; } } for(int i = 0;i < 29;i++){ printf("%c",flag[i]); } return 0; }i % 2 == 1是对的,所以下标为奇数(从0开始)时进行线程1,加密flag
运行得ThisisthreadofwindowshahaIsES
flag{ThisisthreadofwindowshahaIsES}只有35个字符,因为线程1只操作了30位。但是flag有36个字符
差一个未知的,每个字母都猜一遍
flag{ThisisthreadofwindowshahaIsESE}参考 文章:
BUUCTF逆向题Youngter-drive-CSDN博客
Youngter-drive-CSDN博客