第二届强网杯-Picturelock-文件AES加密解密

作者: skywilling 分类: Android逆向,逆向 发布时间: 2018-05-29 11:33

转自https://www.52pojie.cn/thread-745278-1-1.html

0x00前言

又一次遇到AES加密,莫名有种亲切感。这道题目是第二届强网杯逆向题目中的一道,应该是逆向题目中的压轴题吧(我只收到三个逆向题目,具体情况不是很清楚)。本篇文章带大家温习一下AES,长时间不接触真的会忘记,是时候复习一下以前的知识了!

0x01简单分析运行

题目拿到手里当然要看一下是什么类型的了。这是一道Android题目,直接以压缩包的方式打开:

在文件夹里我们发现了两个比较有用的文件,一个是后缀是.lock文件,另一个就是so文件,显然重要的处理方法保存在so文件中。
直接放到虚拟机中运行:

打开软件,界面很简洁,一个ENCRYPT(加密)按钮,一个REFRESH(刷新)按钮。点击第一个按钮后跳转到了另一个界面:

这是一个选择图片的界面,继续操作,又回到了主界面:

这时候界面多了一些信息,文本框多了刚才选中图片的文件名,需要注意的是,文件名后多了(.lock)后缀。同时,弹出了一个土司,里面显示了图片的绝对路径。了解了软件运行情况后,我们开始反编译。

0x02 Java层静态分析

反编译后,我们直接找到点击事件ENCRYPT按钮绑定的OnClick()方法:

对Android开发有一定了解的话,可以看出,代码首先检查了文件读写的权限,然后打开了图片选择的界面,选择图片后,会调用方法onActivityResult():

我们先不管这里具体进行了什么操作,可以看到这里有3个方法被调用了,分别是enc(),j(),i()。enc()是native层的方法,所以在这里就不说了,一会儿具体说,重点看一下i()和j()。

大概可以看出i()使用来显示文本框中的内容,对图片的处理没有影响,这里就不再详细说明。

这个j()方法就有点意思了,这个方法大概是获取APK包签名的MD5值,具体是什么,我们一会儿动态调试获取。

根据enc()方法的声明,可以知道,方法需要三个String类型的参数,这三个参数具体是什么,我们用动态调试的方法获取。

0x03 Java层动态分析

动态调试能够快速获取enc()方法的参数。在这里我们使用Android Studio和插件Smalidea。我们直接在方法调用处下断点:

查看寄存器:

我们看见第一个参数就是图片的绝对路径,第二个参数是加密后的文件的绝对路径,第三个参数就是方法j()获得的MD5值。需要注意的是我这里的APK包是修改过的,所以这里显示的MD5不是未修改包的MD5值,原包的MD5值应该是f8c49056e4ccf9a11e090eaf471f418d。到这里我们就弄清楚了enc()的三个参数,由此得到,enc(filepath,lockpath,signature)。filepath:图片的绝对路径,lockpath:加密后文件绝对路径,signature:APK包签名的MD5值。一般来说,CTF比赛题目的答案就是一个flag(若干个字符组成的字符串)。前面也提到APK包里有一个加密文件,那么flag极有可能就在解密后的文件中,那么我们就需要深入地分析加密过程了。

0x04Native层分析

直接把so文件拖入IDA分析。查看导出表:

很幸运,没有经过混淆,直接可以看到enc()方法的入口。直接F5还原成伪C代码:

在这里我修改了一些函数名,方便分析。通过分析,我们发现sub_51EC7A48是关键的代码段。

