本文是一篇軟件加密技術(shù)的基礎(chǔ)性文章,簡(jiǎn)要介紹了軟件加密的一些基本常識(shí)和一些加密產(chǎn)品
1、加密技術(shù)概述
一個(gè)密碼系統(tǒng)的安全性只在于密鑰的保密性,而不在算法的保密性。
對(duì)純數(shù)據(jù)的加密的確是這樣。對(duì)于你不愿意讓他看到這些數(shù)據(jù)(數(shù)據(jù)的明文)的人,用可靠的加密算法,只要破解者不知道被加密數(shù)據(jù)的密碼,他就不可解讀這些數(shù)據(jù)。
但是,軟件的加密不同于數(shù)據(jù)的加密,它只能是“隱藏”。不管你愿意不愿意讓他(合法用戶,或 Cracker)看見(jiàn)這些數(shù)據(jù)(軟件的明文),軟件最終總要在機(jī)器上運(yùn)行,對(duì)機(jī)器,它就必須是明文。既然機(jī)器可以“看見(jiàn)”這些明文,那么 Cracker,通過(guò)一些技術(shù),也可以看到這些明文。
于是,從理論上,任何軟件加密技術(shù)都可以破解。只是破解的難度不同而已。有的要讓最高明的 Cracker 忙上幾個(gè)月,有的可能不費(fèi)吹灰之力,就被破解了。
所以,反盜版的任務(wù)(技術(shù)上的反盜版,而非行政上的反盜版)就是增加 Cracker 的破解難度。讓他們花費(fèi)在破解軟件上的成本,比他破解這個(gè)軟件的獲利還要高。這樣 Cracker 的破解變得毫無(wú)意義——誰(shuí)會(huì)花比正版軟件更多的錢(qián)去買(mǎi)盜版軟件?
2、密碼學(xué)簡(jiǎn)介
2.1
(1) 發(fā)送者和接收者
假設(shè)發(fā)送者想發(fā)送消息給接收者,且想安全地發(fā)送信息:她想確信偷聽(tīng)者不能閱讀發(fā)送的消息。
(2) 消息和加密
消息被稱為明文。用某種方法偽裝消息以隱藏它的內(nèi)容的過(guò)程稱為加密,加了密的消息稱為密文,而把密文轉(zhuǎn)變?yōu)槊魑牡倪^(guò)程稱為解密。
明文用M(消息)或P(明文)表示,它可能是比特流(文本文件、位圖、數(shù)字化的語(yǔ)音流或數(shù)字化的視頻圖像)。至于涉及到計(jì)算機(jī),P是簡(jiǎn)單的二進(jìn)制數(shù)據(jù)。明文可被傳送或存儲(chǔ),無(wú)論在哪種情況,M指待加密的消息。
密文用C表示,它也是二進(jìn)制數(shù)據(jù),有時(shí)和M一樣大,有時(shí)稍大(通過(guò)壓縮和加密的結(jié)合,C有可能比P小些。然而,單單加密通常達(dá)不到這一點(diǎn))。加密函數(shù)E作用于M得到密文C,用數(shù)學(xué)表示為:
E(M)=C.
相反地,解密函數(shù)D作用于C產(chǎn)生M
D(C)=M.
先加密后再解密消息,原始的明文將恢復(fù)出來(lái),下面的等式必須成立:
D(E(M))=M
(3) 鑒別、完整性和抗抵賴
除了提供機(jī)密性外,密碼學(xué)通常有其它的作用:.
?。╝) 鑒別
消息的接收者應(yīng)該能夠確認(rèn)消息的來(lái)源;入侵者不可能偽裝成他人。
(b) 完整性檢驗(yàn)
消息的接收者應(yīng)該能夠驗(yàn)證在傳送過(guò)程中消息沒(méi)有被修改;入侵者不可能用假消息代替合法消息。
?。╟) 抗抵賴
發(fā)送者事后不可能虛假地否認(rèn)他發(fā)送的消息。
(4) 算法和密鑰
密碼算法也叫密碼,是用于加密和解密的數(shù)學(xué)函數(shù)。(通常情況下,有兩個(gè)相關(guān)的函數(shù):一個(gè)用作加密,另一個(gè)用作解密)
如果算法的保密性是基于保持算法的秘密,這種算法稱為受限制的算法。受限制的算法具有歷史意義,但按現(xiàn)在的標(biāo)準(zhǔn),它們的保密性已遠(yuǎn)遠(yuǎn)不夠。大的或經(jīng)常變換的用戶組織不能使用它們,因?yàn)槊坑幸粋€(gè)用戶離開(kāi)這個(gè)組織,其它的用戶就必須改換另外不同的算法。如果有人無(wú)意暴露了這個(gè)秘密,所有人都必須改變他們的算法。
更糟的是,受限制的密碼算法不可能進(jìn)行質(zhì)量控制或標(biāo)準(zhǔn)化。每個(gè)用戶組織必須有他們自己的唯一算法。這樣的組織不可能采用流行的硬件或軟件產(chǎn)品。但竊聽(tīng)者卻可以買(mǎi)到這些流行產(chǎn)品并學(xué)習(xí)算法,于是用戶不得不自己編寫(xiě)算法并予以實(shí)現(xiàn),如果這個(gè)組織中沒(méi)有好的密碼學(xué)家,那么他們就無(wú)法知道他們是否擁有安全的算法。
盡管有這些主要缺陷,受限制的算法對(duì)低密級(jí)的應(yīng)用來(lái)說(shuō)還是很流行的,用戶或者沒(méi)有認(rèn)識(shí)到或者不在乎他們系統(tǒng)中內(nèi)在的問(wèn)題。
現(xiàn)代密碼學(xué)用密鑰解決了這個(gè)問(wèn)題,密鑰用K表示。K可以是很多數(shù)值里的任意值。密鑰K的可能值的范圍叫做密鑰空間。加密和解密運(yùn)算都使用這個(gè)密鑰(即運(yùn)算都依賴于密鑰,并用K作為下標(biāo)表示),這樣,加/解密函數(shù)現(xiàn)在變成:
EK(M)=C
DK(C)=M.
DK(EK(M))=M.
有些算法使用不同的加密密鑰和解密密鑰,也就是說(shuō)加密密鑰K1與相應(yīng)的解密密鑰K2不同,在這種情況下:
EK1(M)=C
DK2(C)=M
DK2 (EK1(M))=M
所有這些算法的安全性都基于密鑰的安全性;而不是基于算法的細(xì)節(jié)的安全性。這就意味著算法可以公開(kāi),也可以被分析,可以大量生產(chǎn)使用算法的產(chǎn)品,即使偷聽(tīng)者知道你的算法也沒(méi)有關(guān)系;如果他不知道你使用的具體密鑰,他就不可能閱讀你的消息。
密碼系統(tǒng)由算法、以及所有可能的明文、密文和密鑰組成的。
基于密鑰的算法通常有兩類:對(duì)稱算法和公開(kāi)密鑰算法。下面將分別介紹:
2.2
對(duì)稱算法有時(shí)又叫傳統(tǒng)密碼算法,就是加密密鑰能夠從解密密鑰中推算出來(lái),反過(guò)來(lái)也成立。在大多數(shù)對(duì)稱算法中,加/解密密鑰是相同的。這些算法也叫秘密密鑰算法或單密鑰算法,它要求發(fā)送者和接收者在安全通信之前,商定一個(gè)密鑰。對(duì)稱算法的安全性依賴于密鑰,泄漏密鑰就意味著任何人都能對(duì)消息進(jìn)行加/解密。只要通信需要保密,密鑰就必須保密。
對(duì)稱算法的加密和解密表示為:
EK(M)=C
DK(C)=M
對(duì)稱算法可分為兩類。一次只對(duì)明文中的單個(gè)比特(有時(shí)對(duì)字節(jié))運(yùn)算的算法稱為序列算法或序列密碼。另一類算法是對(duì)明文的一組比特亞行運(yùn)算,這些比特組稱為分組,相應(yīng)的算法稱為分組算法或分組密碼。現(xiàn)代計(jì)算機(jī)密碼算法的典型分組長(zhǎng)度為64比特——這個(gè)長(zhǎng)度大到足以防止分析破譯,但又小到足以方便使用(在計(jì)算機(jī)出現(xiàn)前,算法普遍地每次只對(duì)明文的一個(gè)字符運(yùn)算,可認(rèn)為是序列密碼對(duì)字符序列的運(yùn)算)。
2.3
公開(kāi)密鑰算法(也叫非對(duì)稱算法)是這樣設(shè)計(jì)的:用作加密的密鑰不同于用作解密的密鑰,而且解密密鑰不能根據(jù)加密密鑰計(jì)算出來(lái)(至少在合理假定的長(zhǎng)時(shí)間內(nèi))。之所以叫做公開(kāi)密鑰算法,是因?yàn)榧用苊荑€能夠公開(kāi),即陌生者能用加密密鑰加密信息,但只有用相應(yīng)的解密密鑰才能解密信息。在這些系統(tǒng)中,加密密鑰叫做公開(kāi)密鑰(簡(jiǎn)稱公鑰),解密密鑰叫做私人密鑰(簡(jiǎn)稱私鑰)。私人密鑰有時(shí)也叫秘密密鑰。為了避免與對(duì)稱算法混淆,此處不用秘密密鑰這個(gè)名字。
用公開(kāi)密鑰K加密表示為
EK(M)=C.
雖然公開(kāi)密鑰和私人密鑰是不同的,但用相應(yīng)的私人密鑰解密可表示為:
DK(C)=M
有時(shí)消息用私人密鑰加密而用公開(kāi)密鑰解密,這用于數(shù)字簽名(后面將詳細(xì)介紹),盡管可能產(chǎn)生混淆,但這些運(yùn)算可分別表示為:
EK(M)=C
DK(C)=M
當(dāng)前的公開(kāi)密碼算法的速度,比起對(duì)稱密碼算法,要慢的多,這使得公開(kāi)密碼算法在大數(shù)據(jù)量的加密中應(yīng)用有限。
2.4
單向散列函數(shù) H(M) 作用于一個(gè)任意長(zhǎng)度的消息 M,它返回一個(gè)固定長(zhǎng)度的散列值 h,其中 h 的長(zhǎng)度為 m .
輸入為任意長(zhǎng)度且輸出為固定長(zhǎng)度的函數(shù)有很多種,但單向散列函數(shù)還有使其單向的其它特性:
(1) 給定 M ,很容易計(jì)算 h ;
?。?) 給定 h ,根據(jù) H(M) = h 計(jì)算 M 很難 ;
(3) 給定 M ,要找到另一個(gè)消息 M‘ 并滿足 H(M) = H(M’) 很難。
在許多應(yīng)用中,僅有單向性是不夠的,還需要稱之為“抗碰撞”的條件:
要找出兩個(gè)隨機(jī)的消息 M 和 M‘,使 H(M) = H(M’) 滿足很難。
由于散列函數(shù)的這些特性,由于公開(kāi)密碼算法的計(jì)算速度往往很慢,所以,在一些密碼協(xié)議中,它可以作為一個(gè)消息 M 的摘要,代替原始消息 M,讓發(fā)送者為 H(M) 簽名而不是對(duì) M 簽名 .
如 SHA 散列算法用于數(shù)字簽名協(xié)議 DSA中。
2.5
提到數(shù)字簽名就離不開(kāi)公開(kāi)密碼系統(tǒng)和散列技術(shù)。
有幾種公鑰算法能用作數(shù)字簽名。在一些算法中,例如RSA,公鑰或者私鑰都可用作加密。用你的私鑰加密文件,你就擁有安全的數(shù)字簽名。在其它情況下,如DSA,算法便區(qū)分開(kāi)來(lái)了??數(shù)字簽名算法不能用于加密。這種思想首先由Diffie和Hellman提出 .
基本協(xié)議是簡(jiǎn)單的 :
(1) A 用她的私鑰對(duì)文件加密,從而對(duì)文件簽名。
?。?) A 將簽名的文件傳給B.
?。?) B用A的公鑰解密文件,從而驗(yàn)證簽名。
這個(gè)協(xié)議中,只需要證明A的公鑰的確是她的。如果B不能完成第(3)步,那么他知道簽名是無(wú)效的。
這個(gè)協(xié)議也滿足以下特征:
(1) 簽名是可信的。當(dāng)B用A的公鑰驗(yàn)證信息時(shí),他知道是由A簽名的。
?。?) 簽名是不可偽造的。只有A知道她的私鑰。
?。?) 簽名是不可重用的。簽名是文件的函數(shù),并且不可能轉(zhuǎn)換成另外的文件。
?。?) 被簽名的文件是不可改變的。如果文件有任何改變,文件就不可能用A的公鑰驗(yàn)證。
(5) 簽名是不可抵賴的。B不用A的幫助就能驗(yàn)證A的簽名。
在實(shí)際應(yīng)用中,因?yàn)楣裁艽a算法的速度太慢,簽名者往往是對(duì)消息的散列簽名而不是對(duì)消息本身簽名。這樣做并不會(huì)降低簽名的可信性。
3
3.1
數(shù)學(xué)算法一項(xiàng)都是密碼加密的核心,但在一般的軟件加密中,它似乎并不太為人們關(guān)心,因?yàn)榇蠖鄶?shù)時(shí)候軟件加密本身實(shí)現(xiàn)的都是一種編程的技巧。但近幾年來(lái)隨著序列號(hào)加密程序的普及,數(shù)學(xué)算法在軟件加密中的比重似乎是越來(lái)越大了。
看看在網(wǎng)絡(luò)上大行其道的序列號(hào)加密的工作原理。當(dāng)用戶從網(wǎng)絡(luò)上下載某個(gè)shareware——共享軟件后,一般都有使用時(shí)間上的限制,當(dāng)過(guò)了共享軟件的試用期后,你必須到這個(gè)軟件的公司去注冊(cè)后方能繼續(xù)使用。注冊(cè)過(guò)程一般是用戶把自己的私人信息(一般主要指名字)連同信用卡號(hào)碼告訴給軟件公司,軟件公司會(huì)根據(jù)用戶的信息計(jì)算出一個(gè)序列碼,在用戶得到這個(gè)序列碼后,按照注冊(cè)需要的步驟在軟件中輸入注冊(cè)信息和注冊(cè)碼,其注冊(cè)信息的合法性由軟件驗(yàn)證通過(guò)后,軟件就會(huì)取消掉本身的各種限制,這種加密實(shí)現(xiàn)起來(lái)比較簡(jiǎn)單,不需要額外的成本,用戶購(gòu)買(mǎi)也非常方便,在互聯(lián)網(wǎng)上的軟件80%都是以這種方式來(lái)保護(hù)的。
軟件驗(yàn)證序列號(hào)的合法性過(guò)程,其實(shí)就是驗(yàn)證用戶名和序列號(hào)之間的換算關(guān)系是否正確的過(guò)程。其驗(yàn)證最基本的有兩種,一種是按用戶輸入的姓名來(lái)生成注冊(cè)碼,再同用戶輸入的注冊(cè)碼比較,公式表示如下:
序列號(hào) = F(用戶名)
但這種方法等于在用戶軟件中再現(xiàn)了軟件公司生成注冊(cè)碼的過(guò)程,實(shí)際上是非常不安全的,不論其換算過(guò)程多么復(fù)雜,解密者只需把你的換算過(guò)程從程序中提取出來(lái)就可以編制一個(gè)通用的注冊(cè)程序。
另外一種是通過(guò)注冊(cè)碼來(lái)驗(yàn)證用戶名的正確性,公式表示如下:
用戶名稱 = F逆(序列號(hào)) (如ACDSEE)
這其實(shí)是軟件公司注冊(cè)碼計(jì)算過(guò)程的反算法,如果正向算法與反向算法不是對(duì)稱算法的話,對(duì)于解密者來(lái)說(shuō),的確有些困難,但這種算法相當(dāng)不好設(shè)計(jì)。
于是有人考慮到以下的算法:
F1(用戶名稱) = F2(序列號(hào))
F1、F2是兩種完全不同的的算法,但用戶名通過(guò)F1算法計(jì)算出的特征字等于序列號(hào)通過(guò)F2算法計(jì)算出的特征字,這種算法在設(shè)計(jì)上比較簡(jiǎn)單,保密性相對(duì)以上兩種算法也要好的多。如果能夠把F1、F2算法設(shè)計(jì)成不可逆算法的話,保密性相當(dāng)?shù)暮?;可一旦解密者找到其中之一的反算法的話,這種算法就不安全了。一元算法的設(shè)計(jì)看來(lái)再如何努力也很難有太大的突破,那么二元呢?
特定值 = F(用戶名,序列號(hào))
這個(gè)算法看上去相當(dāng)不錯(cuò),用戶名稱與序列號(hào)之間的關(guān)系不再那么清晰了,但同時(shí)也失去了用戶名于序列號(hào)的一一對(duì)應(yīng)關(guān)系,軟件開(kāi)發(fā)者必須自己維護(hù)用戶名稱與序列號(hào)之間的唯一性,但這似乎不是難以辦到的事,建個(gè)數(shù)據(jù)庫(kù)就可以了。當(dāng)然也可以把用戶名稱和序列號(hào)分為幾個(gè)部分來(lái)構(gòu)造多元的算法。
特定值 = F(用戶名1,用戶名2,...序列號(hào)1,序列號(hào)2...)
現(xiàn)有的序列號(hào)加密算法大多是軟件開(kāi)發(fā)者自行設(shè)計(jì)的,大部分相當(dāng)簡(jiǎn)單。而且有些算法作者雖然下了很大的功夫,效果卻往往得不到它所希望的結(jié)果。
3.2
有些程序的試用版每次運(yùn)行都有時(shí)間限制,例如運(yùn)行10分鐘或20分鐘就停止工作,必須重新運(yùn)行該程序才能正常工作。這些程序里面自然有個(gè)定時(shí)器來(lái)統(tǒng)計(jì)程序運(yùn)行的時(shí)間。
這種方法使用的較少。
3.3
Key File(注冊(cè)文件)是一種利用文件來(lái)注冊(cè)軟件的保護(hù)方式。Key File一般是一個(gè)小文件,可以是純文本文件,也可以是包含不可顯示字符的二進(jìn)制文件,其內(nèi)容是一些加密過(guò)或未加密的數(shù)據(jù),其中可能有用戶名、注冊(cè)碼等信息。文件格式則由軟件作者自己定義。試用版軟件沒(méi)有注冊(cè)文件,當(dāng)用戶向作者付費(fèi)注冊(cè)之后,會(huì)收到作者寄來(lái)的注冊(cè)文件,其中可能包含用戶的個(gè)人信息。用戶只要將該文件放入指定的目錄,就可以讓軟件成為正式版。該文件一般是放在軟件的安裝目錄中或系統(tǒng)目錄下。軟件每次啟動(dòng)時(shí),從該文件中讀取數(shù)據(jù),然后利用某種算法進(jìn)行處理,根據(jù)處理的結(jié)果判斷是否為正確的注冊(cè)文件,如果正確則以注冊(cè)版模式來(lái)運(yùn)行。
這種保護(hù)方法使用也不多。
3.4
即光盤(pán)保護(hù)技術(shù)。程序在啟動(dòng)時(shí)判斷光驅(qū)中的光盤(pán)上是否存在特定的文件,如果不存在則認(rèn)為用戶沒(méi)有正版光盤(pán),拒絕運(yùn)行。在程序運(yùn)行的過(guò)程當(dāng)中一般不再檢查光盤(pán)的存在與否。Windows下的具體實(shí)現(xiàn)一般是這樣的:先用GetLogicalDriveStrings()或GetLogicalDrives( )得到系統(tǒng)中安裝的所有驅(qū)動(dòng)器的列表,然后再用GetDriveType()檢查每一個(gè)驅(qū)動(dòng)器,如果是光驅(qū)則用CreateFileA( )或FindFirstFileA()等函數(shù)檢查特定的文件存在與否,并可能進(jìn)一步地檢查文件的屬性、大小、內(nèi)容等。
3.5
軟件狗是一種智能型加密工具。它是一個(gè)安裝在并口、串口等接口上的硬件電路,同時(shí)有一套使用于各種語(yǔ)言的接口軟件和工具軟件。當(dāng)被狗保護(hù)的軟件運(yùn)行時(shí),程序向插在計(jì)算機(jī)上的軟件狗發(fā)出查詢命令,軟件狗迅速計(jì)算查詢并給出響應(yīng),正確的響應(yīng)保證軟件繼續(xù)運(yùn)行。如果沒(méi)有軟件狗,程序?qū)⒉荒苓\(yùn)行,復(fù)雜的軟硬件技術(shù)結(jié)合在一起防止軟件盜版。真正有商業(yè)價(jià)值得軟件一般都用軟件狗來(lái)保護(hù)。
平時(shí)常見(jiàn)的狗主要有“洋狗”(國(guó)外狗)和“土狗”(國(guó)產(chǎn)狗)。這里“洋狗”主要指美國(guó)的彩虹和以色列的HASP,“土狗”主要有金天地(現(xiàn)在與美國(guó)彩虹合資,叫“彩虹天地”)、深思、尖石??偟恼f(shuō)來(lái),“洋狗”在軟件接口、加殼、反跟蹤等“軟”方面沒(méi)有“土狗”好,但在硬件上破解難度非常大;而“土狗”在軟的方面做的很好,但在硬件上不如“洋狗”,稍有單片機(jī)功力的人,都可以復(fù)制。
3.6
通過(guò)在軟盤(pán)上格式化一些非標(biāo)準(zhǔn)磁道,在這些磁道上寫(xiě)入一些數(shù)據(jù),如軟件的解密密鑰等等。這種軟盤(pán)成為“鑰匙盤(pán)”。軟件運(yùn)行時(shí)用戶將軟盤(pán)插入,軟件讀取這些磁道中的數(shù)據(jù),判斷是否合法的“鑰匙盤(pán)”。
軟盤(pán)加密還有其它一些技術(shù),如弱位加密等等。
隨著近年來(lái)軟盤(pán)的沒(méi)落,這種方法基本上退出了歷史舞臺(tái)。
3.7
用戶得到(買(mǎi)到或從網(wǎng)上下載)軟件后,安裝時(shí)軟件從用戶的機(jī)器上取得該機(jī)器的一些硬件信息(如硬盤(pán)序列號(hào)、BOIS序列號(hào)等等),然后把這些信息和用戶的序列號(hào)、用戶名等進(jìn)行計(jì)算,從而在一定程度上將軟件和硬件部分綁定。用戶需要把這一序列號(hào)用Email、電話或郵寄等方法寄給軟件提供商或開(kāi)發(fā)商,軟件開(kāi)發(fā)商利用注冊(cè)機(jī)(軟件)產(chǎn)生該軟件的注冊(cè)號(hào)寄給用戶即可。軟件加密雖然加密強(qiáng)度比硬件方法較弱,但它具有非常廉價(jià)的成本、方便的使用方法等優(yōu)點(diǎn)。非常適合做為采用光盤(pán)(CDROM)等方式發(fā)授軟件的加密方案。
此種加密算法的優(yōu)點(diǎn)
·
·
·
·
·
·
·
這種加密還有以下特點(diǎn)
1、 注冊(cè)加密的軟件,只能在一臺(tái)機(jī)器上安裝使用。把軟件拷貝到其它機(jī)器上不能運(yùn)行。
2、若用戶想在另一機(jī)器上安裝運(yùn)行,必須把軟件在這一機(jī)器上運(yùn)行時(shí)的序列號(hào),寄給軟件出版商換取注冊(cè)密碼。當(dāng)然應(yīng)再交一份軟件費(fèi)用。
3、 此加密方法特別適應(yīng)在因特網(wǎng)上發(fā)布的軟件及用光盤(pán)發(fā)布的軟件。
注釋:
1、“加密技術(shù)概述”部分內(nèi)容參考了大學(xué)教材“密碼學(xué)基礎(chǔ)”。
2、“當(dāng)前流行的一些軟件保護(hù)技術(shù)”部分內(nèi)容參考了“加密與解密--軟件保護(hù)技術(shù)及完全解決方案”一文。
溫馨提示:因考試政策、內(nèi)容不斷變化與調(diào)整,信管網(wǎng)網(wǎng)站提供的以上信息僅供參考,如有異議,請(qǐng)以權(quán)威部門(mén)公布的內(nèi)容為準(zhǔn)!
信管網(wǎng)致力于為廣大信管從業(yè)人員、愛(ài)好者、大學(xué)生提供專業(yè)、高質(zhì)量的課程和服務(wù),解決其考試證書(shū)、技能提升和就業(yè)的需求。
信管網(wǎng)軟考課程由信管網(wǎng)依托10年專業(yè)軟考教研傾力打造,官方教材參編作者和資深講師坐鎮(zhèn),通過(guò)深研歷年考試出題規(guī)律與考試大綱,深挖核心知識(shí)與高頻考點(diǎn),為學(xué)員考試保駕護(hù)航。面授、直播&錄播,多種班型靈活學(xué)習(xí),滿足不同學(xué)員考證需求,降低課程學(xué)習(xí)難度,使學(xué)習(xí)效果事半功倍。
發(fā)表評(píng)論 查看完整評(píng)論 | |