目录
一.查壳
二.运行缺少dll
三.主函数
四.hObject线程
五.Thread线程
六.judge函数
七.解题脚本
这题的关键在于了解一定的线程相关知识
一.查壳
32位带壳,用upx脱壳
二.运行缺少dll
后续尝试了各种方法修复dll但是还是运行不了
值得一提的是脱壳后的程序不能动态调试,如果动态调试会导致原本识别出来的函数和线程名变成红色的JMPOUT(地址)的形式,难以阅读和理解
三.主函数
1.menu函数
打印一个menu,执行输入输出
2.两个线程
根据微软官方文档可以得知由CreateMutexW()创建的::hObject是一个互斥锁(与下方的hObject线程区分开)
hObject和Thread是两个线程,其中CreateThread()函数第三个参数是运行的线程函数
CloseHandle()可以关闭线程
3.judge函数用于判断输入字符是否满足条件
四.hObject线程
1.线程函数基本功能
WaitForSingleObject 函数实际上就是抢占主函数创建的::hObject互斥锁,抢占成功后执行判断
这里的count初始值是29,用于控制循环次数,所以可以猜测flag有29位
显然执行encode()函数后休眠一段时间,最后释放互斥锁,释放之后会使Thread线程开始运行
2.encode()函数的功能
①先判断输入字符串的count下标字符是否为字母,不是字母则结束程序,也就是说flag全是字母
②第二条if语句是判断该字符是否为大写字符,如果是大写字符那么在off_418000指针指向的字符表中根据chr-38后的偏移值查表确定字符
③如果是小写字符,根据chr-96的偏移值查表确定字符
④值得一提的是off_418000指针指向的字符表为 QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm
这里的0-25是大写字母,26-52是小写字母,所以经过查表操作后,原来的小写字母必定变成大写字母,大写字母必定变成小写字母
五.Thread线程
当hObject线程交出互斥锁后,轮到Thread线程运行线程函数,很显然这个函数的功能是--count
那么count初始值是29,由hObject运行后变成28,再由Thread线程运行后变成27
这样往复循环,所以hObject线程中,只对下标为奇数的字符进行了操作,偶数字符由于Thread线程对count的作用所以不被执行
六.judge函数
可以看到是对输入的字符串和off_418004指针指向的字符表进行比较
off_418004 : TOiZiZtOrYaToUwPnToBsOaOapsyS
七.解题脚本
根据比较用的字符表可以得知flag经加密后内容为:TOiZiZtOrYaToUwPnToBsOaOapsyS
同时只对奇数下标进行操作,偶数下标不操作,所以不妨直接令flag=arr
根据每个字符在字符表中的位置可以确定偏移值
如果加密后的字符是小写,那么原来是大写,要加38
加密后的字符是大写则原来是小写,加96
#include <iostream>
using namespace std;
unsigned char table[53] = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
unsigned int find(unsigned char ch)
{
for (int i = 0; i < 52; i++)
if (ch == table[i])
return i;
}
int main()
{
unsigned char arr[30] = "TOiZiZtOrYaToUwPnToBsOaOapsyS";
unsigned char flag[30] = "TOiZiZtOrYaToUwPnToBsOaOapsyS";
for (int i = 29; i >-1;i--)
{
if (arr[i] <= 'Z'&& arr[i]>='A')
flag[i] = find(arr[i]) + 96;
else
flag[i] = find(arr[i]) + 38;
i--;
}
flag[29] = 0;
cout << flag;
//ThisisthreadofwindowshahaIsES
return 0;
}
最终输出ThisisthreadofwindowshahaIsES
但是提交flag{ThisisthreadofwindowshahaIsES}是错误的,看了其他大佬的wp才知道最后一位要加一位E