[C] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
int __fastcall sub_51EC7A48(char *filepath, char *lockpath)
{
  int *dwords; // r5
  int bytes_temp; // r6
  char *filepath1; // r9
  char *lockpath1; // r8
  _DWORD *temp; // r0
  _DWORD *temp1; // r6
  int signature_array1; // r0
  int index; // r1
  int v10; // r3
  int index1; // r4
  int a; // r0
  int v13; // r5
  int v14; // r1
  int v15; // r2
  int temp3; // r10
  int temp_192; // r0
  int signature_array2; // r1
  int index2; // r2
  int index3; // r4
  int b; // r0
  int v22; // r1
  char i; // r9
  char c; // r11
  size_t readSize; // r0
  size_t readSize1; // r4
  _BYTE *readEnd; // r0
  int readLast; // r8
  int *v29; // r0
  int *new_temp; // r11
  signed int index4; // r1
  int signature_array3; // r0
  int result; // r0
  FILE *inputstream; // [sp+0h] [bp-28h]
  FILE *outputstream; // [sp+4h] [bp-24h]
  char bytes_temp0; // [sp+8h] [bp-20h]
  char bytes_temp4; // [sp+9h] [bp-1Fh]
  char bytes_temp8; // [sp+Ah] [bp-1Eh]
  char bytes_temp12; // [sp+Bh] [bp-1Dh]
  char bytes_temp1; // [sp+Ch] [bp-1Ch]
  char bytes_temp5; // [sp+Dh] [bp-1Bh]
  char bytes_temp9; // [sp+Eh] [bp-1Ah]
  char bytes_temp13; // [sp+Fh] [bp-19h]
  char bytes_temp2; // [sp+10h] [bp-18h]
  char bytes_temp6; // [sp+11h] [bp-17h]
  char bytes_temp10; // [sp+12h] [bp-16h]
  char bytes_temp14; // [sp+13h] [bp-15h]
  char bytes_temp3; // [sp+14h] [bp-14h]
  char bytes_temp7; // [sp+15h] [bp-13h]
  char bytes_temp11; // [sp+16h] [bp-12h]
  char bytes_temp15; // [sp+17h] [bp-11h]
  int v52; // [sp+18h] [bp-10h]
  filepath1 = filepath;
  lockpath1 = lockpath;
  if ( !temp2 )
  {
    temp = malloc(0x180u);
    temp1 = temp;
    temp2 = (int)temp;
    signature_array1 = signature_array;
    index = 0;
    do
    {
      v10 = *(unsigned __int8 *)(signature_array1 + index * 4 + 3);
      temp1[index] = _byteswap_ulong(*(_DWORD *)(signature_array1 + index * 4));
      ++index;
    }
    while ( index != 4 );
    index1 = 0;
    a = temp1[3];
    do
    {
      if ( !((index1 + 4) & 3) )
      {
        v13 = *(int *)((char *)&dword_51EC9F20 + ((index1 + 3 + ((unsigned int)((index1 + 3) >> 31) >> 30)) & 0xFFFFFFFC));
        a = func0(__ROR4__(a, 24)) ^ v13;
      }
      v14 = temp1[index1];
      v15 = (int)&temp1[index1++];
      a ^= v14;
      *(_DWORD *)(v15 + 16) = a;
    }
    while ( index1 != 40 );
    temp3 = temp2;
    temp_192 = temp2 + 192;
    temp_192_1 = temp2 + 192;
    signature_array2 = signature_array;
    index2 = 0;
    do
    {
      bytes_temp = (*(unsigned __int8 *)(signature_array2 + index2 + 17) << 16) | (*(unsigned __int8 *)(signature_array2 + index2 + 16) << 24) | (*(unsigned __int8 *)(signature_array2 + index2 + 18) << 8);
      *(_DWORD *)(temp_192 + index2) = _byteswap_ulong(*(_DWORD *)(signature_array2 + index2 + 16));
      index2 += 4;
    }
    while ( index2 != 16 );
    index3 = 0;
    b = *(_DWORD *)(temp3 + 204);
    dwords = &dword_51EC9F20;
    do
    {
      if ( !((index3 + 4) & 3) )
      {
        bytes_temp = *(int *)((char *)&dword_51EC9F20
                            + ((index3 + 3 + ((unsigned int)((index3 + 3) >> 31) >> 30)) & 0xFFFFFFFC));
        b = func0(__ROR4__(b, 24)) ^ bytes_temp;
      }
      v22 = temp3 + 4 * index3++;
      b ^= *(_DWORD *)(v22 + 192);
      *(_DWORD *)(v22 + 208) = b;
    }
    while ( index3 != 40 );
  }
  inputstream = fopen(filepath1, (const char *)&r);
  if ( inputstream )
  {
    outputstream = fopen(lockpath1, (const char *)&w);
    if ( outputstream )
    {
      bytes_temp = (int)malloc(0x100u);
      lockpath1 = (char *)inputstream;
      dwords = (int *)malloc(0x100u);
      for ( i = 0; ; ++i )
      {
        c = *(_BYTE *)(signature_array + (i & 0x1F));
        readSize = fread((void *)bytes_temp, 1u, *(unsigned __int8 *)(signature_array + (i & 0x1F)), (FILE *)lockpath1);
        readSize1 = readSize;
        if ( !readSize )
          goto LABEL_31;
        if ( readSize <= 0xF )
        {
          readEnd = (_BYTE *)(bytes_temp + readSize);
          readLast = 16 - (readSize1 & 0xF);
          if ( (readSize1 & 0xF) != 16 )
          {
            _aeabi_memset(readEnd, 16 - (readSize1 & 0xF), (unsigned __int8)readLast);
            readEnd = (_BYTE *)(readLast + readSize1 + bytes_temp);
          }
          lockpath1 = (char *)inputstream;
          readSize1 = 16;
          *readEnd = 0;
        }
        v29 = &temp_192_1;
        if ( !(c & 1) )
          v29 = &temp2;
        new_temp = (int *)*v29;
        bytes_temp0 = *(_BYTE *)bytes_temp;
        bytes_temp1 = *(_BYTE *)(bytes_temp + 1);
        bytes_temp2 = *(_BYTE *)(bytes_temp + 2);
        bytes_temp3 = *(_BYTE *)(bytes_temp + 3);
        bytes_temp4 = *(_BYTE *)(bytes_temp + 4);
        bytes_temp5 = *(_BYTE *)(bytes_temp + 5);
        bytes_temp6 = *(_BYTE *)(bytes_temp + 6);
        bytes_temp7 = *(_BYTE *)(bytes_temp + 7);
        bytes_temp8 = *(_BYTE *)(bytes_temp + 8);
        bytes_temp9 = *(_BYTE *)(bytes_temp + 9);
        bytes_temp10 = *(_BYTE *)(bytes_temp + 10);
        bytes_temp11 = *(_BYTE *)(bytes_temp + 11);
        bytes_temp12 = *(_BYTE *)(bytes_temp + 12);
        bytes_temp13 = *(_BYTE *)(bytes_temp + 13);
        bytes_temp14 = *(_BYTE *)(bytes_temp + 14);
        bytes_temp15 = *(_BYTE *)(bytes_temp + 15);
        func1(&bytes_temp0, new_temp);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 4);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 8);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 12);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 16);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 20);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 24);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 28);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 32);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func4((unsigned __int8 *)&bytes_temp0);
        func1(&bytes_temp0, new_temp + 36);
        func2(&bytes_temp0);
        func3(&bytes_temp0);
        func1(&bytes_temp0, new_temp + 40);
        *(_BYTE *)dwords = bytes_temp0;
        *((_BYTE *)dwords + 1) = bytes_temp1;
        *((_BYTE *)dwords + 2) = bytes_temp2;
        *((_BYTE *)dwords + 3) = bytes_temp3;
        *((_BYTE *)dwords + 4) = bytes_temp4;
        *((_BYTE *)dwords + 5) = bytes_temp5;
        *((_BYTE *)dwords + 6) = bytes_temp6;
        *((_BYTE *)dwords + 7) = bytes_temp7;
        *((_BYTE *)dwords + 8) = bytes_temp8;
        *((_BYTE *)dwords + 9) = bytes_temp9;
        *((_BYTE *)dwords + 10) = bytes_temp10;
        *((_BYTE *)dwords + 11) = bytes_temp11;
        *((_BYTE *)dwords + 12) = bytes_temp12;
        *((_BYTE *)dwords + 13) = bytes_temp13;
        *((_BYTE *)dwords + 14) = bytes_temp14;
        *((_BYTE *)dwords + 15) = bytes_temp15;
        if ( readSize1 >= 0x11 )
        {
          index4 = 16;
          signature_array3 = signature_array;
          do
          {
            *((_BYTE *)dwords + index4) = *(_BYTE *)(bytes_temp + index4) ^ *(_BYTE *)(signature_array3 + index4 % 32);
            ++index4;
          }
          while ( index4 < readSize1 );
        }
        if ( fwrite(dwords, 1u, readSize1, outputstream) != readSize1 )
          break;
      }
    }
  }
  result = -1;
  while ( _stack_chk_guard != v52 )
  {
LABEL_31:
    free((void *)bytes_temp);
    free(dwords);
    fclose((FILE *)lockpath1);
    fclose(outputstream);
    result = 0;
  }
  return result;
}

