首页
社区
课程
招聘
[分享]ECC替换公钥实践(下篇)
发表于: 2014-9-14 08:36 29368

[分享]ECC替换公钥实践(下篇)

2014-9-14 08:36
29368
上篇总结:
上篇链接:http://bbs.pediy.com/showthread.php?t=192180

(a) 在lm_code.h中,选定LM_SEED1.2,3, private key、public key也就固定了;

(b) 可以用lmnewgen输出lmprikey.h和lmpubkey.h查看private key和public key,高版本的SDK,缺省只输出lmprikey.h,加上-pubkey选项,可以输出lmpubkey.h;

(c) lmpubkey.h中存放的是转换过的public key,转换规律是用lmpubkey.h中的key和daemon( 比如本例中的testlmd)的ASCII代码按位做加、减或异或操作得到真正的公钥,具体如下:
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 - (第0、2、4......26、28、30位用减法运算,比如第0位76-74=02h)
3 9 15 21 27 ^ (第3、9、15、21、27位用异或运算,例如第3位AB^74=DFh)
1 5 7 11 13 17 19 23 25 29 + (第1、5、7......23、25、29位用加法运算,例如第1位9D+65=02,注意取末两位)
0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  <= 0~30(共31 = (1F)h位
76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17  <= pubkey in pubkey.h
74 65 73 74 6C 6D 64 74 65 73 74 6C 6D 64 74 65 73 74 6C 6D 64 74 65 73 74 6C 6D 64 74 65 73  <= testlmd
02 02 6F DF CF AF 73 DF BB C6 D2 A5 80 20 2C B4 58 7F F1 D2 78 91 D4 D6 D0 EC 60 1C B3 F1 A4  <= real pubkey(for SIGN2=)

替换公钥,就是找出lmpubkey.h中的pubkey在硬盘上是如何存储的,找到存储的位置,相应地替换成自己的lmpubkey.h中的public key就可以了。

好,我们开始下篇。

上篇我么在内存[00C93B3C~00C93B5E]处下写断点追踪出

76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17 (lmpubkey.h中的 public key)
=>
02 02 6F DF CF AF 73 DF BB C6 D2 A5 80 20 2C B4 58 7F F1 D2 78 91 D4 D6 D0 EC 60 1C B3 F1 A4 (真实的public key)

现在我么再回头看看这段内存里的public key是怎么来的
76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17

和上篇一样,在run过0041D143后,这块内存区域清零
0041D143  |.  E8 E82A0100   CALL testlmd.l_malloc                    ; \l_malloc

在此处下内存写断点,如下图所示,



按一次F9到停在0047F195,

0047F195  |.  F3:AB         REP STOS DWORD PTR ES:[EDI]

F8跳过,再按一次F9来到0047BEF4

0047BEF4   .  89448F E4     MOV DWORD PTR DS:[EDI+ECX*4-1C],EAX

看下信息窗口里的EAX,此处就是开始往[00C93B3C~00C93B5E]里写“76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17”了

EAX=ABE29D76
DS:[00C93B40]=00000000
MEMCPY.ASM:283. 



往上一步,看看EAX的值是怎么来的

0047BEF0   > \8B448E E4     MOV EAX,DWORD PTR DS:[ESI+ECX*4-1C]

EAX由[ESI+ECX*4-1C] = [00a66744]赋值而来



好了,再往回trace,看看内存[00a66744-00a66762]的赋值是怎么来的,可以看到在_l_public_key_verify的入口处,这段内存的值已经存在了,那就再看看_l_public_key_verify的调用函数的入口处。

* 这里注意下,IDA里看不到哪里调用了_l_public_key_verify,可以在OD里运行到_l_public_key_verify的入口处0041CDB0,然后按“调试”->“执行到返回”(或者按快捷键CTRL+F9),找到_l_public_key_verify调用函数。

这么一路追踪下去(得有耐心,比较耗时间,但没什么难度,如果看到这段值从某处内存拷贝过来,就再去看看那段内存值是怎么来的),最终会找到这里

004A286A  |.  FF15 F47E5200 CALL DWORD PTR DS:[l_n36_buf]            ; \testlmd.00401E60

run过004A286A后,内存[0012F184 - 0012F1A2]里的值为
76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17



我么再往回,在004A286A处按F7进去看看

run过0040326B清零

0040326B  |.  E8 E0BE0700   CALL testlmd.memset                      ; \memset

还是老办法,在内存[0012F184 - 0012F1A2]下写断点,



然后一路F9,你会看到下面的数字一个个的出现(但是不是按顺序的,第一个出现的是在.text:403C0A,往内存0012F196处写5D,最后一个出现的是.text:00412AAE,往内存0012F190处写ED)。
76 9D E2 AB 3B 42 D7 6B 20 B5 46 39 ED BC A0 D1 CB 0B 5D 65 DC E5 39 63 44 80 CD 78 27 8C 17

好,看看第一个出现的5D怎么来的。
00403C0A  |.  0305 30775200 ADD EAX,DWORD PTR DS:[527730]
00403C10  |.  8B4D 0C       MOV ECX,DWORD PTR SS:[EBP+C]
00403C13  |.  8881 AA000000 MOV BYTE PTR DS:[ECX+AA],AL



呵呵,接近成功了,看到这个固定的数了吧,[527730],里面放的是5D,回到程序还没开始运行时,看看,这个值是不是已经在那了,说明什么,这个值是直接存在硬盘上的值了。其余的也可以这么一个一个找到。

呵呵,终于找到pubkey存放的源头了,替换公钥就是把这些值一个一个的替换成自己定义的pubkey就行了。

休息一下,往下再讲除了手工替换,怎么用程序来自动替换。

[课程]Android-CTF解题方法汇总!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (44)
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
2
我们接下去看怎么手工替换公钥,又怎样用程序实现自动替换。

(a)FlexLM SDK v9.2 ECC公钥替换

再回头看看v9.2版本SDK编译产生的testlmd.exe里第一个出现的5D怎么来的。
00403C0A  |.  0305 30775200 ADD EAX,DWORD PTR DS:[527730]
00403C10  |.  8B4D 0C       MOV ECX,DWORD PTR SS:[EBP+C]
00403C13  |.  8881 AA000000 MOV BYTE PTR DS:[ECX+AA],AL

第一个pubkey的值出现的是在.text:403C0A,然后在.text:00403C13往内存0012F196处写5D

插入图

看下信息窗口里的内容
AL=5D (']')
堆栈 DS:[0012F196]=00

ECX的值是0012F0EC,

所以内存[0012F184 - 0012F1A2]分别对应
[ECX + 98h] => 十进制 [ecx + 152]
......
[ECX + B6h] => 十进制 [ecx + 182]
请注意理解这里的(152 ~ 182),后面的不同的SDK版本的ECC pubkey替换仍然要用到这个方法。

我们把sub_401E60 (即_l_n36_buf)F5转伪代码截取memset开始到末尾的一段(见附件ida_f5.txt)
从中很容易(可以用编程脚本提取)出pubkey存储的位置。

                        *((_BYTE *)Dst + 152) += dword_526E30;  =>        526E30存放76
                        *((_BYTE *)Dst + 153) += dword_527D1C;  =>        527D1C存放9d
                        *((_BYTE *)Dst + 154) += dword_527634;  =>        527634存放e2
                        *((_BYTE *)Dst + 155) += dword_5275C8;  =>        5275C8存放ab
                        *((_BYTE *)Dst + 156) += dword_527EA4;  =>        527EA4存放3b
                        *((_BYTE *)Dst + 157) += dword_527824;  =>        527824存放42
                        *((_BYTE *)Dst + 158) += dword_527344;  =>        527344存放d7
                        *((_BYTE *)Dst + 159) += dword_527AD4;  =>        527AD4存放6b
                        *((_BYTE *)Dst + 160) += dword_526E7C;  =>        526E7C存放20
                        *((_BYTE *)Dst + 161) += dword_5277B8;  =>        5277B8存放b5
                        *((_BYTE *)Dst + 162) += dword_527864;  =>        527864存放46
                        *((_BYTE *)Dst + 163) += dword_527314;  =>        527314存放39
                        *((_BYTE *)Dst + 164) += dword_527624;  =>        527624存放ed
                        *((_BYTE *)Dst + 165) += dword_527DF4;  =>        527DF4存放bc
                        *((_BYTE *)Dst + 166) += dword_52707C;  =>        52707C存放a0
                        *((_BYTE *)Dst + 167) += dword_527A8C;  =>        527A8C存放d1
                        *((_BYTE *)Dst + 168) += dword_5277C4;  =>        5277C4存放cb
                        *((_BYTE *)Dst + 169) += dword_526E1C;  =>        526E1C存放0b
                        *((_BYTE *)Dst + 170) += dword_527730;  =>        527730存放5d
                        *((_BYTE *)Dst + 171) += dword_5274B0;  =>        5274B0存放65
                        *((_BYTE *)Dst + 172) += dword_527180;  =>        527180存放dc
                        *((_BYTE *)Dst + 173) += dword_5278E4;  =>        5278E4存放e5
                        *((_BYTE *)Dst + 174) += dword_527178;  =>        527178存放39
                        *((_BYTE *)Dst + 175) += dword_527540;  =>        527540存放63
                        *((_BYTE *)Dst + 176) += dword_527AC8;  =>        527AC8存放44
                        *((_BYTE *)Dst + 177) += dword_52766C;  =>        52766C存放80
                        *((_BYTE *)Dst + 178) += dword_527A54;  =>        527A54存放cd
                        *((_BYTE *)Dst + 179) += dword_527070;  =>        527070存放78
                        *((_BYTE *)Dst + 180) += dword_527140;  =>        527140存放27
                        *((_BYTE *)Dst + 181) += dword_5278F4;  =>        5278F4存放8c
                        *((_BYTE *)Dst + 182) += dword_5270F0;  =>        5270F0存放17

请注意上面的(152,153......182)的排序,IDA F5的输出不是这样的,这里排好序是为了后面脚本处理时,原pubkey和要替换的pubkey比较容易一一对应上。
但是替换时,必须要按地址存放顺序搜索、替换,呵呵这个稍微想一下,应该不难理解。

这样排序后,原来的pubkey和将要替换的pubkey就很容易一一对应上了
org_pubkey = '76 9d e2 ab 3b 42 d7 6b 20 b5 46 39 ed bc a0 d1 cb 0b 5d 65 dc e5 39 63 44 80 cd 78 27 8c 17'
rep_pubkey = '76 e5 b4 61 32 35 26 f4 6d 28 d8 28 c4 8d 4c 73 bb dc fe 5f 0f 90 ad 24 97 83 0b 0c 47 e5 ff'

手工替换,就找到相应的地址,手工输入替换就可以了。

至于自动替换,这里是用ruby脚本实现的。

在编程脚本里很容易再按地址排序,并将org_pubkey和rep_pubkey随之相应按地址一一对应如下

526E1C  0b      dc
526E30  76      76
526E7C  20      6d
527070  78      0c
52707C  a0      4c
5270F0  17      ff
527140  27      47
527178  39      ad
527180  dc      0f
527314  39      28
527344  d7      26
5274B0  65      5f
527540  63      24
5275C8  ab      61
527624  ed      c4
527634  e2      b4
52766C  80      83
527730  5d      fe
5277B8  b5      28
5277C4  cb      bb
527824  42      35
527864  46      d8
5278E4  e5      90
5278F4  8c      e5
527A54  cd      0b
527A8C  d1      73
527AC8  44      97
527AD4  6b      f4
527D1C  9d      e5
527DF4  bc      8d
527EA4  3b      32

这时就可以搜索、替换了,整个的搜索、替换的规律如下

0b......76......20...... ......9d......bc......3b
=>
dc......76......6d...... ......e5......8d......32

由于数据不是连续存放的,中间“......”对应的数据必须原封不动的保留。

你可以将脚本里得到的total_search输出
total_search = total_search(address, save)

0b(.{38})76(.{150})20(.{998})78(.{22})a0(.{230})17(.{158})27(.{110})39(.{14})dc(.{806})39(.{94})d7(.{726})65(.{286})63(.{270})ab(.{182})ed(.{30})e2(.{110})80(.{390})5d(.{270})b5(.{22})cb(.{190})42(.{126})46(.{254})e5(.{30})8c(.{702})cd(.{110})d1(.{118})44(.{22})6b(.{1166})9d(.{430})bc(.{350})3b

(.{38}) => 0b和76间有38个其他数字,这里38是程序根据0b和76的存放地址的差(526E30 - 526E1C)自动计算出来的

作为参考,公钥替换脚本见附件testlmd_replace_pubkey_v9.2.rb,是一个ruby脚本,可以用testlmd_replace_pubkey_v9.2.rb ida_f5.txt testlmd.exe替换testlmd.exe里的公钥,输出文件为outfile。

声明:这里的脚本只是实现了功能,性能上未作优化(修改大的应用文件会很慢),仅供参考。从实用的角度而言,优化下这个脚本是可行的,如果懒得优化的话,可以从这个脚本输出seatch/replace文本,然后用sfk(Swiss File Knife)全目录自动替换。

稍后看下11.9.1和11.11.1.1的ECC公钥替换
......
上传的附件:
2014-9-14 08:37
0
雪    币: 44229
活跃值: (19965)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
编辑了一下帖子,加上了上篇的链接。
上下2篇只设一个精华,上篇设精华,下篇设“优秀”
2014-9-14 08:48
0
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
4
谢谢老大鼓励:)
2014-9-14 09:28
0
雪    币: 150
活跃值: (74)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
占楼备用
2014-9-14 10:05
0
雪    币: 8865
活跃值: (2379)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
6
good job~
ECC替换公钥,学习了~
2014-9-14 17:13
0
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
7
2楼更新: 用脚本自动替换公钥
2014-9-15 17:58
0
雪    币: 0
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
有心人,很详细,非常不错,不过无法对其他软件通用,我也写了个通用公钥替换程序(自动生成公钥,提取公钥,再替换公钥),能适用于大部分软件,但不同编译器编译生成汇编代码都有差异,vc6与vc2010都不一样,tanker那个不知道是否更牛?
2014-9-16 13:04
0
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
9
嗯,这里主要是分享下分析的过程,修改公钥最好还是自己完全了解,否则改错了而自己都不知道就麻烦了,而且这个东西随着SDK版本升级可能一直会变,依靠别人的程序会比较麻烦:)
2014-9-16 13:47
0
雪    币: 0
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
目前windows版软件,基本都可以自动查找,提取,替换,由于替换的是数据区,不存在改程序问题,整个过程自动完成,基本秒杀了,…手工找1个小时都跟不下来,你再往前走一步就可以通用自动替换了。
2014-9-16 15:13
0
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
11
嗯,只要自己知道改了什么就好~

