超星PDG轉換(10千字)

看雪資料發表於2015-11-15


======================================
“超星讀書卡”持卡使用者可以透過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

相關文章