还原出来的伪C代码,并不是完全可信的,但是还是有很大参考价值的。

根据伪C代码我们可以还原出C代码:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
int i = 0, j,k;
temp = (unsigned int*)malloc(0x180);
memset(temp,0,0x180);
do {
  temp[i] = _byteswap_ulong(*(unsigned int*)(signature + i * 4));
  ++i;
} while (i != 4);
i = 0;
a = temp[3];
do {
  if (!((i + 4) & 3)) {
    a = func0(ror(a, 24)) ^ *(unsigned int*)(dwords + ((i + 3 + ((unsigned int)((i + 3) >> 31) >> 30)) & 0xFFFFFFFC));
  }
  a ^= temp[i];
  temp[i+4] = a;
  i++;
} while (i != 40);

这段代码是根据前16个字节扩展出40个双字。

还原出C代码:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
i = 0;
do {
  temp[i+48] = _byteswap_ulong(*(unsigned int*)(signature + i*4 + 16));
  i++;
} while (i != 4);
i = 0;
b = temp[51];
do {
  if (!((i + 4) & 3)) {
    b = func0(ror(b, 24)) ^ *(unsigned int*)(dwords + ((i + 3 + ((unsigned int)((i + 3) >> 31) >> 30)) & 0xFFFFFFFC));
  }
  b ^= temp[i+48];
  temp[i+52] = b;
  i++;
} while (i != 40);