到11.11.1.1,就转到.text,不在.data区了~

> 手工找1个小时都跟不下来
这个不用这么久,熟悉了,半小时就差不多了,当然不包括写程序的时间:)

呵呵,再强调下,我这里只是分享分析的经验,所写的脚本也只是供参考,不推荐大家直接拿去用。当然理解了方法,修改下自己用也是可以的。
2014-9-16 16:18
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
girlmore能不能分享一下你的工具啊,让我们这些菜鸟也学习一下
2014-9-27 07:51
0
雪    币: 15
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
期待。。。。。。。。。。。。。。。。

稍后看下11.9.1和11.11.1.1的ECC公钥替换
2014-9-27 11:04
0
雪    币: 332
活跃值: (479)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
14
学习了!!!!
2014-9-28 14:20
0
雪    币: 484
活跃值: (269)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
15
前辈客气了,学习您的帖子收获很多:)
2014-9-29 10:28
0
雪    币: 0
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
你用tanker的工具即可,我只是自己重新研究写了个通用工具而已,纯属研究自动提取,生成并替换公钥匙
2014-9-30 23:27
0
雪    币: 57
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
bridgeic, good job..!!!
And about the version v11.11 and v11.12, you're right.. Flexera has changed the obfuscation schema that's hide the private and the public key. So now it's very hard to inject your pubkey...
The PubKey Replacer v1.70 work only till the v11.10. But in some vendors the tool cannot change properly the pubkey, so you need to change manually...

