XCTF-攻防世界CTF平台-Reverse逆向类——56tar-tar-binks
Posted 大灬白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XCTF-攻防世界CTF平台-Reverse逆向类——56tar-tar-binks相关的知识,希望对你有一定的参考价值。
题目提供了两个文件flag.tar和libarchive.dylib
一、解压缩
flag.tar是一个不能直接在windwos下解压缩的文件,直接用tar -xvf flag.tar命令解压缩:
提示不是一个tar压缩文件,但是其中包含了一个flag.txt的文件头,解压缩出了一个flag.txt文件。
再用file flag.tar命令来查看文件的格式:
是一个POSIX格式的tar压缩包
关于POSIX格式:
1、压缩
如果你使用的是GNU tar,那么就有个参数来设置所产生的文件的格式:
-H, --format=FORMAT 创建指定格式的归档
FORMAT 是以下格式中的一种:
gnu | GNU tar 1.13.x 格式 |
---|---|
oldgnu | GNU 格式 as per tar <= 1.12 |
pax | POSIX 1003.1-2001 (pax) 格式 |
posix | 等同于 pax |
ustar | POSIX 1003.1-1988 (ustar) 格式 |
v7 | old V7 tar 格式 |
所以假如你要把flag目录打包成 posix格式的tar包,就可以用
tar -cf flag.tar --format=posix flag
或者是
tar -cf flag.tar -H posix flag
2、解压:
tar -xvf flag.tar -H posix
二、查看文件
继续回到题目,打开flag.txt:
每4个16进制数字一组,以逗号分割,应该是另一个文件libarchive.dylib使用的输入或者输出文件,查看libarchive.dylib的格式:
是Mac平台下的64位动态链接共享库
用IDA64打开:
三、分析程序
根据flag.txt文件的格式,输入输出的时候应该是使用了格式化字符串的%04x、%04X之类的把数据格式化,直接在IDA字符串窗口中搜索%04x、%04X:
找到按%04X输出的位置,__int64 __fastcall archive_write_client_write(__int64 a1, __int64 a2, unsigned __int64 a3)函数:
函数原型:
int __sprintf_chk (char *s, int flags, size_t len, const char *format, ...)
__sprintf_chk(&v9[v3], 0, 0xFFFFFFFFFFFFFFFFLL, "%04X,", sub_101[v8++]);
把sub_101[v8++]根据格式字符串"%04X,",将格式化输出写入&v9[v3]
函数原型:
void * __memcpy_chk (void *dest, const void *src, size_t len, size_t dstlen)
__memcpy_chk(v10, v9, v18, -1LL);
把v9的值复制给v10
所以我们接下来看一下sub_101数组的值是怎么来的,选中sub_101右键Jump to xrefs或者快捷键X查看使用到sub_101数组的地方:
第二次就是在这个archive_write_client_write函数中,点击第一次使用sub_101数组的地方应该就是生成它的值的地方:
posi是一个全局变量的数组下标,这个sub_1023458函数每次由输入*a1计算得到一个sub_101[posi]的值。
再找到上一个调用sub_1023458函数的地方:
sub_1023456(int a1)函数:
果然sub_1023457函数就是在循环中每次调用sub_1023456函数根据输入的字符返回不同的下标返回值,作为sub_1023458函数的参数,调用sub_1023458函数给sub_101数组赋值。
sub_1023456(int a1)函数:
sub_1023458函数的实际参数v9数组有3个元素,刚好对应上面sub_1023458函数的*a1、a1[1]、a1[2]。
再找到调用sub_1023457函数的地方,发现就是archive_write_client_write函数:
四、程序主要逻辑:
所以现在的程序主要逻辑就是在archive_write_client_write函数中,流程图:
1、 首先archive_write_client_write函数第25行:
__memcpy_chk(v13, a2, a3, -1LL);
从输入的字符串a2中复制字符串的长度a3个字节到v13;
2、之后当字符串的长度a3>0x200时,调用sub_1023457(v13, (unsigned int)a3);对输入的字符串v13进行计算;
3、sub_1023456(v5)判断输入的字符串的每个字符,是否属于ctable[]数组,根据不同情况置pending标志位,然后设置返回值返回;
4、每执行3次v9[–v6] = sub_1023456(v5),重置了新的v9[3]数组的3个值,就执行一次sub_1023458(v9),sub_1023458函数中主要是计算得到一个sub_101[v2] = *a1 + 40 * a1[1] + 1600 * a1[2]的值;
5、计算结束之后,返回到archive_write_client_write函数:
__sprintf_chk(&__s[v3], 0, 0xFFFFFFFFFFFFFFFFLL, “%04X,”, sub_101[v11++]);
就会把sub_101[v8++]根据格式字符串"%04X,",将格式化输出写入&v9[v3]。
五、逆向思路:
所以我们现在是相当于知道了最后的结果sub_101,要得到最开始的输入v13。
步骤一:
1、首先是sub_1023458函数中解sub_101[v2] = a1 + 40 * a1[1] + 1600 * a1[2]这个三元一次方程组,每个sub_101[v2]的值都可以计算得到一组v9数组的值a1、a1[1]、a1[2]:
*a1=sub_101[v2] % 40;
a1[1]=(sub_101[v2] / 40) % 40;
a1[2]=sub_101[v2] / 1600;
步骤二:
2、之后每一组v9数组的值都对应1个sub_1023456函数输入的字符,也就是根据函数返回值(ctable数组下标),找到输入的字符(ctable数组下标对应的字符)。sub_1023457函数调用sub_1023456(v5)给v9数组的3个数赋值之后,再调用sub_1023458函数。
(1)sub_1023456(v5)函数原理是判断输入的字符,如果属于ctable数组前39个,pending 置0,直接返回对应的ctable数组下标i,并且返回之后会退出给v9[1]赋值的循环;
(2)如果如果属于ctable数组后39个(i+39),就返回39,且下一次的返回值就是i;如果不属于ctable数组就返回37。
所以我们在知道返回值的时候,要逆推得到输入的字符,就要分为返回值是39也就是属于后39个字符的情况和返回值是小于39也就是属于前39个字符的两种情况。
这里我们为了方便理解sub_1023457和sub_1023456函数的作用我们直接用C语言复现了它的逻辑:
#include<stdio.h>
#include<stdbool.h>
bool pending;
int sub_1023456_shifted = -1;
int ctable[] = {
0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73,
0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x20, 0x0A, 0x00,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x28, 0x21, 0x40, 0x23,
0x2C, 0x2E, 0x3F, 0x2F, 0x2A, 0x29, 0x3C, 0x3E, 0x00};
int main()
{
char * v3; // rax
unsigned int v5; // [rsp+4h] [rbp-2Ch]
int v6; // [rsp+8h] [rbp-28h]
int v9[3]; // [rsp+1Ch] [rbp-14h] BYREF
char a1[] = "abcABc";//定义一个字符数组
int length = sizeof(a1);
int i;
v6 = 3;
v3 = a1; // 输入的字符串
while ( v3-a1 < length )
{
v5 = *v3++;
//printf("%c ",v5);
pending = 1; // 这是一个全局bool变量pending,在sub_1023456(v5)中也有改变它的值
while ( pending )
{
v9[--v6] = sub_1023456(v5); // v9[2]、v9[1]、v9[0]=,一个字符v5最多可以得到3个不同的返回值
//printf("%d ",v9[v6]);
if ( !v6 ) // v6=0时执行
{
for(i = 0 ; i<3; i++){
printf("%d ",v9[i]);
}
printf("\\n");
//sub_1023458(v9); // v9数组每得到3个值就调用sub_1023458函数给sub_101数组赋一个值
v6 = 3; // 重新赋值为3
}
}
}
if ( v6 != 3 ) // 如果上面v9数组没有全部赋值,剩下的补足0后继续调用sub_1023458函数给sub_101数组赋最后一个值
{
while ( v6 != -1 )
v9[--v6] = 0;
for(i = 0 ; i<3; i++){
printf("%d ",v9[i]);
}
printf("\\n");
//sub_1023458(v9);
}
return 0;
}
int sub_1023456(int a1)
{
int v2; // [rsp+0h] [rbp-Ch]
int i; // [rsp+0h] [rbp-Ch]
int v4; // [rsp+4h] [rbp-8h]
unsigned int v5; // [rsp+8h] [rbp-4h]
v4 = a1; // 输入的字符
if ( sub_1023456_shifted == -1 )
{
if ( a1 == 126 ) // ASCII值等于126(‘~’)
v4 = 0;
for ( i = 0; i < 39; ++i )
{
if ( ctable[i] == v4 ) // ctable数组前39个包括小写字母和数字,如果v4在这里面
{
pending = 0; // pending 置0,会退出给v9赋值的循环
return (unsigned int)i; // 就返回下标
}
if ( ctable[i + 39] == v4 ) // ctable数组后39个包括大写字母和符号,如果v4在这里面
{
pending = 1; // pending 置1,会继续给v9[1]赋值
sub_1023456_shifted = i; // 这个值改为当前i,下次调用的时候就会执行else部分
return 39; // 返回39,下一次的返回值就直接为这次的i
}
}
pending = 0; // 如果v4的值不在数组里
v5 = 37; // ctable[37]是空格
}
else
{
v2 = sub_1023456_shifted; // 获得上一次执行返回的i
sub_1023456_shifted = -1; // 值重新置为-1,下次又执行if部分
pending = 0; // pending 置0
v5 = v2;
}
return v5; // 返回上一次执行的i
}
运行结果:
可以看到当输入的字符串是:"abcABc"时,对应的输出是:
3 2 1
39 1 39
0 3 2
其中字符’a’在ctable数组中的下标是1,所以返回值是1赋值给v9[2];
其中字符’b’在ctable数组中的下标是2,所以返回值是2赋值给v9[1];
其中字符’c’在ctable数组中的下标是3,所以返回值是3赋值给v9[0];
字符’A’在ctable数组中的下标是40,所以返回值是39赋值给v9[2],以及下一次的返回值是1赋值给v9[1];
字符’B’在ctable数组中的下标是41,所以返回值是39赋值给v9[0],以及下一次的返回值是2赋值给v9[2];
其中字符’c’在ctable数组中的下标是3,所以返回值是3赋值给v9[1];
最后这次v9[0]没有被赋值,所以最后补0。
六、解密代码:
到这,我们就完全明白了加密的流程和逆向的思路,接下来用Python编写解密代码:
# py -3
# -*- coding: utf-8 -*-
# coding:utf-8
# 最后flag.txt中的密文
ciphertext=[0xF5D1,0x4D6B,0xED6A,0x08A6,0x38DD,0xF7FA,0x609E,0xEBC4,0xE55F,0xE6D1,0x7C89,0xED5B,0x0871,0x1A69,0x5D58,0x72DE,0x224B,0x3AA6,0x0845,0x7DD6,0x58FB,0xE9CC,0x0A2D,0x76B8,0xED60,0x251A,0x1F6B,0x32CC,0xE78D,0x12FA,0x201A,0xE889,0x2D25,0x922A,0x4BC5,0xF5FF,0xF8E5,0xC79B,0x3A77,0x4BDB,0xEA11,0x5941,0x58BD,0x3A95,0xF5C9,0xA225,0xAD40,0xF8BD,0x095D,0x70B6,0x458C,0xE7A9,0xEA68,0x252F,0x094B,0x5E41,0x0969,0x6015,0x5ED5,0xF6E5,0x59B9,0x7CAF,0x66DF,0x265B,0x7837,0x57B4,0x7CAF,0xAED9,0xF707,0x6A3C,0xF8E5,0xF509,0x7C8B,0x0915,0x2235,0x336F,0x33E9,0x2D14,0x7C91,0x5804,0x83E5,0xE78D,0xF4EA,0x0874,0xED6B,0x4B35,0xE839,0x57B4,0xE77C,0xEA68,0x2525,0xAD41,0xED6F,0x3A4A,0x4BCC,0x6015,0xF440,0x0858,0x3AA6,0x7809,0x671D,0x0874,0xEA77,0x63AF,0x2E91,0x5845,0xF6C4,0x086D,0x7795,0x3939,0x57B4,0x7C89,0x82DC,0x32ED,0xB994,0xC7AF,0x9135,0x0E65,0x1B66,0xED5B,0x3235,0x6577,0x5A80,0x3AD3,0xE776,0x1EE5,0xAD41,0xED59,0x864C,0x70B4,0x3876,0xED67,0x64D6,0xF8E5,0xF505,0xEAD9,0x7C9C,0x32ED,0xB994,0xB4EF,0x0C6C,0xF665,0xF5F5,0x9047,0x521A,0xE99E,0xEA68,0x252F,0x9D09,0x76B7,0xE776,0x1ED0,0x095D,0x0D4D,0x5D5A,0x087B,0x2005,0x1526,0x7E76,0x85AD,0x78B9,0xE8B6,0x782C,0x251C,0x32ED,0x7F68,0xEBE3,0xEA41,0x57FD,0xED59,0x846D,0x7A05,0xB994,0xBB78,0xED6A,0x08A6,0x38DD,0x3B5D,0x7E45,0xE839,0x738C,0xE9CC,0x0A2D,0x764A,0x609E,0xE8B6,0xEA68,0x2524,0xE6BB,0x7C9C,0x639F,0x3A95,0x0895,0xF40F,0x8328,0xEA69,0x7EE5,0xF8BD,0x7F7D,0x0D6D,0x70B6,0x458C,0xE8B6,0xEA68,0x251C,0x6065,0xB35F,0xC789,0x5845,0x7F7D,0x6D89,0x4C6E,0xA20E,0x60B5,0x7E45,0xED59,0xF707,0x69EF,0x922A,0x4BC5,0xF6EF,0x8635,0xF4B9,0x57B4,0x7CF8,0xED60,0x2510,0x095D,0x20AF,0x3545,0xF40F,0x8328,0xEA41,0x58A4,0x225D,0x7E7C,0x4BDB,0xF8BD,0x082C,0xEAE7,0x5D57,0x5D50,0x0914,0xE7C7,0x8624,0x7CF8,0xED60,0x2511,0x7C8E,0x7159,0x8416,0x7EF9,0xE7E5,0x774A,0x3895,0x1EC9,0x7C90,0x09B9,0x58BD,0x5FF5,0xE99E,0xEA68,0x250A,0x224C,0xEA3D,0x73F5,0x7C89,0x53A6,0x3190,0x3B5D,0x1526,0x7DD5,0x666A,0x0919,0x225F,0xCDEF,0x79E1,0x7E7B,0x7E6B,0x082C,0xA277,0xE885,0xE8BB,0xE775,0x5FF7,0xEA68,0x251B,0x7FDF,0x589D,0x7A05,0x779A,0x8A5A,0x7C91,0x5D5C,0x32ED,0xF628,0x2195,0xF49A,0x0C77,0xEAE1,0x59B9,0x58BD,0xE570,0xE99E,0xEA3D,0x73F9,0x13AD,0x2BF5,0x225D,0x7F7D,0x70B6,0x4A9C,0x337A,0x1EC9,0x4D05,0x7E75,0x2578,0xED59,0x38E5,0x1ECA,0xA210,0x3B5D,0x779A,0x8A6F,0xC790,0x2518,0x4B41,0x7C89,0x5D49,0x4D05,0x152D,0x73C5,0x79F9,0x4BED,0x913C,0x37C9,0x5D4D,0x53C8,0x0941,0x7C97,0x5D5B,0x346A,0x82D8,0x5F36,0x801F,0xC800]
# 所有的v9数组
v9 = []
# 每个v9数组逆推得到的字符下标数组
v5 = []
# 输入的flag
flag = ""
# ctable字符常量数组
ctable 以上是关于XCTF-攻防世界CTF平台-Reverse逆向类——56tar-tar-binks的主要内容,如果未能解决你的问题,请参考以下文章
XCTF-攻防世界CTF平台-Reverse逆向类——53easyCpp
XCTF-攻防世界CTF平台-Reverse逆向类——65reverse-box
XCTF-攻防世界CTF平台-Reverse逆向类——59mfc逆向-200
XCTF-攻防世界CTF平台-Reverse逆向类——52handcrafted-pyc