这段代码是根据后16个字节扩展出40个双字。

这就是密钥扩展出来的88个双字,继续往下看。

这里调用了fopen(),以读模式打开了图片文件,以写模式打开了加密文件。同样可以转换为C代码:

[C] 纯文本查看 复制代码
1
2
fopen_s(&inputstream,filepath, "rb");
fopen_s(&outputstream,lockpath, "wb");

接下来就是读取文件,在这里,根据signature数组获取特定长度的数据,保存到bytes_temp中。

还原出C代码:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
if (readSize <= 0xF) {
  readEnd = (unsigned char*)(bytes_temp + readSize);
  readLast = 16 - (readSize & 0xF);
  if ((readSize & 0xF) != 16) {
    memset((void*)readEnd, readLast, readLast);
    readEnd = (unsigned char*)readLast + readSize + (unsigned int)bytes_temp;
  }
  readSize = 16;
  *readEnd = 0;
}

这里是处理了长度不足0x10的数据,该代码段的实际作用就是填充不足0x10的部分数据。

还原出C代码:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
if (!(c & 1)) {                //偶数
  new_temp = temp;
}
else {                                //奇数
  new_temp = &temp[48];
}
//转置
for (j=0;j<4;j++) {
  for (k=0;k<4;k++) {
    result[j*4+k]=bytes_temp[k*4+j];
  }
}

这里是根据读取数据长度的奇偶性来决定使用第一段密钥还是第二段密钥。接下来就是把读取数据的前16个字节保存到了新的地方,并进行了转置,也就是保存为了一个4×4矩阵,后面的操作都是基于这个矩阵来进行的。需要注意的是,要看出这里进行了转置,就需要动态调试了。接下来可以看到重复调用几个函数:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
func1(&bytes_temp0, new_temp);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 4);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 8);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 12);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 16);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 20);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 24);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 28);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 32);
func2(&bytes_temp0);
func3(&bytes_temp0);
func4((unsigned __int8 *)&bytes_temp0);
func1(&bytes_temp0, new_temp + 36);
func2(&bytes_temp0);
func3(&bytes_temp0);
func1(&bytes_temp0, new_temp + 40);

整理成C代码是:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
for (j = 0; j < 9; j++) {
  func1(result, new_temp + 4 * j);
  func2(result);
  func3(result);
  func4(result);
}
func1(result, new_temp + 4 * j++);
func2(result);
func3(result);
func1(result, new_temp + 4 * j);

先说func1(),伪C代码是:

整理后

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
unsigned char *func1(unsigned char* result, unsigned int*new_temp) {
        unsigned int temp;
        temp=new_temp[0];
        result[0] ^= (temp >> 24);
        result[4] ^= (temp >> 16);
        result[8] ^= (temp >> 8);
        result[12] ^= temp;
        temp = new_temp[1];
        result[1] ^= (temp >> 24);
        result[5] ^= (temp >> 16);
        result[9] ^= (temp >> 8);
        result[13] ^= temp;
        temp = new_temp[2];
        result[2] ^= (temp >> 24);
        result[6] ^= (temp >> 16);
        result[10] ^= (temp >> 8);
        result[14] ^= temp;
        temp = new_temp[3];
        result[3] ^= (temp >> 24);
        result[7] ^= (temp >> 16);
        result[11] ^= (temp >> 8);
        result[15] ^= temp;
        /*
        简化后:
        int i;
        for (i=0;i<4;i++) {
                result[i*4]^=new_temp[i]>>24;
                result[i*4+1]^=new_temp[i]>>16;
                result[i*4+2]^=new_temp[i]>>8;
                result[i*4+3]^=new_temp[i];
        }
        */
        return result;
}

很显然这里进行了异或操作。然后是func2(),伪C代码是:

整理后是:

[C] 纯文本查看 复制代码
1
2
3
4
5
6
7
unsigned char *func2(unsigned char*result) {
        int i;
        for (i = 0; i < 16; i++) {
                result[i] = s_box[(result[i] & 0xF0) + (result[i] & 0x0F)];
        }
        return result;
}

这里就是S盒的置换操作了。func3()的伪C代码:

整理后:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
unsigned char *func3(unsigned char*result) {
        unsigned char temp;
        temp = result[4];
        result[4] = result[5];
        result[5] = result[6];
        result[6] = result[7];
        result[7] = temp;
        temp = result[8];
        result[8] = result[10];
        result[10] = temp;
        temp = result[9];
        result[9] = result[11];
        result[11] = temp;
        temp = result[12];
        result[12] = result[15];
        result[15] = result[14];
        result[14] = result[13];
        result[13] = temp;
        return result;
}

这里进行了正向行移位操作。最后是func4(),伪c代码是:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
unsigned __int8 *__fastcall func4(unsigned __int8 *result)
{
  int result0; // r2
  int result12; // r3
  int result8; // lr
  int result4; // r12
  char result0_byte; // r10
  char result12_byte; // r9
  char v7; // r6
  char v8; // r4
  char v9; // r5
  int result13; // r6
  int result1; // r7
  int result5; // r8
  int result9; // r3
  char result13_byte; // r9
  char result13_byte_1; // r11
  char result1_byte; // r10
  int result14; // r6
  int result2; // r7
  int result6; // r8
  int result10; // r3
  char v21; // r9
  char v22; // r11
  char v23; // r10
  int result15; // r6
  int result3; // r7
  int result7; // r2
  int result11; // r3
  char v28; // r9
  char v29; // r10
  result0 = *result;
  result12 = result[12];
  result8 = result[8];
  result4 = result[4];
  result0_byte = byte_51EC9920[6 * result0 + 1];
  result12_byte = byte_51EC9920[6 * result12];
  v7 = result12 ^ result8 ^ byte_51EC9920[6 * result0];
  v8 = byte_51EC9920[6 * result8 + 1];
  v9 = byte_51EC9920[6 * result4] ^ result0;
  LOBYTE(result0) = result0 ^ result4 ^ byte_51EC9920[6 * result8] ^ byte_51EC9920[6 * result12 + 1];
  *result = v7 ^ byte_51EC9920[6 * result4 + 1];
  result[4] = result12 ^ v9 ^ v8;
  result[8] = result0;
  result[12] = result8 ^ result4 ^ result0_byte ^ result12_byte;
  result13 = result[13];
  result1 = result[1];
  result5 = result[5];
  result9 = result[9];
  result13_byte = byte_51EC9920[6 * result13];
  result13_byte_1 = byte_51EC9920[6 * result13 + 1];
  result1_byte = byte_51EC9920[6 * result1 + 1];
  LOBYTE(result8) = byte_51EC9920[6 * result5];
  LOBYTE(result4) = byte_51EC9920[6 * result9];
  result[1] = byte_51EC9920[6 * result5 + 1] ^ result13 ^ result9 ^ byte_51EC9920[6 * result1];
  result[5] = result8 ^ result1 ^ result13 ^ byte_51EC9920[6 * result9 + 1];
  result[9] = result5 ^ result1 ^ result4 ^ result13_byte_1;
  result[13] = result9 ^ result5 ^ result1_byte ^ result13_byte;
  result14 = result[14];
  result2 = result[2];
  result6 = result[6];
  result10 = result[10];
  v21 = byte_51EC9920[6 * result14];
  v22 = byte_51EC9920[6 * result14 + 1];
  v23 = byte_51EC9920[6 * result2 + 1];
  LOBYTE(result8) = byte_51EC9920[6 * result6];
  LOBYTE(result4) = byte_51EC9920[6 * result10];
  result[2] = byte_51EC9920[6 * result6 + 1] ^ result14 ^ result10 ^ byte_51EC9920[6 * result2];
  result[6] = result8 ^ result2 ^ result14 ^ byte_51EC9920[6 * result10 + 1];
  result[10] = result6 ^ result2 ^ result4 ^ v22;
  result[14] = result10 ^ result6 ^ v23 ^ v21;
  result15 = result[15];
  result3 = result[3];
  result7 = result[7];
  result11 = result[11];
  LOBYTE(result8) = byte_51EC9920[6 * result15];
  v28 = byte_51EC9920[6 * result15 + 1];
  LOBYTE(result6) = byte_51EC9920[6 * result3 + 1];
  result[3] = byte_51EC9920[6 * result3] ^ result15 ^ result11 ^ byte_51EC9920[6 * result7 + 1];
  v29 = byte_51EC9920[6 * result11];
  result[7] = byte_51EC9920[6 * result7] ^ result3 ^ result15 ^ byte_51EC9920[6 * result11 + 1];
  result[11] = result7 ^ result3 ^ v29 ^ v28;
  result[15] = result11 ^ result7 ^ result6 ^ result8;
  return result;
}

