REVERSE-PRACTICE-BUUCTF-13
firmware
.bin(二进制)文件,由题目提示知是路由器固件逆向
参考:逆向路由器固件之解包 Part1
linux安装好binwalk和firmware-mod-kit
binwalk会分析二进制文件中可能的固件头或者文件系统,然后输出识别出的每个部分以及对应的偏移量
binwalk该二进制文件,发现包含了squashfs文件系统(squashfs是linux下的一种只读压缩文件系统类型)
binwalk -e 该二进制文件,提取出各部分数据到各个文件,相当于解压固件,解压出来的文件放在当前目录的/_firmware.bin.extracted文件夹下(注意此时的suqashfs-root文件夹是否为空)
使用firmware-mod-kit解包提取出来的squashfs文件
/*******************************************************
firmware-mod-kit的一些用法:
extract-firmware.sh 解包固件
build-firmware.sh 重新封包
check_for_upgrade.sh 检查更新
unsquashfs_all.sh 解包提取出来的squashfs文件
*******************************************************/
解包squashfs文件出来的文件放在了当前目录的squashfs-root-1文件夹中
(实际上,在本人虚拟机binwalk -e 该二进制文件后,生成文件夹/_firmware.bin.extracted中的squashfs-root文件夹,已经是解包squashfs文件出来的文件夹了,即squashfs-root文件夹和squashfs-root-1文件夹中包含的文件是相同的,如果squashfs-root文件夹下内容为空,是由于sasquatch安装有问题导致的,可以通过重新安装sasquatch解决,参考:dir815_FW_102.bin路由器固件解压碰到的坑)
在/_firmware.bin.extracted/squashfs-root/tmp目录或/_firmware.bin.extracted/squashfs-root-1/tmp目录下有一个backdoor文件
backdoor查壳发现有upx壳,脱壳后拖入ida分析
在字符串窗口找到网址
交叉引用网址,在initConnection函数中找到端口
[ACTF新生赛2020]Oruga
elf文件,无壳,ida分析
main函数逻辑清晰,获取输入,检验输入是否为“actf{”开头,验证输入内容,以及最后一个字符是否为“}”
进入check函数,分析可知是一个16x16的迷宫,和常规迷宫的按一次方向键则往该方向前进一格的机制不同,该迷宫的前进机制为,按一次方向键,则往该方向一直前进,直到撞到墙,具体分析知道,在map为零的位置时,可以持续前进,到达第一个非零的位置就停下来,然后减一个步长回到前一个零的位置,再读input的内容更新步长,更新步长实际上就是换方向
_BOOL8 __fastcall check(__int64 input)
{
int index; // [rsp+Ch] [rbp-Ch]
signed int input_index; // [rsp+10h] [rbp-8h]
signed int step; // [rsp+14h] [rbp-4h]
index = 0;
input_index = 5;
step = 0;
while ( map[index] != '!' ) // 终点为"!"
{
index -= step; // 由第48行代码可知,由于index叠加走到了非零的位置,这时需要减一个步长,回到前一个零位置
if ( *(_BYTE *)(input_index + input) != 'W' || step == -16 )// 由input的内容决定步长step
{
if ( *(_BYTE *)(input_index + input) != 'E' || step == 1 )
{
if ( *(_BYTE *)(input_index + input) != 'M' || step == 16 )
{
if ( *(_BYTE *)(input_index + input) != 'J' || step == -1 )
return 0LL;
step = -1; // J-左
}
else
{
step = 16; // M-下
}
}
else
{
step = 1; // E-右
}
}
else
{
step = -16; // W-上
}
++input_index; // 读下一个input的字符
while ( !map[index] ) // 该while循环体只能在map[index]=='0'时执行
{
if ( step == -1 && !(index & 0xF) ) // 判断边界的四个if
return 0LL;
if ( step == 1 && index % 16 == 15 )
return 0LL;
if ( step == 16 && (unsigned int)(index - 240) <= 0xF )
return 0LL;
if ( step == -16 && (unsigned int)(index + 15) <= 0x1E )
return 0LL;
index += step; // index按照当前的步长循环叠加,即按下一个方向键,就会沿着这个方向一直走,直到第一个非零的位置停下
}
}
return *(_BYTE *)(input_index + input) == '}';
}
把map提取出来,制成16x16的迷宫,按照前进机制走完迷宫即可,起始点为左上角的[0,0],终止点为“!”(0x21),W-上,M-下,J-左,E-右,路线即为flag
[Zer0pts2020]easy strcmp
elf文件,无壳,ida分析
main函数,有一个比较字符串的if语句决定输出的内容,其他什么也没有
来到start函数,发现在调用main函数前,先调用了fini函数和init函数
fini函数直接返回了,分析init函数,可以知道,在调用main函数前,程序将.init_array段地址到.fini_array段地址之间的函数全部执行一遍,命令行参数作为段之间函数的参数
这里可以看到,.init_array段和.fini_array段之间有3个函数,依次分析知道,重要的是sub_795函数
sub_795->sub_6EA,分析sub_6EA函数,计算输入的长度,将输入的内容顺序地与qword_201060数组的元素相减,然后去到main函数和那段字符串比较
写脚本即可得到flag
[GXYCTF2019]simple CPP
exe程序,运行后提示输入flag,输入错误退出程序,无壳,ida分析
交叉引用字符串来到sub_140001290函数
__int64 sub_140001290()
{
bool v0; // si
__int64 v1; // rax
__int64 v2; // r8
unsigned __int8 *v3; // rax
unsigned __int8 *v4; // rbx
int v5; // er10
__int64 v6; // r11
_BYTE *input_copy; // r9
void **v8; // r8
__int64 arr[3]; // rdi
__int64 arr[2]; // r15
__int64 arr[1]; // r12
__int64 arr[0]; // rbp
signed int v13; // ecx
unsigned __int8 *v14; // rdx
__int64 v15; // rdi
__int64 *v16; // r14
__int64 v17; // rbp
__int64 v18; // r13
_QWORD *v19; // rdi
__int64 v20; // r12
__int64 v21; // r15
__int64 v22; // rbp
__int64 v23; // rdx
__int64 v24; // rbp
__int64 v25; // rbp
__int64 v26; // r10
__int64 v27; // rdi
__int64 v28; // r8
bool v29; // dl
__int64 v30; // rax
void *v31; // rdx
const char *v32; // rax
__int64 v33; // rax
_BYTE *v34; // rcx
__int64 v36; // [rsp+20h] [rbp-68h]
void *input; // [rsp+30h] [rbp-58h]
unsigned __int64 input_len; // [rsp+40h] [rbp-48h]
unsigned __int64 v39; // [rsp+48h] [rbp-40h]
v0 = 0;
input_len = 0i64;
v39 = 15i64;
LOBYTE(input) = 0;
LODWORD(v1) = printf(std::cout, "I'm a first timer of Logic algebra , how about you?");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v1, sub_140001B90);
printf(std::cout, "Let's start our game,Please input your flag:");
sub_140001DE0(std::cin, &input, v2); // 读取input
std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_140001B90);
if ( input_len - 5 > 25 ) // input不需要GXY{}包住
{
LODWORD(v33) = printf(std::cout, "Wrong input ,no GXY{} in input words");
std::basic_ostream<char,std::char_traits<char>>::operator<<(v33, sub_140001B90);
goto LABEL_45;
}
v3 = sub_1400024C8(32ui64); // 开辟一块大小为32的,元素类型为unsigned int8的地址空间给v3
v4 = v3;
if ( v3 ) // 新开辟的地址空间全部赋0值
{
*v3 = 0i64;
*(v3 + 1) = 0i64;
*(v3 + 2) = 0i64;
*(v3 + 3) = 0i64;
}
else
{
v4 = 0i64;
}
v5 = 0;
if ( input_len > 0 )
{
v6 = 0i64;
do
{
input_copy = &input;
if ( v39 >= 16 )
input_copy = input;
v8 = &Dst;
if ( qword_140006060 >= 0x10 )
v8 = Dst; // Dst="i_will_check_is_debug_or_not",长度为28
v4[v6] = input_copy[v6] ^ *(v8 + v5++ % 27);// input和Dst异或,结果放入v4
++v6;
}
while ( v5 < input_len );
}
arr[3] = 0i64;
arr[2] = 0i64;
arr[1] = 0i64;
arr[0] = 0i64;
if ( input_len > 30 ) // input长度验证
goto LABEL_28;
v13 = 0;
if ( input_len <= 0 )
goto LABEL_28;
v14 = v4; // v14=v4,是input和Dst异或的结果
do // do循环体,实际上是把v14分成四段,前三段长度为8,分别放入v12,v11,v10中,剩下的放在v9
// 依次记为arr[0],arr[1],arr[2],arr[3],重要的是算出arr[0~3]
{
v15 = *v14 + arr[3];
++v13;
++v14;
switch ( v13 )
{
case 8:
arr[0] = v15;
goto LABEL_24;
case 16:
arr[1] = v15;
goto LABEL_24;
case 24:
arr[2] = v15;
LABEL_24:
v15 = 0i64;
break;
case 32:
printf(std::cout, "ERRO,out of range");
exit(1);
break;
}
arr[3] = v15 << 8;
}
while ( v13 < input_len );
if ( arr[0] )
{
v16 = sub_1400024C8(32ui64);
*v16 = arr[0]; // arr[0~3]放入v16[0~3]中
v16[1] = arr[1];
v16[2] = arr[2];
v16[3] = arr[3];
goto LABEL_29;
}
LABEL_28:
v16 = 0i64;
LABEL_29:
v36 = v16[2]; // v36=arr[2]
v17 = v16[1]; // v17=arr[1]
v18 = *v16; // v18=arr[0]
v19 = sub_14000223C(32ui64); // 开辟一块大小为32的,元素类型为unsigned int8的地址空间给v19
if ( IsDebuggerPresent() ) // 反调试
{
printf(std::cout, "Hi , DO not debug me !");
Sleep(0x7D0u);
exit(0);
}
v20 = v17 & v18; // v20=arr[1]&arr[0]
*v19 = v17 & v18; // v19[0]=arr[1]&arr[0]
v21 = v36 & ~v18; // v21=arr[2]&(~arr[0])
v19[1] = v21; // v19[1]=arr[2]&(~arr[0])
v22 = ~v17; // v22=~arr[1]
v23 = v36 & v22; // v23=arr[2]&(~arr[1])
v19[2] = v36 & v22; // v19[2]=arr[2]&(~arr[1])
v24 = v18 & v22; // v24=arr[0]&(~arr[1])
v19[3] = v24; // v19[3]=arr[0]&(~arr[1])
if ( v21 != 0x11204161012i64 ) // 验证v19[1]==0x11204161012,即arr[2]&(~arr[0])==0x11204161012
{
v19[1] = 0i64;
v21 = 0i64;
}
v25 = v21 | v20 | v23 | v24; // 需v25==0x3E3A4717373E7F1F成立,即(arr[2]&(~arr[0])) | (arr[1]&arr[0]) | (arr[2]&(~arr[1])) | (arr[0]&(~arr[1]))==0x3E3A4717373E7F1F
v26 = v16[1]; // v26=arr[1]
v27 = v16[2]; // v27=arr[2]
v28 = v23 & *v16 | v27 & (v20 | v26 & ~*v16 | ~(v26 | *v16));// (arr[2]&(~arr[1])) & (arr[0]) | (arr[2]) & ((arr[1]&arr[0]) | (arr[1]) & ~(arr[0]) | ~((arr[1]) | (arr[0])))==0x8020717153E3013
v29 = 0;
if ( v28 == 0x8020717153E3013i64 ) // 由第171行代码可知,v0需要为1,v28==0x8020717153E3013成立
v29 = v25 == 0x3E3A4717373E7F1Fi64; // 验证v25==0x3E3A4717373E7F1F,相等返回1,不等返回0
if ( (v25 ^ v16[3]) == 0x3E3A4717050F791Fi64 )// v16[3]=arr[3],即arr[3]^0x3E3A4717373E7F1F==0x3E3A4717050F791F
v0 = v29; // v0为v25==0x3E3A4717373E7F1F的返回值,等式成立返回1,不等返回0
if ( (v21 | v20 | v26 & v27) != (~*v16 & v27 | 0xC00020130082C0Ci64) || v0 != 1 )// 需要v0为1,意味着v25==0x3E3A4717373E7F1F成立且((arr[2]&(~arr[0])) |(arr[1]&arr[0]) | (arr[1]) & (arr[2])) == (~(arr[0])& (arr[2]) | 0xC00020130082C0C)
{
printf(std::cout, "Wrong answer!try again");
j_j_free(v4);
}
else
{
LODWORD(v30) = printf(std::cout, "Congratulations!flag is GXY{");// input不需要GXY{}包住
v31 = &input;
if ( v39 >= 16 )
v31 = input;
v32 = sub_140001FD0(v30, v31, input_len);
printf(v32, "}");
j_j_free(v4);
}
LABEL_45:
if ( v39 >= 0x10 )
{
v34 = input;
if ( v39 + 1 >= 0x1000 )
{
v34 = *(input - 1);
if ( (input - v34 - 8) > 0x1F )
invalid_parameter_noinfo_noreturn();
}
j_j_free(v34);
}
return 0i64;
}
写解arr[0~3]的脚本
写逆异或运算得到flag的脚本,由flag的明文字符串可知,arr[1]的结果错误,原因是原方程组有多组解,参考别的师傅的wp,比赛给出了第二部分的flag,e!P0or_a,替换掉错误的8个字符,结果为We1l_D0ne!P0or_algebra_am_i