这是一个信安比赛的题目,有兴趣的可以拿来练练手,题目如下:
key.txt的内容为:NDE4OUM3MUVBNUZFOTFBM0NCRUI5OTcyMDUzN0VBNEIzMjIxRUIxNDhCOTU3Mzcw=
好,现在开始逆向该程序
运行后,界面如下:
这里,原始数据输入12345678,然后加密文本
这个提示很关键,得到的密文在encrypted.txt中,密文为:MDhGMkQ5RDA0RTU2MzY3NkRFRDRCQUFCODREMjU0OTY=
使用IDA搜索字符串,即可得到调用这个字符串的位置。然后找到这个位置的函数头,在407AB0处设断,用OD载入运行。
.text:00407AB0 push ebp
.text:00407AB1 mov ebp, esp
.text:00407AB3 push 0FFFFFFFFh
.text:00407AB5 push offset SEH_407AB0
当点击加密文本,就会在断点处停下。说明断点没有问题,但是这个函数里面有大量的函数调用,要是一个个分析还是挺繁琐的。所以当我运行到某个函数调用时我会想用IDA看看子函数的功能,考虑要不要进入。关键是要找个与自己输入数据有关的算法即可。
00407AE4 |. 59 pop ecx
00407AE5 |. 894D F0 mov [local.4],ecx
00407AE8 |. 6A 01 push 0x1
00407AEA |. 8B4D F0 mov ecx,[local.4]
00407AED |. E8 89CA0800 call Key-enc.0049457B
00407AF2 |. 8B4D F0 mov ecx,[local.4]
00407AF5 |. 83C1 5C add ecx,0x5C ;输入数据的偏移
00407AF8 |. E8 63A60D00 call Key-enc.004E2160 ; 关键函数 判断输入数据长度
00407AFD |. 85C0 test eax,eax
00407AFF |. 7F 13 jg XKey-enc.00407B14 ;若输入数据为0则结束
00407B01 |. 6A 00 push 0x0
00407B03 |. 6A 00 push 0x0
00407B05 |. 68 B8D55C00 push Key-enc.005CD5B8 ; 加密数据不能为空!
00407B0A |. E8 12C80A00 call Key-enc.004B4321
00407B0F |. E9 32020000 jmp Key-enc.00407D46
00407B14 |> 8D4D EC lea ecx,[local.5] ; 18f630
00407B17 |. E8 A097FFFF call Key-enc.004012BC ; 将5cd01c写入18f630
00407B1C |. C745 FC 00000>mov [local.1],0x0
00407B23 |. 8D4D DC lea ecx,[local.9] ; 18f620
00407B26 |. E8 A795FFFF call Key-enc.004010D2 ; 将5cd094写入18f620
00407B2B |. C645 FC 01 mov byte ptr ss:[ebp-0x4],0x1
00407B2F |. 8D45 DC lea eax,[local.9]
00407B32 |. 50 push eax ; 18f620
00407B33 |. E8 3596FFFF call Key-enc.0040116D ; 获取关键信息到 [18f620+0c]
00407B38 |. 83C4 04 add esp,0x4
00407B3B |. 8D4D DC lea ecx,[local.9]
00407B3E |. E8 EF94FFFF call Key-enc.00401032 ; 关键函数这里会进行一些算法的转换,但与输入数据无关
为了节省时间 若后来用的到只需用其结果即可。
00407B43 |. 8D4D DC lea ecx,[local.9]
00407B46 |. E8 E695FFFF call Key-enc.00401131 ; [18f620+0c]处的数据会变
00407B4B |. 8D4D CC lea ecx,[local.13] ; 18f610
00407B4E |. E8 7F95FFFF call Key-enc.004010D2 ; 将5cd094写入ecx
00407B53 |. C645 FC 02 mov byte ptr ss:[ebp-0x4],0x2
00407B57 |. 8D4D CC lea ecx,[local.13]
00407B5A |. 51 push ecx
00407B5B |. 8B4D F0 mov ecx,[local.4]
00407B5E |. 83C1 5C add ecx,0x5C ; 终于看到输入数据啦嘿嘿
00407B61 |. E8 FAA50D00 call Key-enc.004E2160
00407B66 |. 50 push eax ; size
00407B67 |. 8B4D F0 mov ecx,[local.4]
00407B6A |. 83C1 5C add ecx,0x5C ; buffer
00407B6D |. E8 38A60D00 call Key-enc.004E21AA
00407B72 |. 50 push eax ; 输入数据的buffer
00407B73 |. 8D4D DC lea ecx,[local.9]
00407B76 |. E8 5F97FFFF call Key-enc.004012DA
00407B7B |. 50 push eax ; size2
00407B7C |. 8D4D DC lea ecx,[local.9]
00407B7F |. E8 7096FFFF call Key-enc.004011F4
00407B84 |. 50 push eax ; 相关数据的buffer
00407B85 |. E8 7E96FFFF call Key-enc.00401208 ; 关键函数 变第一次
这个函数应该比较关键 有5个压栈参数这里有输入数据的偏移,大小,关键信息的偏移,大小,和一个地址,在函数返回后即算法结束后得到的结果应该与该地址有关
这里的地址为18f610,在18f610+c的地方存的就是数据的地址
0018F610 005CD094 斝\. Key-enc.005CD094
0018F614 00000008 ...
0018F618 00000801 ..
0018F61C 02127798 榳
02127798 92 A8 01 86 FB BA 80 4B 00 00 00 00 00 00 00 00 挩嘂簚K........
这个数据应该就是进行第一次算法变换后得到的数据,然后就密切关注该数据怎么样变化才能得到最终的密文,这是我是先找关键地方的代码,最后再分析具体流程,上面红色字就会第一次关键变换。
接着分析主函数:
00407B85 |. E8 7E96FFFF call Key-enc.00401208 ; 关键函数 变一次
00407B8A |. 83C4 14 add esp,0x14
00407B8D |. 8D4D CC lea ecx,[local.13]
00407B90 |. E8 9D94FFFF call Key-enc.00401032 ; 关键函数 变第二次 kqgBhvu6gEs=
这里的地址用ecx传的,还是为18f610 在18f610+c处存的地址有变化
0018F610 005CD094 斝\. Key-enc.005CD094
0018F614 0000000C ....
0018F618 00000801 ..
0018F61C 02126728 (g ASCII "kqgBhvu6gEs="
02126728 6B 71 67 42 68 76 75 36 67 45 73 3D 00 00 00 00 kqgBhvu6gEs=....
这个数据应该就是进行第二次算法变换后得到的数据,然后就密切关注该数据怎么样变化才能得到最终的密文。
接着分析主函数 这里也有很多函数调用,可以用IDA分析具体功能,一下是我自己分析的一些作用的标注
00407B95 |. 8D4D C8 lea ecx,[local.14]
00407B98 |. E8 FC96FFFF call Key-enc.00401299
00407B9D |. C645 FC 03 mov byte ptr ss:[ebp-0x4],0x3
00407BA1 |. 8D4D B8 lea ecx,[local.18]
00407BA4 |. E8 2995FFFF call Key-enc.004010D2 ; 18f5fc 不用跟进
00407BA9 |. C645 FC 04 mov byte ptr ss:[ebp-0x4],0x4
00407BAD |. 8D55 B8 lea edx,[local.18]
00407BB0 |. 52 push edx
00407BB1 |. E8 C096FFFF call Key-enc.00401276
00407BB6 |. 83C4 04 add esp,0x4
00407BB9 |. 8D4D B8 lea ecx,[local.18]
00407BBC |. E8 7095FFFF call Key-enc.00401131 ; [18f5fc+0x0c]的数据与输入无关
00407BC1 |. 8D4D A8 lea ecx,[local.22]
00407BC4 |. E8 0995FFFF call Key-enc.004010D2
00407BC9 |. C645 FC 05 mov byte ptr ss:[ebp-0x4],0x5 ; 18f5ec 不用跟进
00407BCD |. 8D45 A8 lea eax,[local.22]
00407BD0 |. 50 push eax
00407BD1 |. 8D4D CC lea ecx,[local.13]
00407BD4 |. E8 0197FFFF call Key-enc.004012DA ; 取[18f610+0x0c]数据的长度
00407BD9 |. 50 push eax
00407BDA |. 8D4D CC lea ecx,[local.13]
00407BDD |. E8 1296FFFF call Key-enc.004011F4 ; 不用管
00407BE2 |. 50 push eax
00407BE3 |. 8D4D B8 lea ecx,[local.18]
00407BE6 |. E8 EF96FFFF call Key-enc.004012DA
00407BEB |. 50 push eax ; [18f5fc+0x0c]的数据长度
00407BEC |. 8D4D B8 lea ecx,[local.18]
00407BEF |. E8 0096FFFF call Key-enc.004011F4
00407BF4 |. 50 push eax
00407BF5 |. E8 0E96FFFF call Key-enc.00401208 ; 关键函数 第三次变
这个函数和第一个关键函数一样 有5个压栈参数这里有输入数据的偏移,大小,关键信息的偏移,大小,和一个地址,在函数返回后即算法结束后得到的结果应该与该地址有关
这里的地址为18f5ec,在18f5ec+c的地方存的就是数据的地址
0018F5EC 005CD094 斝\. Key-enc.005CD094
0018F5F0 00000010 ...
0018F5F4 00000801 ..
0018F5F8 02128808 ?
02128808 08 F2 D9 D0 4E 56 36 76 DE D4 BA AB 84 D2 54 96 蛸蠳V6v拊韩勔T?
这个数据应该就是进行第三次算法变换后得到的数据,然后就密切关注该数据怎么样变化才能得到最终的密文
接着分析主函数:
00407BFA |. 83C4 14 add esp,0x14
00407BFD |. 8D4D A8 lea ecx,[local.22]
00407C00 |. E8 0094FFFF call Key-enc.00401005 ; 要进去 第四次
这里有ecx传参数 为18f5ec,运行后,
这里的地址为18f5ec,在18f5ec+c的地方存的就是数据的地址
0018F5EC 005CD094 斝\. Key-enc.005CD094
0018F5F0 00000020 ...
0018F5F4 00000800 ...
0018F5F8 02127798 榳 ASCII "08F2D9D04E563676DED4BAAB84D25496"
02127798 30 38 46 32 44 39 44 30 34 45 35 36 33 36 37 36 08F2D9D04E563676
021277A8 44 45 44 34 42 41 41 42 38 34 44 32 35 34 39 36 DED4BAAB84D25496
这个数据应该就是进行第四次算法变换后得到的数据 不过这功能很明显,和第三次算法后得到的数据对比 即可知晓。
然后就密切关注该数据怎么样变化才能得到最终的密文
接着分心主函数:
00407C05 |. 8D4D A8 lea ecx,[local.22]
00407C08 |. E8 2594FFFF call Key-enc.00401032 ; 关键函数 第五次
00407C0D |. 8D4D 98 lea ecx,[local.26] ; 最终数据已经出来了
这里有ecx传参数 为18f5ec,运行后,
这里的地址为18f5ec,在18f5ec+c的地方存的就是数据的地址
0018F5EC 005CD094 斝\. Key-enc.005CD094
0018F5F0 0000002C ,...
0018F5F4 00000801 ..
0018F5F8 00227798 榳". ASCII "MDhGMkQ5RDA0RTU2MzY3NkRFRDRCQUFCODREMjU0OTY="
00227798 4D 44 68 47 4D 6B 51 35 52 44 41 30 52 54 55 32 MDhGMkQ5RDA0RTU2
002277A8 4D 7A 59 33 4E 6B 52 46 52 44 52 43 51 55 46 43 MzY3NkRFRDRCQUFC
002277B8 4F 44 52 45 4D 6A 55 30 4F 54 59 3D ODREMjU0OTY=
这是第五次变换得到的数据也就是最终数据。
所以主要就是这5个关键的变换函数,会发现第一次和第三次函数为同一个,第二次与第第五次函数为同一个,第四次的功能很明显就不需要分析了。
接着先分析函数00401208
函数进入以后,还是有很多功能调用,用IDA分析,最关键的是下面一个函数调用:
用IDA进入分析,有三个关键函数,40D1D0,40D880,40D310 其他函数有时间也可以分析下如果有时间的话,很多应该是迷惑我们的,这里可以设将输入的数据内存断点 分析也会跳到第一个函数里面,不过期间程序又将数据转存了的,所以内存断点的位置也应该变
第一个函数应该是将输入的数据以4个位单位倒叙 每次倒叙8个字节 得到如下,这也是函数2的一个参数
0018F278 34 33 32 31 38 37 36 35 43218765
函数2的第二个参数是18f2f0,从下面看函数二调用了三次,每次的第一个参数为18f278,第二个参数分别为18f2f0,18f370,18f3f0
0040D094 |. 50 |push eax ; 18f2f0 这三个函数最关键
0040D095 |. 8D4D F0 |lea ecx,[local.4]
0040D098 |. 51 |push ecx
0040D099 |. E8 E2070000 |call Key-enc.0040D880 ; 跟进
0040D09E |. 83C4 08 |add esp,0x8
0040D0A1 |. 8B55 08 |mov edx,[arg.1]
0040D0A4 |. 81C2 80000000 |add edx,0x80
0040D0AA |. 52 |push edx ; 18f370
0040D0AB |. 8D45 F0 |lea eax,[local.4]
0040D0AE |. 50 |push eax
0040D0AF |. E8 CC070000 |call Key-enc.0040D880 ; 同上
0040D0B4 |. 83C4 08 |add esp,0x8
0040D0B7 |. 8B4D 08 |mov ecx,[arg.1]
0040D0BA |. 81C1 00010000 |add ecx,0x100
0040D0C0 |. 51 |push ecx ; 18f3f0
0040D0C1 |. 8D55 F0 |lea edx,[local.4]
0040D0C4 |. 52 |push edx
0040D0C5 |. E8 B6070000 |call Key-enc.0040D880 ; 同上
这三个地方存的数据为(第二次调用本函数时这个地方的值会不同,不过都与输入数据无关):
0018F2F0 29 32 33 38 01 06 06 09 22 1C 19 34 21 18 32 03 )238."4!2
0018F300 30 20 39 39 1F 20 34 0D 0E 03 0D 31 14 33 06 2F 0 99 4..13/
0018F310 07 0C 1D 0A 21 14 33 2D 20 10 05 3B 07 28 2B 15 ..!3- ;(+
0018F320 16 31 0F 28 1C 22 19 3C 0F 0A 2E 07 09 11 13 15 1("<...
0018F330 19 1B 2D 0F 20 22 08 34 25 0E 26 02 0E 0C 1D 27 - "4%&.'
0018F340 12 01 35 07 12 0D 1D 12 11 33 32 11 21 12 29 36 5.32!)6
0018F350 29 2A 36 36 08 2C 24 3A 1C 04 3A 36 16 0D 0B 0A )*66,$::6. .
0018F360 12 27 18 1C 20 10 2E 1B 0B 00 3A 2C 34 17 2C 0B ' . .:,4,
0018F370 1F 15 00 28 20 10 26 1B 0D 23 1A 3A 0A 10 20 0B .( &.#:.
0018F380 14 3D 38 34 12 30 22 32 28 08 32 21 13 2E 25 0A =840"2(2!.%.
0018F390 27 2A 24 10 21 06 3C 12 12 07 30 06 24 23 0D 04 '*$!<0$#.
0018F3A0 18 04 07 0E 0F 0D 0C 22 0B 3E 22 07 08 18 0A 35 .." >".5
0018F3B0 09 30 24 2E 11 19 19 14 27 25 07 02 18 10 2B 24 .0$.'%+$
0018F3C0 2C 11 05 0B 36 24 08 3D 20 2A 09 13 23 25 17 25 , 6$= *.#%%
0018F3D0 16 0A 1D 21 29 13 10 29 12 10 08 3D 1C 2F 30 07 .!))=/0
0018F3E0 21 16 3B 28 0D 08 32 00 09 15 19 34 04 0F 0E 09 !;(.2..4.
0018F3F0 32 3D 12 3C 1C 0E 2C 0B 30 20 32 24 0D 3B 26 0B 2=<, 0 2$.;&
0018F400 1E 3A 39 3C 30 23 32 02 2C 0E 18 39 2F 14 24 0F :9<0#2,9/$
0018F410 22 15 1D 30 12 25 16 29 05 31 09 1B 3D 12 33 0D "0%)1.=3.
0018F420 2B 28 05 29 08 3B 2A 3D 1C 1C 0F 2A 37 09 13 24 +();*=*7.$
0018F430 04 2F 09 0F 0C 2D 13 35 1F 00 27 0B 26 07 08 35 /..-5.' &5
0018F440 22 3F 07 06 21 0A 1D 26 39 38 34 07 1B 24 19 14 "?!.&984$
0018F450 0C 07 26 11 1A 3B 2D 32 15 2D 32 26 20 15 05 3A .&;-2-2& :
0018F460 29 12 3A 16 26 0A 2A 12 07 14 2A 18 39 0C 2C 1A ):&.**9.,
这应该是前面所说的关键信息经过变换得到的,当时我没有分析,因为这与输入数据无关,现在就直接用吧。
用IDA分析函数2:
unsigned int __cdecl sub_40D880(int a1, int a2)
{
unsigned int v2; // ecx@1
unsigned int v3; // ecx@4
unsigned int result; // eax@4
//char v5; // [sp+Ch] [bp-54h]@1
int i; // [sp+4Ch] [bp-14h]@1
unsigned int v7; // [sp+50h] [bp-10h]@1
unsigned int v8; // [sp+54h] [bp-Ch]@1
unsigned int v9; // [sp+58h] [bp-8h]@1
int v10; // [sp+5Ch] [bp-4h]@3
int v11; // [sp+6Ch] [bp+Ch]@3
int dword_5F47B0[] = {0x80108020,0x80008000,0x00008000,0x00108020,
0x00100000,0x00000020,0x80100020,0x80008020,
0x80000020,0x80108020,0x80108000,0x80000000,
0x80008000,0x00100000,0x00000020,0x80100020,
0x00108000,0x00100020,0x80008020,0x00000000,
0x80000000,0x00008000,0x00108020,0x80100000,
0x00100020,0x80000020,0x00000000,0x00108000,
0x00008020,0x80108000,0x80100000,0x00008020,
0x00000000,0x00108020,0x80100020,0x00100000,
0x80008020,0x80100000,0x80108000,0x00008000,
0x80100000,0x80008000,0x00000020,0x80108020,
0x00108020,0x00000020,0x00008000,0x80000000,
0x00008020,0x80108000,0x00100000,0x80000020,
0x00100020,0x80008020,0x80000020,0x00100020,
0x00108000,0x00000000,0x80008000,0x00008020,
0x80000000,0x80100020,0x80108020,0x00108000};
int dword_5F49B0[] = {0x00802001,0x00002081,0x00002081,0x00000080,
0x00802080,0x00800081,0x00800001,0x00002001,
0x00000000,0x00802000,0x00802000,0x00802081,
0x00000081,0x00000000,0x00800080,0x00800001,
0x00000001,0x00002000,0x00800000,0x00802001,
0x00000080,0x00800000,0x00002001,0x00002080,
0x00800081,0x00000001,0x00002080,0x00800080,
0x00002000,0x00802080,0x00802081,0x00000081,
0x00800080,0x00800001,0x00802000,0x00802081,
0x00000081,0x00000000,0x00000000,0x00802000,
0x00002080,0x00800080,0x00800081,0x00000001,
0x00802001,0x00002081,0x00002081,0x00000080,
0x00802081,0x00000081,0x00000001,0x00002000,
0x00800001,0x00002001,0x00802080,0x00800081,
0x00002001,0x00002080,0x00800000,0x00802001,
0x00000080,0x00800000,0x00002000,0x00802080};
int dword_5F4BB0[] = {0x20000010,0x20400000,0x00004000,0x20404010
,0x20400000,0x00000010,0x20404010,0x00400000
,0x20004000,0x00404010,0x00400000,0x20000010
,0x00400010,0x20004000,0x20000000,0x00004010
,0x00000000,0x00400010,0x20004010,0x00004000
,0x00404000,0x20004010,0x00000010,0x20400010
,0x20400010,0x00000000,0x00404010,0x20404000
,0x00004010,0x00404000,0x20404000,0x20000000
,0x20004000,0x00000010,0x20400010,0x00404000
,0x20404010,0x00400000,0x00004010,0x20000010
,0x00400000,0x20004000,0x20000000,0x00004010
,0x20000010,0x20404010,0x00404000,0x20400000
,0x00404010,0x20404000,0x00000000,0x20400010
,0x00000010,0x00004000,0x20400000,0x00404010
,0x00004000,0x00400010,0x20004010,0x00000000
,0x20404000,0x20000000,0x00400010,0x20004010};
int dword_5F4DB0[] = {0x10001040,0x00001000,0x00040000,0x10041040
,0x10000000,0x10001040,0x00000040,0x10000000
,0x00040040,0x10040000,0x10041040,0x00041000
,0x10041000,0x00041040,0x00001000,0x00000040
,0x10040000,0x10000040,0x10001000,0x00001040
,0x00041000,0x00040040,0x10040040,0x10041000
,0x00001040,0x00000000,0x00000000,0x10040040
,0x10000040,0x10001000,0x00041040,0x00040000
,0x00041040,0x00040000,0x10041000,0x00001000
,0x00000040,0x10040040,0x00001000,0x00041040
,0x10001000,0x00000040,0x10000040,0x10040000
,0x10040040,0x10000000,0x00040000,0x10001040
,0x00000000,0x10041040,0x00040040,0x10000040
,0x10040000,0x10001000,0x10001040,0x00000000
,0x10041040,0x00041000,0x00041000,0x00001040
,0x00001040,0x00040040,0x10000000,0x10041000};
int dword_5F46B0[] = {0x01010400,0x00000000,0x00010000,0x01010404,
0x01010004,0x00010404,0x00000004,0x00010000,
0x00000400,0x01010400,0x01010404,0x00000400,
0x01000404,0x01010004,0x01000000,0x00000004,
0x00000404,0x01000400,0x01000400,0x00010400,
0x00010400,0x01010000,0x01010000,0x01000404,
0x00010004,0x01000004,0x01000004,0x00010004,
0x00000000,0x00000404,0x00010404,0x01000000,
0x00010000,0x01010404,0x00000004,0x01010000,
0x01010400,0x01000000,0x01000000,0x00000400,
0x01010004,0x00010000,0x00010400,0x01000004,
0x00000400,0x00000004,0x01000404,0x00010404,
0x01010404,0x00010004,0x01010000,0x01000404,
0x01000004,0x00000404,0x00010404,0x01010400,
0x00000404,0x01000400,0x01000400,0x00000000,
0x00010004,0x00010400,0x00000000,0x01010004};
int dword_5F48B0[] = {0x00000208,0x08020200,0x00000000,0x08020008,
0x08000200,0x00000000,0x00020208,0x08000200,
0x00020008,0x08000008,0x08000008,0x00020000,
0x08020208,0x00020008,0x08020000,0x00000208,
0x08000000,0x00000008,0x08020200,0x00000200,
0x00020200,0x08020000,0x08020008,0x00020208,
0x08000208,0x00020200,0x00020000,0x08000208,
0x00000008,0x08020208,0x00000200,0x08000000,
0x08020200,0x08000000,0x00020008,0x00000208,
0x00020000,0x08020200,0x08000200,0x00000000,
0x00000200,0x00020008,0x08020208,0x08000200,
0x08000008,0x00000200,0x00000000,0x08020008,
0x08000208,0x00020000,0x08000000,0x08020208,
0x00000008,0x00020208,0x00020200,0x08000008,
0x08020000,0x08000208,0x00000208,0x08020000,
0x00020208,0x00000008,0x08020008,0x00020200};
int dword_5F4AB0[] = {0x00000100,0x02080100,0x02080000,0x42000100,
0x00080000,0x00000100,0x40000000,0x02080000,
0x40080100,0x00080000,0x02000100,0x40080100,
0x42000100,0x42080000,0x00080100,0x40000000,
0x02000000,0x40080000,0x40080000,0x00000000,
0x40000100,0x42080100,0x42080100,0x02000100,
0x42080000,0x40000100,0x00000000,0x42000000,
0x02080100,0x02000000,0x42000000,0x00080100,
0x00080000,0x42000100,0x00000100,0x02000000,
0x40000000,0x02080000,0x42000100,0x40080100,
0x02000100,0x40000000,0x42080000,0x02080100,
0x40080100,0x00000100,0x02000000,0x42080000,
0x42080100,0x00080100,0x42000000,0x42080100,
0x02080000,0x00000000,0x40080000,0x42000000,
0x00080100,0x02000100,0x40000100,0x00080000,
0x00000000,0x40080000,0x02080100,0x40000100};
int dword_5F4CB0[] = {0x00200000,0x04200002,0x04000802,0x00000000,
0x00000800,0x04000802,0x00200802,0x04200800,
0x04200802,0x00200000,0x00000000,0x04000002,
0x00000002,0x04000000,0x04200002,0x00000802,
0x04000800,0x00200802,0x00200002,0x04000800,
0x04000002,0x04200000,0x04200800,0x00200002,
0x04200000,0x00000800,0x00000802,0x04200802,
0x00200800,0x00000002,0x04000000,0x00200800,
0x04000000,0x00200800,0x00200000,0x04000802,
0x04000802,0x04200002,0x04200002,0x00000002,
0x00200002,0x04000000,0x04000800,0x00200000,
0x04200800,0x00000802,0x00200802,0x04200800,
0x00000802,0x04000002,0x04200802,0x04200000,
0x00200800,0x00000000,0x00000002,0x04200802,
0x00000000,0x00200802,0x04200000,0x00000800,
0x04000002,0x04000800,0x00000800,0x00200002};
//memset(&v5, -858993460, 0x54u);
v7 = *(DWORD *)a1;
v8 = *(DWORD *)(a1 + 4);
v9 = (v8 ^ (v7 >> 4)) & 0xF0F0F0F;
v8 ^= v9;
v7 ^= 16 * v9;
v9 = (unsigned __int16)(v8 ^ HIWORD(v7));
v8 ^= v9;
v7 ^= v9 << 16;
v9 = (v7 ^ (v8 >> 2)) & 0x33333333;
v7 ^= v9;
v8 ^= 4 * v9;
v2 = v7 ^ (v8 >> 8);
v7 ^= v2 & 0xFF00FF;
v8 ^= (v2 & 0xFF00FF) << 8;
v8 = (v8 >> 31) & 1 | 2 * v8;
v9 = (v8 ^ v7) & 0xAAAAAAAA;
v7 ^= v9;
v8 ^= v9;
v7 = (v7 >> 31) & 1 | 2 * v7;
for ( i = 0; i < 8; ++i )
{
v9 = (v8 >> 4) | (v8 << 28);
v9 = *(DWORD *)a2 ^ ((v8 >> 4) | (v8 << 28));
v11 = a2 + 4;
v10 = dword_5F4CB0[v9 & 0x3F];
v10 |= dword_5F4AB0[(v9 >> 8) & 0x3F];
v10 |= dword_5F48B0[(v9 >> 16) & 0x3F];
v10 |= dword_5F46B0[(v9 >> 24) & 0x3F];
v9 = *(DWORD *)v11 ^ v8;
v11 += 4;
v10 |= dword_5F4DB0[v9 & 0x3F];
v10 |= dword_5F4BB0[(v9 >> 8) & 0x3F];
v10 |= dword_5F49B0[(v9 >> 16) & 0x3F];
v10 |= dword_5F47B0[(v9 >> 24) & 0x3F];
v7 ^= v10;
v9 = (v7 >> 4) | (v7 << 28);
v9 = *(DWORD *)v11 ^ ((v7 >> 4) | (v7 << 28));
v11 += 4;
v10 = dword_5F4CB0[v9 & 0x3F];
v10 |= dword_5F4AB0[(v9 >> 8) & 0x3F];
v10 |= dword_5F48B0[(v9 >> 16) & 0x3F];
v10 |= dword_5F46B0[(v9 >> 24) & 0x3F];
v9 = *(DWORD *)v11 ^ v7;
a2 = v11 + 4;
v10 |= dword_5F4DB0[v9 & 0x3F];
v10 |= dword_5F4BB0[(v9 >> 8) & 0x3F];
v10 |= dword_5F49B0[(v9 >> 16) & 0x3F];
v10 |= dword_5F47B0[(v9 >> 24) & 0x3F];
v8 ^= v10;
}
v8 = (v8 >> 1) | (v8 << 31);
v9 = (v8 ^ v7) & 0xAAAAAAAA;
v7 ^= v9;
v8 ^= v9;
v7 = (v7 >> 1) | (v7 << 31);
v3 = v8 ^ (v7 >> 8);
v8 ^= v3 & 0xFF00FF;
v7 ^= (v3 & 0xFF00FF) << 8;
v9 = (v8 ^ (v7 >> 2)) & 0x33333333;
v8 ^= v9;
v7 ^= 4 * v9;
v9 = (unsigned __int16)(v7 ^ HIWORD(v8));
v7 ^= v9;
v8 ^= v9 << 16;
v9 = (v7 ^ (v8 >> 4)) & 0xF0F0F0F;
v7 ^= v9;
v8 ^= 16 * v9;
*(DWORD *)a1 = v8;
result = v7;
*(DWORD *)(a1 + 4) = v7;
return result;
}
经过三次函数2的变换后,函数3即将18f278处的数据再以4个字节为单位,倒叙复制到别处。
若参数1的长度大于8,会循环再次调用后两个函数,并多加一步如下(后面会给出具体代码):
(*(DWORD*)str12)^=(*(DWORD*)(str11+8));
然后分析函数00401032
里面还是有很多迷惑的函数,关键地方为:
00405533 . 50 push eax
00405534 . 8D8D ECFEFFFF lea ecx,dword ptr ss:[ebp-0x114]
0040553A . E8 79BCFFFF call Key-enc.004011B8 ; 关键
进去分析,得到的算法如下,str为传入的字符串,第二次关键函数是92 A8 01 86 FB BA 80 4B
第五次关键函数是30 38 46 32 44 39 44 30 34 45 35 36 33 36 37 36 44 45 44 34 42 41 41 42 38 34 44 32 35 34 39 36
void Fun2(int str,int size)
{
char buff[] = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"};
char buff2[50];
int sum = size/3;
int yu = size%3;
int i;
for (i = 0;i<sum;i++)
{
buff2[4*i] = buff[(UCHAR)((char *)str)[3*i]>>2];//第一个
buff2[1+4*i] = buff[((((char *)str)[1+3*i] & 0xF0) >> 4) | 16 * (((char *)str)[3*i] & 3)];
buff2[2+4*i] = buff[((((char *)str)[2+3*i] & 0xC0) >> 6) | 4 * (((char *)str)[1+3*i] & 0xF)];
buff2[4*i+3] = buff[((char *)str)[2+3*i] & 0x3F];
}
if (yu == 1)
{
buff2[4*i] = buff[(UCHAR)(((char *)str)[3*i])>>2];
buff2[4*i+1] = buff[16 * (((char *)str)[3*i] & 3)];
buff2[4*i+2] = '=';
buff2[4*i+3] = '=';
}
else
if (yu == 2)
{
buff2[4*i] = buff[(unsigned char)(((char *)str)[3*i])>>2];//第一个
buff2[1+4*i] = buff[((((char *)str)[1+3*i] & 0xF0) >> 4) | 16 * (((char *)str)[3*i] & 3)];
buff2[4*i+2] = buff[4 * (((char *)str)[1+3*i] & 0xF)];
buff2[4*i+3] = '=';
}
for (int j = 0;j<4*(sum+1);j++)
{
BUFF2[j] = buff2[j];
}
return;
}
在第五次关键函数完成后,即可出现密文MDhGMkQ5RDA0RTU2MzY3NkRFRDRCQUFCODREMjU0OTY=
具体的算法应该就是这些,根据这些流程,现在已知密文:
NDE4OUM3MUVBNUZFOTFBM0NCRUI5OTcyMDUzN0VBNEIzMjIxRUIxNDhCOTU3Mzcw=
经过第五个关键函数和第四个关键函数,得到数据为41 88 C7 0E A4 FE 81 A0 CB DB 98 72 05 34 EA 4B 30 21 DB 14 8B 85 70 70
char buff[] = "NDE4OUM3MUVBNUZFOTFBM0NCRUI5OTcyMDUzN0VBNEIzMjIxRUIxNDhCOTU3Mzcw=";
char buff2[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
char buff3[49] = {0};
int len = strlen(buff);
int num = len/4;
UCHAR j,data1,data2;
for (int i=0;i<16;i++)
{
for (j = 0;j<64;j++)
{
if (buff2[j] == buff[4*i])
break;
}
buff3[3*i] = 4*j;
for (j = 0;j<64;j++)
{
if (buff2[j] == buff[4*i+1])
break;
}
data1 = 16*j;
data1&=0xf0;
for (j = 0;j<64;j++)
{
if (buff2[j] == buff[4*i+2])
break;
}
data2 = j/4;
buff3[3*i+1] = data1|data2;
for (j = 0;j<64;j++)
{
if (buff2[j] == buff[4*i+3])
break;
}
if (j<30)
{
buff3[3*i+2] = j|0x40;
}
else
{
buff3[3*i+2] = j;
}
}
printf("%s\n",buff3);
}
//41 88 C7 0E A4 FE 81 A0 CB DB 98 72 05 34 EA 4B 30 21 DB 14 8B 85 70 70
对于第三个关键调用即
unsigned int __cdecl sub_40D880(int a1, int a2)反向推的时候还是比较复杂,不知道有没有简单的方法。整个程序的关键流程基本都比较清楚了。
即:
代码1(00401208 ) 输入数据 自身关键数据1
代码2(00401032) 代码1得到的数据
代码3(00401208 ) 代码2得到的数据 自身关键数据2
代码4(00401005) 一个简单的变换
代码5(00401032) 代码4得到的数据
有兴趣的可以继续跟着逆向下,哎,每天白天忙着上课,到晚上才能看看,不能连续性的做,纠结啊……
Key-enc.zip
Key-enc.txt
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)