整理后:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
unsigned char *func4(unsigned char*result) {
        unsigned char a,b,c,d;
        a=result[0];
        b=result[4];
        c=result[8];
        d=result[12];
        result[0] = func5(c,d,a,b);
        result[4] = func5(a,d,b,c);
        result[8] = func5(a,b,c,d);
        result[12] = func5(b,c,d,a);
        a=result[1];
        b=result[5];
        c=result[9];
        d=result[13];
        result[1] = func5(c, d, a, b);
        result[5] = func5(a, d, b, c);
        result[9] = func5(a, b, c, d);
        result[13] = func5(b, c, d, a);
        a = result[2];
        b = result[6];
        c = result[10];
        d = result[14];
        result[2] = func5(c, d, a, b);
        result[6] = func5(a, d, b, c);
        result[10] = func5(a, b, c, d);
        result[14] = func5(b, c, d, a);
        a = result[3];
        b = result[7];
        c = result[11];
        d = result[15];
        result[3] = func5(c, d, a, b);
        result[7] = func5(a, d, b, c);
        result[11] = func5(a, b, c, d);
        result[15] = func5(b, c, d, a);
        /*简化后
        int i;
        char a,b,c,d;
        for (i=0;i<4;i++) {
                a=result[i];
                b=result[i+4];
                c=result[i+8];
                d=result[i+12];
                result[i] = func5(c, d, a, b);
                result[i+4] = func5(a, d, b, c);
                result[i+8] = func5(a, b, c, d);
                result[i+12] = func5(b, c, d, a);
        }
        */
        return result;
}

其实这里就是列混淆,只是这里的实现方法与以往的不同而已。

加密流程结束后,又进行了矩阵的转置操作。最后是:

整理后:

[C] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
//转置
for (j = 0; j<4; j++) {
  for (k = j; k<4; k++) {
    if (j != k) {
      unsigned char temp = result[j * 4 + k];
      result[j * 4 + k] = result[k * 4 + j];
      result[k * 4 + j] = temp;
    }
  }
}
if (fwrite(result, 1, readSize, outputstream) != readSize) {
  break;
}

这里是对读取数据位置大于0x10的数据进行异或操作,最后写入到文件里。到这里这块数据的加密流程就分析完毕了。在这里我们总结一下加密的流程,首先是读取一个特定长度的数据块,然后取出前16个字节进行AES加密,再把剩余的数据进行异或操作,这样一块数据就加密完毕了。对每块数据进行相同的操作,最后得到加密文件。整个加密流程大致就是这样。我们已经知道了加密流程,那么解密方法就很容易得到了,这些代码我都会放到文章最后地附件中。

0x05写在最后

这样我们就可以解密出加密图片了,最后附上结果图:

附件:https://pan.baidu.com/s/1qrKwTZEqHuajOpADhaPk0A 密码:59dn

版权声明:允许转载,但是一定要注明出处。

发表评论

电子邮件地址不会被公开。 必填项已用*标注