暑假第五周
re5-packed-movement
先脱壳
ida打开全是mov 指令,movfuscator混淆
法一
shift+F12 搜索字符串找到'Wrong Flag!',
交叉引用可以看到有70多处引用,可能就是逐字符比较
随便点一个看看,从每一个'Wrong Flag!'处往上翻可以看到都有一个
mov R2 xxh的指令
直接搜索R2寄存器内容
Alt+B搜索C7 05 68 20 06 08即mov R2对应的16进制编码
得到flagALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
法二
使用idac脚本。
shift+F2打开脚本界面
运行得到flag
flag
ALEXCTF{M0Vfusc4t0r_w0rk5_l1ke_m4g1c}
reverse-for-the-holy-grail-350
直接看主函数,有点多重点锁定这几个语句
v4 = stringmod(v9),v4小于0就输出auuuuuuugh,大于0才输出tuctf{}
应该就是v4>0时输出flag,跟进一下stringmod
stringmod有三个部分先看第一个
v4要大于0所以if 要不执行,那么v3就要是3的倍数,并且
v12读取的字符必须等于firstchar[v3 / 3]
第二部分是一个异或
第三部分
查看一下thirdchar和masterArray,有点乱不好提取
用idapython导出一下
from idc_bc695 import *
addr=[0x601840,0x601860,0x601880]
for a in addr:
list=[]
for i in range(6):
list.append(Dword(a+4*i))
print(list)
前三行时对应16进制结果,后三行则是10进制
exp
firstchar = [0x41, 0x69, 0x6e, 0x45, 0x6f, 0x61]
thirdchar = [0x2ef, 0x2c4, 0x2dc, 0x2c7, 0x2de, 0x2fc]
masterarray = [0x1d7, 0xc, 0x244, 0x25e, 0x93, 0x6c]
list = [666,]
v7 = 666
for i in range(17):
v7 = v7 + v7 % 5
list.append(v7)
print(list)
#求flag第0,3,6,9,12,15位
flag = [0 for i in range(18)]
index1 = 0
for i in range(0,16,3):
flag[i] = firstchar[index1]
index1 += 1
# print(flag)
#求第2,5,8,11,14,17位
index2=0
for i in range(2,18,3):
flag[i] = list[i] ^ thirdchar[index2]
index2 += 1
# print(flag)
# 暴力求第1,4,7,10,13,16位
index3 = 0
for i in range(1,17,3):
for j in range(32,127):
if ((list[i-1] ^ firstchar[index3]) * (list[i] ^ j)) % thirdchar[index3] == masterarray[index3]:
flag[i] = j
index3 +=1
break
print('tuctf{'+"".join(map(chr,flag))+'}')
#[666, 667, 669, 673, 676, 677, 679, 683, 686, 687, 689, 693, 696, 697, 699, 703, 706, 707]
#tuctf{AfricanOrEuropean?}
flag
tuctf{AfricanOrEuropean?}
[XCTF]reverse_box
题目描述
挑战描述
$ ./reverse_box ${FLAG}
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
flag格式:TWCTF{}
分析
主函数很短,简单分析如下
运行一下可以发现,输入相同的flag输出不同,说明v4是动态变化的
跟进sub_804858D函数,可以看到是根据时间生成随机数种子v1
再生成随机数,构建出v4
法一
通过查看汇编代码,知道随机生成的范围不超过0xff,那这个构建v4的函数应该可以模拟出来。现在需要知道的就是__ROR1__的作用
这是一个IDA内置定义的函数,通过查看ida/plugins/defs.h查看__ROR1__定义
可以看到这是一个调用__ROL__的函数,查找__ROL__
有两个参数:(value, int count)
第一个参数为左移的数,第二个参数为左移的位数。
如果第二个参数值为负数,则实际上为循环右移 -count位。
该函数的实现细节为:
先得到value的位数,然后count对位数取模。
如果count值大于0,则先右移-count取模的结果,然后在左移取模的结果,得到的两个数相或,即为循环左移的结果。
如果count值小于0,先左移在右移即可。
举例来说: value = 0110, count = 6
value为4位数, 6 % 4 = 2,
0110先右移4-2=2位,得到0001,然后在左移2位,得到1000,0001 | 1000结果为1001,即循环左移结果为1001。
其实简单理解就是把所有位都向左移。最高位复制到进位标志位和最低位。
知道了__ROR1__的作用,接下来根据提示输入flag会输出
95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a
而flag格式为TWCTF{}对应的ASCII码是84,87,67,84,70,123,125
可以推出v4[84]=0x95,v4[87]=0xee,v4[67]=0xaf,v4[84]=0x95,v4[70]=0xef,v4[123]=0x94,v4[125]=0x4a,这就是我们模拟爆破出v4的依据
最后根据表反推出flag
exp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int __ROR1__(unsigned __int8 value,int x){
value= (value>>x) | (value<<(8-x));
return value;
}
int main(){
unsigned int v1; // eax
char v2; // al
char v3; // al
int v4; // ecx
int v5; // eax
int v6; // ecx
int v7; // eax
int v8; // ecx
int v9; // eax
int v10; // ecx
int result; // eax
char v12; // [esp+1Ah] [ebp-Eh]
unsigned __int8 v13; // [esp+1Bh] [ebp-Dh]
char v14; // [esp+1Bh] [ebp-Dh]
char v15; // [esp+1Bh] [ebp-Dh]
int v16; // [esp+1Ch] [ebp-Ch]
unsigned __int8 a1[256]={0};
int i;
for(i=1; i<256; i++){
memset(a1,0,sizeof(a1));
a1[0] = i;
v12 = 1;
v13 = 1;
do
{
if ( v12 >= 0 )
v2 = 0;
else
v2 = 27;
v12 ^= (2 * v12) ^ v2;
v14 = (4 * ((2 * v13) ^ v13)) ^ (2 * v13) ^ v13;
v15 = (16 * v14) ^ v14;
if ( v15 >= 0 )
v3 = 0;
else
v3 = 9;
v13 = v15 ^ v3;
v4 = v13;
v4 = __ROR1__(v13, 7);
v5 = v4 ^ (v13 ^ *a1);
v6 = v13;
v6 = __ROR1__(v13, 6);
v7 = v6 ^ v5;
v8 = v13;
v8 = __ROR1__(v13, 5);
v9 = v8 ^ v7;
v10 = v13;
v10 = __ROR1__(v13, 4);
result = v10 ^ v9;
a1[v12] = result;
}while ( v12 != 1 );
if(a1[84]==0x95 && a1[87]==0xee && a1[67]==0xaf && a1[84]==0x95 && a1[70]==0xef && a1[123]==0x94 && a1[125]==0x4a){
printf("随机种子为%d\n",i);
break;
}
}
for(i=0; i<256; i++){
if(i%16 == 0) printf("\n");
printf("0x%02x ",a1[i]);
}
}
/*
随机种子为214
0xd6 0xc9 0xc2 0xce 0x47 0xde 0xda 0x70 0x85 0xb4 0xd2 0x9e 0x4b 0x62 0x1e 0xc3
0x7f 0x37 0x7c 0xc8 0x4f 0xec 0xf2 0x45 0x18 0x61 0x17 0x1a 0x29 0x11 0xc7 0x75
0x02 0x48 0x26 0x93 0x83 0x8a 0x42 0x79 0x81 0x10 0x50 0x44 0xc4 0x6d 0x84 0xa0
0xb1 0x72 0x96 0x76 0xad 0x23 0xb0 0x2f 0xb2 0xa7 0x35 0x57 0x5e 0x92 0x07 0xc0
0xbc 0x36 0x99 0xaf 0xae 0xdb 0xef 0x15 0xe7 0x8e 0x63 0x06 0x9c 0x56 0x9a 0x31
0xe6 0x64 0xb5 0x58 0x95 0x49 0x04 0xee 0xdf 0x7e 0x0b 0x8c 0xff 0xf9 0xed 0x7a
0x65 0x5a 0x1f 0x4e 0xf6 0xf8 0x86 0x30 0xf0 0x4c 0xb7 0xca 0xe5 0x89 0x2a 0x1d
0xe4 0x16 0xf5 0x3a 0x27 0x28 0x8d 0x40 0x09 0x03 0x6f 0x94 0xa5 0x4a 0x46 0x67
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
*/
list = [
0xd6,0xc9,0xc2,0xce,0x47,0xde,0xda,0x70,
0x85,0xb4,0xd2,0x9e,0x4b,0x62,0x1e,0xc3,
0x7f,0x37,0x7c,0xc8,0x4f,0xec,0xf2,0x45,
0x18,0x61,0x17,0x1a,0x29,0x11,0xc7,0x75,
0x02,0x48,0x26,0x93,0x83,0x8a,0x42,0x79,
0x81,0x10,0x50,0x44,0xc4,0x6d,0x84,0xa0,
0xb1,0x72,0x96,0x76,0xad,0x23,0xb0,0x2f,
0xb2,0xa7,0x35,0x57,0x5e,0x92,0x07,0xc0,
0xbc,0x36,0x99,0xaf,0xae,0xdb,0xef,0x15,
0xe7,0x8e,0x63,0x06,0x9c,0x56,0x9a,0x31,
0xe6,0x64,0xb5,0x58,0x95,0x49,0x04,0xee,
0xdf,0x7e,0x0b,0x8c,0xff,0xf9,0xed,0x7a,
0x65,0x5a,0x1f,0x4e,0xf6,0xf8,0x86,0x30,
0xf0,0x4c,0xb7,0xca,0xe5,0x89,0x2a,0x1d,
0xe4,0x16,0xf5,0x3a,0x27,0x28,0x8d,0x40,
0x09,0x03,0x6f,0x94,0xa5,0x4a,0x46,0x67]
flag = ""
enc = "95eeaf95ef94234999582f722f492f72b19a7aaf72e6e776b57aee722fe77ab5ad9aaeb156729676ae7a236d99b1df4a"
for i in range(0, len(enc), 2):
s1 = int(enc[i:i+2], 16)
flag += chr(list.index(s1))
print(flag)
#TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}
法二
参考reverse_box gdb调试
生成随机数后第一个断点
此时rand()随机数生成函数刚生成一个随机数与0xFF后,从eax寄存器取出存放在[ebp+var_C]
查看栈中var_C的地址相对于栈顶是-0000000C所以当时加载到栈中的地址就是ebp-0xc
第二个断点
正是printf函数开始输入的位置,这时候我们判断它输入的第一个16进制值是不是0x95,如果是那么我们就认为找到了正确的解;然后输出找到解的随机值和v4数组的所有值,查看起始地址从esp+0x1c开始的256个16进制字节的内容,在上图的汇编中数组地址是esp+eax+1Ch,但是当输出数组的第一个值时i为0即eax为0,所以此时数组起始地址esp+eax+1Ch=esp+1Ch,之所以输出256字节就够了,实际上也许用不上256字节,因为数组的下标是我们输入的字符的ASCII码,又我们输入的字符只能是可打印的字符,所以范围就是0-255中哪些可打印的字符就行了,这里我们输出完整的256字节是没有具体去区分可打印字符了,因为也不会影响到结果。
exp
采用gdb调试
#设置从0到255的随机值爆破
set $i=0
set $total=256
while($i<$total)
#在生成随机值之后的地址下第一个断点
b *0x80485b1
#在main函数中printf输出的位置下第二个断点
b *0x8048707
#运行程序,并输入参数"TWCTF"
run TWCTF
#$i变量的值递加1
set $i=$i+1
#程序在第一个断点停下来的时候,把当前的随机值赋值给eax寄存器
set $eax=$i
#继续运行程序
continue
#程序在第二个断点停下来的时候,判断此时的输出的第一个16进制值是否是0x95
if ($eax==0x95)
#如果是,则认为找到了正确的解,输出此时的随机值
print $i
#查看起始地址从esp+0x1c开始的256个16进制字节的内容,这里就是存放数组的地方
x/256xb $esp+0x1c
#令变量的值为256,退出循环
set $i=256
end
stop
end
end
flag
TWCTF{5UBS717U710N_C1PH3R_W17H_R4ND0M123D_5-B0X}
[SUCTF2019]hardCPP
ida打开注意到流程图比较不一样,是OLLVM混淆控制流平坦化
用脚本deflat.py去控制流平坦化,需要用到angr,版本为8.19.4.5
pip install angr==8.19.4.5
python3 deflat.py hardCpp 0x4007E0
得到去控制流平坦化之后的主函数如下
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v3; // al
char v4; // al
char v5; // al
char v6; // al
char v8; // al
char v9; // al
char v10; // al
char v11; // al
char v12[8]; // [rsp+A0h] [rbp-90h] BYREF
char v13[8]; // [rsp+A8h] [rbp-88h] BYREF
char v14[8]; // [rsp+B0h] [rbp-80h] BYREF
char v15[8]; // [rsp+B8h] [rbp-78h] BYREF
char v16[8]; // [rsp+C0h] [rbp-70h] BYREF
char v17[7]; // [rsp+C8h] [rbp-68h] BYREF
char v18; // [rsp+CFh] [rbp-61h]
int v19; // [rsp+D0h] [rbp-60h]
int v20; // [rsp+D4h] [rbp-5Ch]
int v21; // [rsp+D8h] [rbp-58h]
int v22; // [rsp+DCh] [rbp-54h]
char s; // [rsp+E0h] [rbp-50h] BYREF
char v24[23]; // [rsp+E1h] [rbp-4Fh] BYREF
char v25[8]; // [rsp+F8h] [rbp-38h] BYREF
char v26[8]; // [rsp+100h] [rbp-30h] BYREF
char v27[8]; // [rsp+108h] [rbp-28h] BYREF
char v28[4]; // [rsp+110h] [rbp-20h] BYREF
int v29; // [rsp+114h] [rbp-1Ch]
const char **v30; // [rsp+118h] [rbp-18h]
int v31; // [rsp+120h] [rbp-10h]
int v32; // [rsp+124h] [rbp-Ch]
int v33; // [rsp+128h] [rbp-8h]
bool v34; // [rsp+12Eh] [rbp-2h]
v32 = 0;
v31 = argc;
v30 = argv;
v29 = time(0LL);
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
s = getchar();
fgets(v24, 21, stdin);
v22 = time(0LL);
v21 = v22 - v29;
v33 = v22 - v29;
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_13;
while ( 1 )
{
v20 = strlen(&s);
v34 = v20 != 21;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_13:
v20 = strlen(&s);
}
while ( 1 )
{
v19 = 1;
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
v19 = 1;
}
while ( v19 < 21 )
{
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
{
v18 = v21 ^ *(&s + v19);
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v8 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v8);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v9 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v9);
v10 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v10);
v11 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v11);
}
do
{
v18 = v21 ^ *(&s + v19);
v17[0] = main::$_0::operator()(v27, (unsigned int)v18);
v16[0] = main::$_1::operator()(v25, (unsigned int)*(&s + v21 + v19 - 1));
v3 = main::$_1::operator() const(char)::{lambda(int)#1}::operator()(v16, 7LL);
v18 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v17, (unsigned int)v3);
v15[0] = main::$_2::operator()(v28, (unsigned int)v18);
v14[0] = main::$_2::operator()(v28, (unsigned int)*(&s + v21 + v19 - 1));
v4 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v14, 18LL);
v13[0] = main::$_3::operator()(v26, (unsigned int)v4);
v5 = main::$_3::operator() const(char)::{lambda(char)#1}::operator()(v13, 3LL);
v12[0] = main::$_0::operator()(v27, (unsigned int)v5);
v6 = main::$_0::operator() const(char)::{lambda(char)#1}::operator()(v12, 2LL);
v18 = main::$_2::operator() const(char)::{lambda(char)#1}::operator()(v15, (unsigned int)v6);
}
while ( enc[v19 - 1] != v18 );
while ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
;
++v19;
}
if ( y >= 10 && ((((_BYTE)x - 1) * (_BYTE)x) & 1) != 0 )
goto LABEL_16;
while ( 1 )
{
puts("You win");
if ( y < 10 || ((((_BYTE)x - 1) * (_BYTE)x) & 1) == 0 )
break;
LABEL_16:
puts("You win");
}
return 0;
}
先给了一个提示 md5 值
puts("func(?)=\"01abfc750a0c942167651c40d088531d\"?");
通过查找得到是#
之后有一对冗余代码,对加密不影响
重点看一下
点进去看看每个操作operator
main::$_0::operator()
直接返回第二个参数
char __fastcall main::$_0::operator()(__int64 a1, char a2)
{
return a2;
}
main::$_1::operator()
同样是直接返回第二个参数
char __fastcall main::$_1::operator()(__int64 a1, char a2)
{
return a2;
}
main::$_1::operator() const(char)::{lambda(int)#1}::operator()
返回的是a1%a2
__int64 __fastcall main::$_1::operator() const(char)::{lambda(int)#1}::operator()(char *a1, int a2)
{
return (unsigned int)(*a1 % a2);
}
main::$_0::operator() const(char)::{lambda(char)#1}::operator()
冗余代码挺多,关键就5句
v15 = *(&v5-16) + *v5 = a1 + a2
main::$_2::operator()
其实就是返回第二个参数
main::$_2::operator() const(char)::{lambda(char)#1}::operator()
返回a2 ^ a1
__int64 __fastcall main::$_2::operator() const(char)::{lambda(char)#1}::operator()(_BYTE *a1, char a2)
{
return (unsigned int)(char)(a2 ^ *a1);
}
main::$_3::operator()
返回第二个参数
main::$_3::operator() const(char)::{lambda(char)#1}::operator()
返回a2 * a1
__int64 __fastcall main::$_3::operator() const(char)::{lambda(char)#1}::operator()(char *a1, char a2)
{
return (unsigned int)(a2 * *a1);
}
最后整理一下
v18 = v21 ^ s[v19];
v17 = v18
v16 = s[v21 - 1 + v19]
v3 = v16[0] % 7
v18 = v17 + v3
v15 = v18
v14 = s[v21 - 1 + v19]
v4 = 18 ^ v14[0]
v13 = v4
v5 = v13[0] * 3
v12 = v5
v6 = v12[0] + 2
v18 = v15[0] ^ v6
//组合一下结果如下
v18 = ((v21 ^ s[v19]) + (s[v21 - 1 + v19] % 7)) ^ ((18 ^ s[v21 - 1 + v19]) * 3 + 2)
其中v21是time(0LL)即为0
s是输入的字符串
最后v18 = ((0 ^ input[i]) + (input[i-1] % 7)) ^ ((18 ^ input[i-1]) * 3 + 2)
最后判断v18是否等于enc
exp
enc = [0xF3, 0x2E, 0x18, 0x36, 0xE1, 0x4C, 0x22, 0xD1, 0xF9, 0x8C, 0x40, 0x76, 0xF4, 0x0E, 0x00, 0x05, 0xA3, 0x90, 0x0E, 0xA5]
flag = '#'
for i in range(20):
flag += chr(((enc[i] ^ ((ord(flag[i]) ^ 18) * 3 + 2)) - (ord(flag[i]) % 7)) & 0xff)
print(flag)
#flag{mY-CurR1ed_Fns}
flag
flag{mY-CurR1ed_Fns}
secret-galaxy-300
先运行看看
显示了五个星系的信息,用IDA打开
主函数是调用填充函数和打印函数
打印函数里面没什么内容,那我们就关注一下填充函数
跟踪一下galaxy_name,总共有6个星系名字
而刚刚运行结果只有前五个,最后一个DARK SECRET GALAXY会不坏就藏有flag信息呢
交叉引用看看,跟踪到一个__libc_csu_gala函数有点像flag的生成函数
应该是截取不同星系名字凭借而成一个byte_40DAXX这个串
手动拼接一下或者在return result处下断点
动态调试F9后跟踪一下byte串
按a将数据转换成字符串,得到flag
flag
aliens_are_around_us
[HCTF2018]seven
函数不多,直接定位到关键函数
__int64 __fastcall sub_1400012F0(__int64 a1, __int64 a2)
{
__int64 v3; // rsi
unsigned __int64 v4; // rdx
int v5; // ecx
__int16 *v6; // rdi
__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) / 0xCui64;
if ( (_DWORD)v4 )
{
v5 = dword_1400030E4;
v6 = (__int16 *)(v3 + 2);
v7 = (unsigned int)v4;
while ( *(_WORD *)(v3 + 4) )
{
LABEL_30:
v6 += 6;
if ( !--v7 )
goto LABEL_31;
}
aO[v5] = 46;
v8 = *v6;
if ( *v6 == 17 ) // 对应w
{
if ( (v5 & 0xFFFFFFF0) != 0 )
{
v5 -= 16; // 坐标移动,向上
goto LABEL_13;
}
v5 += 208;
dword_1400030E4 = v5;
}
if ( v8 != 31 ) // 对应s
goto LABEL_14;
if ( (v5 & 0xFFFFFFF0) == 208 )
v5 -= 208;
else
v5 += 16; // 坐标移动,向下
LABEL_13:
dword_1400030E4 = v5;
LABEL_14:
if ( v8 == 30 ) // 对应a
{
if ( (v5 & 0xF) != 0 )
--v5; // 向左
else
v5 += 15; // 边界移动,最右端0位置时,+15,向左移动到最右端
dword_1400030E4 = v5;
}
if ( v8 == 32 ) // 对应d
{
if ( (v5 & 0xF) == 15 )
v5 -= 15; // 向右
else
++v5; // 同理
dword_1400030E4 = v5;
}
v9 = aO[v5];
if ( v9 == 42 )
{
v10 = "-1s\n";
}
else
{
if ( v9 != '7' ) // 终点是一个字符'7'
{
LABEL_29:
aO[v5] = 'o'; // 起点是'o'
goto LABEL_30;
}
v10 = "The input is the flag!\n";
}
dword_1400030E4 = 16;
DbgPrint(v10);
v5 = dword_1400030E4;
goto LABEL_29;
}
}
LABEL_31:
if ( *(_BYTE *)(a2 + 65) )
*(_BYTE *)(*(_QWORD *)(a2 + 184) + 3i64) |= 1u;
return *(unsigned int *)(a2 + 48);
}
提取一下迷宫手动走一下得到flag
****************
o..............*
**************.*
************...*
***********..***
**********..****
*********..*****
********..******
*******..*******
******..********
*****..*********
****..**********
****7***********
****************
flag
hctf{ddddddddddddddssaasasasasasasasasas}
[buu]Youngter-drive
先脱UPX壳,然后看主函数,有多个线程
sub_4110FF里是读入source
先看第一个线程hObject,它执行了startAddress
继续查看 sub_41112C 函数
如果不是大写字母或者小写字母就正常退出
如果是小写字母,则对off_418000的小写字母进行-38的加密操作
反之如果是大写字母,则对off_418000的大写字母进行-96的加密操作.
off_418000如下
看一下第二个线程sub_41119F
和第一个线程一样都调用了dword_418008看了一下是0x1D
两个线程执行一次都会把dword_418008减一
两个线程交替运行,由于sub_41119F是直接让dword_418008减一
意味着只有一半的dword_418008会传入sub_41112C
最后回到主函数有个 sub_411190,点开是比较flag
off_418004如下
已知第一个传入的dword_418008是0x1D(29)是奇数
那么只有奇数位会进行加密,偶数为应该就是off_418004的内容
解出来29位提交上去是错的,
仔细观察可以发现最后的sub_411880只比较了0~28位这29个字符
而根据分析主函数判断dword_418008!=-1,说明是从29~0总共应该有30位
没有更多信息去知道最后一位是什么,按理来说应该就是多解了
但是好像只有填充E才会是对的。
exp
enc ='QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'
key = 'TOiZiZtOrYaToUwPnToBsOaOapsyS'
flag = ''
for i in range(len(key)):
if i%2 == 0:
flag += key[i]
else:
if key[i].isupper():
flag += chr(enc.find(key[i])+96)
else:
flag += chr(enc.find(key[i])+38)
print(flag)
#ThisisthreadofwindowshahaIsES
flag
ThisisthreadofwindowshahaIsESE
本文来自博客园,作者:{Tree_24},转载请注明原文链接:{https://www.cnblogs.com/Tree-24/}