Could be good to have a tool to change the pubkey in the lmcrypt.exe instead  the vendor/exe/dll target.. You can easly find the pubkey in the lmcrypt by building your test with different lmseeds...
If you can make a video or write a pdf in english will be very usefull...
2014-10-4 03:58
0
雪    币: 5784
活跃值: (6432)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
18
ECC替换公钥实践.doc学习中,在这里见到了很多传说中的大神!  谢谢!

经实践,对v9.x,v10.x好用。非常期待替换v11.x的方法。附件是本人学习本帖过程中的心得备注,供初学者参考。以此感谢楼主的无私奉献!

====================================================
查看0041D5C4处,edx里对应的就是public key  (Myron备注:这个地址可以根据规律找到:用W32Dasm找到第一个FFFFF8FFFFFF后,往回找到第二个call,再往回找到第二个push的位置就是,注意它的上面是“add e%x 00000044“)
===================================================
上传的附件:
2014-10-22 10:05
0
雪    币: 5784
活跃值: (6432)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
19
ECC替换公钥实践.doc注意附件在楼下,新版附件
上传的附件:
2014-11-1 20:31
0
雪    币: 5784
活跃值: (6432)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
20
重新修改了确定追踪内存段的原理和通用方法,适合于所有版本。请下载过的朋友注意更新

=====================================================
(Myron备注:这里0012F184怎么来的?我们由下图可以看到EAX由[ESI+ECX*4-1C] = [00A66744]赋值而来,而[00A66744]通过追踪是由[00A54848] 赋值而来,继续追踪[00A54848],直到找到[0012F184]是从硬盘数据读来的。最终的赋值关系如下:
EDI+ECX*4-1C=[00C93B3C] <== ESI+ECX*4-1C=[00A66744] <== [00A54848]  <== [00A544F8] <== [0012F184] <== [直接读硬盘数据]


=====================================================
上传的附件:
2014-11-2 19:03
0
雪    币: 2
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
精彩,希望楼主继续更新11.x的分析
2014-11-3 00:13
0
雪    币: 211
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
Thanks

Good job
2014-12-5 15:07
0
雪    币: 321
活跃值: (121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
非常不错,期待11.11
2014-12-8 13:34
0
雪    币: 1
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
I hope anyone can help me a working lmcrypt and public key for 11.12 target

http://filecloud.io/download.html
2014-12-22 18:24
0
雪    币: 318
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
收藏学习了,谢谢楼主
2015-2-28 00:38
0
游客
登录 | 注册 方可回帖
返回
//