转发一篇文章给你
文章不是我的,是heXer/iPB的个人研究成果,写得不错!
==========================================================================================
“超星读书卡”持卡用户可以通过Internet下载超星数字图书馆中的数字图书。
令很多用户困扰的是在一台机器上下载的文件,在另一台机器上无法直接阅读。
虽然可以通过获取离线注册码的方式解决,但总感觉不尽方便,能否妥善解决?
也许我们注意到在线阅读时是不分那台机器的,可以想象其PDG文件在从网上传
到机器内时是不具备机器识别特征的,这些文件是下载到本地时在本地加密后存
盘的。
因此我们有可能在分析出其本地加密算法,然后推演出其逆算法,将其还原
成原始文件,那么就可以实现自由的离线阅读了。
经过算法分析,证明此方法是可行的,并做出这个demo版的小工具:
1. 此工具暂没有批量转换功能,一次只能转换一个选定文件
2. 我只做了不全面的简单测试,不一定适用于所有的PDG文件
3. 我用的是SSReader3.6及以前版下载的PDG文件,通过测试
4.
现在SSReader3.7的加密方式已经改变,肯定不支持,有待以后分析
==========================================================================================
以下论述是我跟踪分析的大致结果,表述不是很精确,有些是猜测
所有分析是基于3.6版的,新版的格式可能会增加内容或有所改变,
我的分析是针对于两种主要类型(格式)的,其实也是目前绝大部分
图书所采用的类型,一种是我们在线阅览的那种pdgtype=02h
另一种是下载存储到本地硬盘文件的那种pdgtype=10h
因此以下描述的结构只能保证对这两种文件有效,其它类型的还需
进一步的分析验证.
==========================================================================================
1. PDG文件的大致结构:
PDG2_file STRUCT
HH_header<>
optional header<>
PDG_data<>
PDG2_file ENDS
HH_header STRUCT
dw 4848h ;超星文件特征标志
db 02h ;PDG_VERSION=2
db 00h
;我见到的PDG2这里都是0,就算默认吧
dd ? ;不知道,对我们似乎没有价值
dd offset_optional_header
;optional_header在文件中的偏移地址,应该等于0Ch吧
HH_header ENDS
optional_header STRUCT
db 80h,00h,00h ;没什么好说的,算是标志吧
db pdgtype
;文档类型,我只分析02h和10h两种类型
dw x_pix ;扫描图像的横向参数
dw y_pix ;扫描图像的纵向参数
db 01h,00h,00h,00h ;按默认算吧
dd offset_PDG_data
;扫描图像数据在文件中的偏移地址,大概都是8Ch吧
dd size_PDG_data ;扫描图像数据的字节数
dd 8 dup (?)
;作用可能不大,把它们添上0不影响页面的显示
key_data<>
db 1Ch dup (?)
;作用可能不大,把它们添上0不影响页面的显示
optional_header ENDS
key_data STRUCT
db 1Eh dup (?)
;这里有时是一个有关超星公司字符信息,有时为空(全0)
dw ? ;我不清楚什么含义
db 8 dup (?) ;可能有几处默认是0
dd SS_user_key
;重要的数据,可用于解码还原x_pix和y_pix
dd ? ;
key_data ENDS
==========================================================================================
2.
我所想象的图书阅览管理过程(基于简单的分析,未必准确,仅供参考)
超星图书服务器上所存储的图书是扫描生成的(废话,大家都知道),是按pdgtype=02h存储的,
文件结构与上述结构相符,单其扫描数据PDG_data是经过加密的,加密过程如下:
30h字节长的key_data经过md5运算得到128位即16字节的数据,此16字节的数据作为密钥,采用
一种分组加密算法以16字节为单位进行加密运算,直到数据结束,此分组加密算法我根据手头的不多
的密码学算法比较,我没有识别出来,我暂且称之为encode_sub,我将它放在后面了,大家有兴趣的可
以帮忙看一下,有知道可以的告诉我,万分感谢.
当我们在线阅览时,ssreader.exe中的相对应decode_sub将数据解码后,就可以显示出来了.
在下载到本地硬盘时,文件存储成pdgtype=10h类型的了,PDG_data扫描数据是用解码后的原始
数据存储的,但它把optional_header中部分数据加密了,加密过程如下:
跟据每台机器的硬盘C:分区的卷序列号和空间大小对应此机器的SS机器码,由SS机器码可以算
出一个word值,暂时称为SS_w,这段算法我没去分析,对我们用处也不大.在下载存盘时还需要一个随
机word值RANDOM_w,RANDOM_w实际就作为SS_user_key的高16位,由SS_w和RANDOM_w再算出一个word
值作为SS_user_key的低16位,因其算法可逆,所以可以用SS_user_key还原出原来的SS_w,这也是原程
序用来检测当前硬盘的SS_w和下载文件的SS_w是否一致的方法,不一致则提示你用户不对不是无法阅
读.
接下来用SS_user_key(用SS_w和RANDOM_w算法稍变也可以)算出两个word值,分别去减x_pix和
y_pix,最后把pdgtype添上10h.这样与我们有用的处理就算结束了,存盘后就可以了.
==========================================================================================
3. 我们需要做的工作
我们只要把pdgtype=10h的文件转变成pdgtype=02h的文件,就可以实现不受硬盘的限制脱机自由
阅读了.过程大致这样吧,大家也应该可以想到了,我用语言描述很费尽的,因此这里用汇编代码描述吧:
下面这段算法在pdg2.dll可以找到,我贴的稍有调整,并去掉了SS_w的检测
mov ebp,SS_user_key
shr eax, 10h
xor edx, edx
mov esi,1FFh
div esi
mov ecx, edx
mov eax,ebp
shr eax,10h
xor edx, edx
mov esi,0DBh
div esi
imul ecx, edx
lea edx, dword ptr [ecx-000050EEh]
mov ebx, ebp
sub ebx, edx
and ebx, 0000FFFFh
shr ebp, 10h
mov eax, ebx
xor edx, edx
mov ecx, 000003FBh
mov edi, 00000083h
div ecx
imul ebx, ebp
mov eax, ebp
mov ecx, edx
xor edx, edx
div edi
mov eax, edx
sub eax, ebx
sub eax, ecx
sub ecx, ebx
sub ecx, edx
add x_pix,ax ;还原x_pix
add y_pix,cx ;还原y_pix
为了看起来干净利索,我们把key_data区域都清0,不这样也没问题,不过有几处可能不要乱数
lea edi,key_data
mov ecx,30h
xor eax,eax
rep stosb
然后对30h字节长度的key_data进行一次md5,md5我就不贴在这里了,太费篇幅了,大家都可以
找到,我们下面要用到这128位(16字节)的结果作为分组加密算法的密钥,实际上我们的key_data
已经是清0的了,结果也是固定不变的了,也可以直接用现成的结果.我假设结果存在key_128处.
为转成pdgtype=02h文件,PDG_data要用encode_sub加密
mov edi, size_PDG_data
;数据长度
cmp edi, 10h
jl data_encode_end
mov ebx, key_128
mov esi, offset PDG_data
shr edi, 4
@@: push ebx ;密钥指针
push esi
;数据指针,加密结果也存储在这里
call sub_encode
;分组算法,每次加密10h字节
add esp, 8
add esi, 10h
dec edi
jnz @B
data_encode_end: ;处理结束
;下面是完整的分组加密算法,在********.exe中可以找到
;其中注释是我分析时加的,不尽准确,本想删去,后来一想算了,献丑也罢,只当交流了
;开始看时感觉上有点象RC5族算法,但越看越不象,资料有限,还是确认不了,望高手指教
encode_sub proc near
var_2C = dword ptr -2Ch
var_28 = dword ptr -28h
var_24 = dword ptr -24h
var_20 = dword ptr -20h
var_1C = dword ptr -1Ch
var_18 = dword ptr -18h
var_14 = dword ptr -14h
var_10 = dword ptr -10h
var_C = dword ptr -0Ch
var_8 = dword ptr -8
var_4 = dword ptr -4
arg_0 = dword ptr 8 ;pData
arg_4 = dword ptr 0Ch ;pKey
push ebp
mov ebp, esp
add esp, 0FFFFFFD4h
mov eax, [ebp+arg_0] ;pData
mov edx, [eax] ;A
mov [ebp+var_4], edx ;sa=A
mov edx, [eax+4] ;B
mov [ebp+var_8], edx ;sb=B
mov edx, [eax+8] ;C
mov [ebp+var_C], edx ;sc=C
mov edx, [eax+0Ch] ;D
mov [ebp+var_10], edx ;sd=D
mov eax, [ebp+arg_4] ;pKey
mov edx, [eax] ;ka
mov [ebp+var_14], edx ;ka
mov edx, [eax+4] ;kb
mov [ebp+var_18], edx ;kb
mov edx, [eax+8] ;kc
mov [ebp+var_1C], edx ;kc
mov edx, [eax+0Ch] ;kd
mov [ebp+var_20], edx ;kd
xor edx, edx ;c1
mov [ebp+var_24], edx ;c1
mov [ebp+var_28], 9E3779B9h ;c0
mov [ebp+var_2C], 10h ;r
@@: mov ecx, [ebp+var_28] ;c0
add [ebp+var_24], ecx ;c1+c0
mov eax, [ebp+var_8] ;sb
shl eax, 4 ;sb<<4
add eax, [ebp+var_14] ;sb<<4+ka
mov edx, [ebp+var_8] ;sb
add edx, [ebp+var_24] ;sb+c1
xor eax, edx ;(sb<<4+ka) xor
(sb+c1)
mov ecx, [ebp+var_8] ;sb
shr ecx, 5 ;sb>>5
add ecx, [ebp+var_18] ;sb>>5+kb
xor eax, ecx ;(sb<<4+ka) xor
(sb+c1) xor (sb>>5+kb)
add [ebp+var_4], eax
;sa=sa+[(sb<<4+ka) xor (sb+c1) xor (sb>>5+kb)]
mov eax, [ebp+var_C] ;sc
shl eax, 4 ;sc<<4
add eax, [ebp+var_1C] ;sc<<4+kc
mov edx, [ebp+var_C] ;sc
add edx, [ebp+var_24] ;sc+c1
xor eax, edx ;(sc<<4+kc) xor
(sc+c1)
mov ecx, [ebp+var_C] ;sc
shr ecx, 5 ;sc>>5
add ecx, [ebp+var_20] ;sc>>5+kd
xor eax, ecx ;(sc<<4+kc) xor
(sc+c1) xor (sc>>5+kd)
add [ebp+var_8], eax
;sb=sb+[(sc<<4+kc) xor (sc+c1) xor (sc>>5+kd)]
mov eax, [ebp+var_10] ;sd
shl eax, 4 ;sd<<4
add eax, [ebp+var_14] ;sd<<4+ka
mov edx, [ebp+var_10] ;sd
add edx, [ebp+var_24] ;sd+c1
xor eax, edx ;(sd<<4+ka) xor
(sd+c1)
mov ecx, [ebp+var_10] ;sd
shr ecx, 5 ;sd>>5
add ecx, [ebp+var_20] ;sd>>5+kd
xor eax, ecx ;(sd<<4+ka) xor
(sd+c1) xor (sd>>5+kd)
add [ebp+var_C], eax
;sc=sc+[(sd<<4+ka) xor (sd+c1) xor (sd>>5+kd)]
mov eax, [ebp+var_4] ;sa
shl eax, 4 ;sa<<4
add eax, [ebp+var_1C] ;sa<<4+kc
mov edx, [ebp+var_4] ;sa
add edx, [ebp+var_24] ;sa+c1
xor eax, edx ;(sa<<4+kc) xor
(sa+c1)
mov ecx, [ebp+var_4] ;sa
shr ecx, 5 ;sa>>5
add ecx, [ebp+var_18] ;sa>>5+kb
xor eax, ecx ;(sa<<4+kc) xor
(sa+c1) xor (sa>>5+kb)
add [ebp+var_10], eax
;sd=sd+[(sa<<4+kc) xor (sa+c1) xor (sa>>5+kb)]
mov eax, [ebp+var_2C] ;r
dec eax ;r-1
mov [ebp+var_2C], eax ;r=r-1
jnz @B
mov eax, [ebp+arg_0] ;pData
mov edx, [ebp+var_4] ;sa
mov [eax], edx ;A=sa
mov edx, [ebp+var_8] ;sb
mov [eax+4], edx ;B=sb
mov edx, [ebp+var_C] ;sc
mov [eax+8], edx ;C=sc
mov edx, [ebp+var_10] ;sd
mov [eax+0Ch], edx ;D=sd
mov esp, ebp
pop ebp
retn
encode_sub endp
==========================================================================================
后记:文章我是最懒得写的,写起来真是辛苦,花了数个小时,总感觉表述的不是很满意,无奈已经尽力了
分析水平有限,错误之处在所难免,唯恐误人子弟,读者朋友们见谅了,希望大家能提出自己的主见.
==========================================================================================
heXer/iPB
2002.10.26