




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
用IDA反匯編動態(tài)庫最近,一直在學習如何利用IDA來反匯編動態(tài)庫,這里把我的學習心得寫下來。為簡單起見,這里就自己所寫的一個動態(tài)庫里的一個簡單函數(shù)進行一下反匯編,給出如何寫出其C代碼的詳細過程,希望對新手有點幫助。廢話少說,先給出其動態(tài)連接庫的C代碼如下_declspec(dllexport)intadd(chara,intb,intc[2]){intd=a+b+c[0]+c[1]; returnd;}至于為什么要設(shè)置這樣的參數(shù),待會在反匯編時進行說明。下面給出其詳細的反匯編過程,并補充相關(guān)的經(jīng)驗總結(jié)。第一步、裝載動態(tài)庫文件”first.dll”,裝載之后得到下面的截圖:通過在Functions一欄中雙擊add函數(shù),我們來到add()函數(shù)的地方(同上圖),我們看到”text:10001010addprocnear;CODEXREF:add(char,int,int*const)j”這樣一欄顯示了add函數(shù)的參數(shù),雖然有點出入,但大體正確??赡苁且驗閍dd函數(shù)本身比較簡單,所以IDA很容易就識別出了其參數(shù),一般地,IDA是識別不出來的,網(wǎng)上有一個插件為”Flair.v5.20”據(jù)說可以部分地解決函數(shù)的參數(shù)識別問題,但這個軟件我沒有下載到,就不說這個了。第二步、我們看到”.text:10001010”這些欄有很多標示,在下面的匯編語句中會用到。我們看接下來的三行代碼:.text:10001010pushebp.text:10001011movebp,esp.text:10001013subesp,44h這三行代碼模式基本上是固定的,(至少我遇到的都是這樣)首先是保存ebp,然后用ebp來保存esp的原始指向,再將esp的指向向上移動44h個字節(jié),(當然這里44h不是固定的)為什么會有這樣固定的代碼呢?就代碼“subesp44h”而言,在原esp的基礎(chǔ)上向上移動44h的字節(jié)空間,而esp-----esp-44h這個44h的空間是為了存放一般變量的。其他兩行相信讀者很容易理解其理由。第三步、看下面三行代碼..text:10001016pushebx.text:10001017pushesi.text:10001018pushedi當我們在函數(shù)的開頭看到這樣的代碼時,而后面又沒有緊跟著”call+function”時,我們大可不用理解,因為這些push語句目的都是為了不破壞原始ebx,esi,edi的值而將他們保存起來,并且這里我們可以看到,是保存在esp-44h之上的。也就是說,如果我們看到函數(shù)的開頭出現(xiàn)將寄存器push進esp-x之上的空間(我們將”esp-x至esp”的堆棧空間成為“一般變量??臻g”),這里就是指push進esp-44h之上的堆棧空間,我們不用去關(guān)心,在函數(shù)末尾肯定會有相應(yīng)的代碼將他們還原的。(待會我們會看到)第四步、繼續(xù)向下看,進入關(guān)鍵代碼段。在做進一步解釋之前我先畫一幅堆棧圖。如下:ediediexiebx一般變量堆??臻gcan1can2can3ebp=esp(假設(shè)這里是最先的esp,且值為0x0013ff80,“1000101代碼”)(esp-44h)指向,為(0x0013ff80-44h)esp-44h之上的空間ebp+arg_0ebp+arg_4ebp+arg_8好了,有了上一幅圖,說明起來會容易些??唇酉聛淼乃臈l代碼:.text:10001019leaedi,[ebp+var_44].text:1000101Cmovecx,11h.text:10001021moveax,0CCCCCCCCh.text:10001026repstosd這四條代碼和上面的三條代碼一樣,其模式一般是固定不變的。其作用就是實現(xiàn)了“一般變量棧空間”的初始化。這里將圖中所示的“一般變量??臻g“初始化為0xCCCCCCCC.其具體的代碼解釋如下:leaedi,[ebp+var_44]:edi=ebp+vat_44movecx,11h:ecx=0x11hmoveax,0CCCCCCCCh:eax=0xCCCCCCCC;repstosd:for(inti1=0;i1<ecx;i1++)edi[i1]=eax;這里因為ecx=0x11(44h/4),所以將整個“一般變量??臻g”全部初始化為eax=0xCCCCCCCC;這下就清楚了為什么上面的四條代碼一般模式是固定的,原因就是對將要用到的“一般變量棧空間”進行初始化。第五步、進入核心代碼段.text:10001028movsxeax,[ebp+arg_0].text:1000102Caddeax,[ebp+arg_4].text:1000102Fmovecx,[ebp+arg_8].text:10001032addeax,[ecx].text:10001034movedx,[ebp+arg_8].text:10001037addeax,[edx+4].text:1000103Amov[ebp+var_4],eax.text:1000103Dmoveax,[ebp+var_4]從圖上我們可以知道arg_0,arg_4,arg_8分別是0x8,0xc,0x10。(要是從圖上看不清,請參考文件”first.txt”)代碼”movsxeax,[ebp+arg_0]”表示將[ebp+arg_0]的值進行有符號擴展后傳給eax。但是這里的[ebp+arg_0]究竟是什么呢?我們假設(shè)[ebp+arg_0]=can1,如圖,那么”movsxeax,[ebp+arg_0]”就表示”eax=(char)can1”。因為我們有源代碼,我們知道函數(shù)add()的參數(shù)為intadd(chara,intb,intc[2]),這樣我們有理由懷疑(char)can1就是chara,即add的第一個參數(shù)。我們接著看下一條代碼:”addeax,[ebp+arg_4]”,有了上面的猜想,我們不無理由認為[ebp+arg_4]就是can2,這樣就實現(xiàn)了eax=can1+can2;再看代碼”movecx,[ebp+arg_8]”和”addeax,[ecx]”,我們可以猜想得到[ebp+arg_8]其實是一個地址,因為后面有”addeax,[ecx]”這樣的代碼,表示為將ecx所指向的地址的值傳給eax。所以肯定這就是can3,即一個int型的地址參數(shù)?!盿ddeax,[ecx]”之后,eax=can1+can2+can3[0]。接下來的兩行代碼”movedx,[ebp+arg_8”和”addeax,[edx+4]”與上面的相似,實現(xiàn)了eax=can1+can2+can3[0]+can3[1]。再看剩下的兩行代碼,”mov[ebp+var_4],eax”,”moveax,[ebp+var_4]”,這兩行代碼是先將eax的值賦給[ebp+var_4],再將其值給eax。第一行代碼其實是將結(jié)果儲存在[ebp+var_4],第二行代碼是將返回值給eax。(一般地,函數(shù)的返回值要是是int型的話,都會將返回值賦給exa,這也可以看成是一種固定的模式)。到這里,函數(shù)基本上完成了,這里可以看出為什么將add函數(shù)的參數(shù)設(shè)置成char,int和int*了,目的就是為了認清楚原來的參數(shù)是放在堆棧中具體什么地方就目前可見,函數(shù)的第i個參數(shù)放在esp+4+i*4所指的堆棧中(i從1開始,esp是指最先的棧頂指針,后來傳給了ebp)(一般第一個參數(shù)都是從[ebp+arg_0]開始,而arg_0一般為8,其他的參數(shù)依次放置)第六步、接下來的代碼基本上就是還原先前存儲的寄存器,就不做詳細解釋了。.text:10001040popedi.text:10001041popesi.text:10001042popebx.text:10001043movesp,ebp.text:10001045popebp.text:10001046retn.text:10001046addendp好了,到這里,我們對用IDA反匯編動態(tài)庫文件有了一定的認識和經(jīng)驗積累。但是這樣反匯編成C語言似乎太慢了,對于這個簡單的add()函數(shù)還好,遇到難一點的函數(shù)那就說不定了。好在我們有”hexray”這個將匯編代碼轉(zhuǎn)成C語言代碼的插件,下面我們就用試著用這個插件來寫C代碼。加載first.dll文件后,同樣在”Functions”一欄雙擊函數(shù)add,來到view的add函數(shù)區(qū)。按F5鍵,得到如下的截圖int__cdecladd(chara1,inta2,inta3){charv4;//[sp+Ch][bp-44h]@1memset(&v4,-858993460,0x44u);return*(_DWORD*)(a3+4)+*(_DWORD*)a3+a2+a1;}這里很明顯Hexray在做轉(zhuǎn)換時,第三個參數(shù)沒有分清是地址還是值,沒關(guān)系,在下面的代碼中我們看到*(_DWORD*)(a3+4)這樣的代碼,其實就是a3[4/4]=a3[1],后面的*(_DWORD*)a3就是a3[0]了。很多時候,都會出現(xiàn)上面的強制轉(zhuǎn)換問題,比如遇到代碼*(_DWORD*)(str+4*i)那么一般地,str就是一個地址,代碼本身就表示是str[i](要對這些常見的轉(zhuǎn)換做出很快的反應(yīng)以節(jié)省時間)。這樣,Hexray就直接幫我們轉(zhuǎn)換得到了源代碼。當然,有時候Hexray的轉(zhuǎn)換是不可信的,這時我們就要寫程序進行檢測了。接續(xù)看上面的代碼,charv4;和后面緊跟著的memset(&v4,-858993460,0x44u)其實就是將“一般變量棧空間”初始化為-858993460=0xcccccccc,這兩行代碼一般是固定模式的,其作用都是將“一般變量??臻g”初始化,一般不用管,接下來的才是真正的代碼區(qū)。既然這里我們說到了Hexray的轉(zhuǎn)換問題,這里我們不妨將所見到的幾個常見轉(zhuǎn)換問題規(guī)整出來,以便以后我們更快地根據(jù)DLL得到C源代碼。如果函數(shù)的參數(shù)中出現(xiàn)int*這樣整型指針,Hexray一般會視為int型進行轉(zhuǎn)換,(原因就是因為地址int*其實也是一個整形,只不過是表示一個地址而已)。怎樣看出這樣的轉(zhuǎn)換問題呢,很簡單,一般出現(xiàn)這樣的轉(zhuǎn)換問題,在轉(zhuǎn)換的C代碼中都有像“*(_DWORD*)(par+4*i)”這樣的代碼,(其中par是函數(shù)中的一個整型參數(shù)),這時一般地,我們能肯定轉(zhuǎn)化除了問題,直接將“*(_DWORD*)(par+4*i)”改成par[i]就可以了。更簡單的改寫,就是直接利用hexray的改寫功能進行改寫,如上面的那個Dll文件中的add()函數(shù),Hexray轉(zhuǎn)換得到的最初C代碼如圖:在add函數(shù)上點擊右鍵,選擇SetitemtypeY,如圖:點擊之后得到:將inta3修改成int*strnum,點ok保存,我們發(fā)現(xiàn)Hexray轉(zhuǎn)換的C代碼也發(fā)上了改變,如圖:到這里我們不得不佩服Hexray的智能化。同樣地,和上面出現(xiàn)的轉(zhuǎn)換問題相似,hexray在轉(zhuǎn)換過程中要是函數(shù)有參數(shù)char*str型,那么hexray一般也會將參數(shù)識為int型,其原因不再羅嗦了。那么怎樣發(fā)現(xiàn)這樣的轉(zhuǎn)換問題呢?和上面的原理一樣,在C代碼中一般會有如下的代碼出現(xiàn):*(_BYTE*)(str+i)(注意:這里已經(jīng)不是4*i了,為什么,因為一個int型占4個字節(jié),而一個char型只占1個字節(jié),所以是i而不是4*i了,類似地我們可以推測,參數(shù)要是是short*str,那么C代碼中一般就會出現(xiàn)*(_WORD*)(str+2*i),究竟是不是這樣,可以寫個Dll函數(shù)嘗試一些)看到這樣的代碼,我們一般敢肯定Hexray將char*str轉(zhuǎn)換成了一個整型。出現(xiàn)了轉(zhuǎn)換問題,我們用上面的辦法將他們修改過來就行了。要是不是參數(shù),而是在函數(shù)體內(nèi)部我們定義了一個int*str類型或者char*str類型的數(shù)組,那么這時Hexray會不會將他們轉(zhuǎn)換成int型呢?未必,Hexray有的時候能認出來,有的時候又分不出來,但我們怎么才能看出來呢。第一、看代碼中也沒有如上所說強制轉(zhuǎn)換問題,要是有的話我們照葫蘆畫瓢就可以了。第二、也有看不出來的,比如我們在代碼中定義了int*str[100]。但是我們只用到其中的前3個數(shù)字,那么hexray一般不會出現(xiàn)強制轉(zhuǎn)換,直接就在”一般變量??臻g”的一塊連續(xù)地址空間存放了要用的前三個數(shù)字,這可能是Hexray的一種優(yōu)化吧。(注意:可以從轉(zhuǎn)換得到的C代碼后面的注釋看出是不是連續(xù)的,如:intv6//[sp+5Ch][bp-188h]@1,intv7;//[sp+58h][bp-18Ch]@1,intv8;//[sp+54h][bp-190h]@1就能很明顯地根據(jù)后面的注釋看出是一個連續(xù)空間。)總結(jié)起來,要確定在轉(zhuǎn)換中是否出現(xiàn)了轉(zhuǎn)換問題,最主要的是看代碼中是不是有強制轉(zhuǎn)換的代碼,如:*(_DWORD*)(str+4*i),*(_BYTE*)(str+i)等。要是有。我們可以認為出現(xiàn)了轉(zhuǎn)換問題,沒有那更好了。說明:上面所說的一切都不是絕對的,只是我的一個經(jīng)驗總結(jié)。畢竟插件是人家的,并不清楚插件中的代碼是如何處理,所以具體的情況我們還需具體對待。intv6//[sp+5Ch][bp-188h]@1intv7;//[sp+58h][bp-18Ch]@1intv8;//[sp+54h][bp-190h]@1利用上面的一些經(jīng)驗,我們來看下面的一個dll文件”second.dll”,同樣我們也對里面的一個函數(shù)char*initstr(intn,char*str,intlen)進行跟蹤得到C代碼。同樣先給出真正的C代碼如下:_declspec(dllexport)char*initstr(intn,char*str,intlen){ if(len<n)returnNULL; for(inti1=0;i1<n;i1++) { str[i1]=i1+1; } returnstr;}盡管我們用Hexray能很容易地得到上面的C代碼,但是我們這里還是一步步先來看一下匯編代碼,一是看看我們上面所獲取的經(jīng)驗是不是可以用上,二是我們進一步熟悉匯編代碼。好了,在加載second.dll文件之后,我們得到如下的截圖:具體的代碼可以參考文件“second.txt”。這個代碼比上面的代碼稍微復(fù)雜些。沒事,看代碼:.text:10001010pushebp.text:10001011movebp,esp.text:10001013subesp,44h.text:10001016pushebx.text:10001017pushesi.text:10001018pushedi.text:10001019leaedi,[ebp+var_44].text:1000101Cmovecx,11h.text:10001021moveax,0CCCCCCCCh.text:10001026repstosd上面列出的代碼我們再熟悉不過了,都是些固定的模塊,不用去細看了。接下來看下面的代碼:.text:10001028moveax,[ebp+arg_8].text:1000102Bcmpeax,[ebp+arg_0].text:1000102Ejgeshortloc_10001034.text:10001030xoreax,eax.text:10001032jmpshortloc_10001061根據(jù)上面的經(jīng)驗,我們知道[ebp+arg_0]里面放的就是第一個參數(shù)。代碼意思大體如下:.text:10001028:將參數(shù)3傳給eax.text:1000102B:eax與參數(shù)1比較,即第三個參數(shù)與第一個參數(shù)比較.text:1000102E:if(eax>=[ebp+arg_0])gotoloc_10001034;elsedonothing.text:10001030:eax=0;。.text:10001032:gotoloc_10001061繼續(xù)看接下來的代碼:.text:10001034loc_10001034:;CODEXREF:initstr_0+1Ej.text:10001034mov[ebp+var_4],0.text:1000103Bjmpshortloc_10001046(以上的代碼其實就是for(i=0;……)的模式)上面的代碼沒什么好說的,我們繼續(xù)往下看:.text:1000103Dloc_1000103D:;CODEXREF:initstr_0+4Cj.text:1000103Dmovecx,[ebp+var_4].text:10001040addecx,1.text:10001043mov[ebp+var_4],ecx上面的代碼相當于[ebp+var_4]++;for(i=0;i??;i++)繼續(xù)向下:.text:10001046loc_10001046:;CODEXREF:initstr_0+2Bj.text:10001046movedx,[ebp+var_4].text:10001049cmpedx,[ebp+arg_0].text:1000104Cjgeshortloc_1000105E(for(i=0;i<can1;i++)).text:1000104Emoveax,[ebp+var_4].text:10001051addeax,1.text:10001054movecx,[ebp+arg_4].text:10001057addecx,[ebp+var_4].text:1000105Amov[ecx],al.text:1000105Cjmpshortloc_1000103D.text:1000105Eloc_1000105E:;CODEXREF:initstr_0+3Cj.text:1000105Emoveax,[ebp+arg_4].text:10001061.text:10001061loc_10001061:;CODEXREF:initstr_0+22j.text:10001061popedi.text:10001062popesi.text:10001063popebx.text:10001064movesp,ebp.text:10001066popebp.text:10001067retn.text:10001067initstr_0endp跳轉(zhuǎn)到loc_10001061:就意味著函數(shù)體的結(jié)束,我們看到在代碼.text:10001032jmpshortloc_10001061中,就直接跳轉(zhuǎn)到結(jié)束處,即if(can3<can1)就直接結(jié)束了,此時的exa=0;所以有”if(can3<can1)return0;”下面的代碼構(gòu)成了一個循環(huán),.text:1000103Dloc_1000103D:;CODEXREF:initstr_0+4Cj.text:1000105Cjmpshortloc_1000103D從夾在中的代碼.text:10001049cmpedx,[ebp+arg_0].text:1000104Cjgeshortloc_1000105E從這段循環(huán)代碼我們看到IDA是怎樣反匯編一個for循環(huán)的:出現(xiàn)如下的代碼模塊(對計數(shù)器i的初始化).text:1000103Dloc_1000103D:;CODEXREF:initstr_0+4Cj.text:1000103Dmovecx,[ebp+var_4].text:10001040addecx,1.text:10001043mov[ebp+var_4],ecx這就相當于for(i=0;……)接下倆的代碼模塊相當于實現(xiàn)for(i=0;i???;i++)中的i++功能。.text:1000103Dloc_1000103D:;CODEXREF:initstr_0+4Cj.text:1000103Dmovecx,[ebp+var_4].text:10001040addecx,1.text:10001043mov[ebp+var_4],ecx接下來的模塊就實現(xiàn)了for(i=0;i<can1;i++)中的i<can1功能??梢姡琁DA在實現(xiàn)一個for循環(huán)時,首先初始化i=num;然后i+=n;最后是for循環(huán)中間判定功能的實現(xiàn)。剩下的代碼就說其中幾條吧.text:1000105Emoveax,[ebp+arg_4]這條代碼實際上是將返回的char的指針放在了eax里面當做返回。這和我們上面的經(jīng)驗猜想符合。從代碼.text:10001057addecx,[ebp+var_4].text:1000105Amov[ecx],al我們很容易看出[ebp+var_4]是一個指針(因為有[ecx]),而且很可能還是一個char型的指針(因為傳給[ecx]的是一個字節(jié)al)其他的代碼這里就不說了。突然發(fā)先一個問題:在函數(shù)剛開始的時候都會有相應(yīng)的標示,這個函數(shù)的標示如下:.text:10001010initstr_0procnear;CODEXREF:initstrj.text:10001010.text:10001010var_44=byteptr-44h.text:10001010var_4=dwordptr-4.text:10001010arg_0=dwordptr8.text:10001010arg_4=dwordptr0Ch.text:10001010arg_8=dwordptr10h這些標示有什么用呢?我的猜想如下:.text:10001010var_44=byteptr-44h這一條標示表明“一般變量棧空間”的大小為44h..text:10001010var_4=dwordptr-4這一條表明什么呢?暫時不清楚剩下的三行.text:10001010arg_0=dwordptr8.text:10001010arg_4=dwordptr0Ch.text:10001010arg_8=dwordptr10h表明參數(shù)有三個,依次放在[ebp+arg_0],[ebp+arg_4],[ebp+arg_8]里面。(ebp就是最初的esp)也不知道上面的猜測對不對。僅當一種猜測吧!接下來我們看看hexray給我們轉(zhuǎn)換的C代碼int__cdeclinitstr_0(inta1,inta2,inta3){intresult;//eax@2intv4;//[sp+4Ch][bp-4h]@3if(a3>=a1){v4=0;while(v4<a1){*(_BYTE*)(v4+a2)=(_BYTE)v4+1;++v4;}result=a2;}else{result=0;}returnresult;}利用上面的經(jīng)驗,我們很容易將參數(shù)inta2改寫成char*a2,其他的就不說多說了,注意一點,原來的初始化代碼,如第一個例子中的“memset(&v4,-858993460,0x44u)“這次并沒有出來,不過這關(guān)系不大。暫且就到這里吧!技術(shù)含量很低,但愿對初學者有點用處,就倍感欣慰了這里附上一句從名叫Boxer的作者所寫的一片文章中摘抄下來的話“一般而言,ss:[ebp+4]處為返回地址ss:[ebp+8]處為第一個參數(shù)值(這里是a),ss:[ebp+0Ch]處為第二個參數(shù)(這里是b,這里8+4=12=0Ch)ss:[ebp-4]處為第一個局部變量(這里是c),ss:[ebp]處為上一層EBP值ebp和函數(shù)返回值是32位,所以占4個字節(jié)“附送編程必備之素質(zhì)在新手入門編程之前小編想給大家一些關(guān)于學習編程的建議。很多零基礎(chǔ)非計算機科班出身的初學者擔
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 北海藝術(shù)設(shè)計學院《建筑構(gòu)造技術(shù)》2023-2024學年第二學期期末試卷
- 華北科技學院《班級活動及其創(chuàng)新》2023-2024學年第二學期期末試卷
- 河南工程學院《景觀設(shè)計》2023-2024學年第二學期期末試卷
- 四川省廣元天立學校2025年高三3月大聯(lián)考生物試題含解析
- 廣東新安職業(yè)技術(shù)學院《特種膠黏劑》2023-2024學年第二學期期末試卷
- 2倉儲作業(yè)管理
- 陜西經(jīng)濟管理職業(yè)技術(shù)學院《裝配式建筑技術(shù)及應(yīng)用》2023-2024學年第二學期期末試卷
- 大同市新榮區(qū)2025年五年級數(shù)學第二學期期末綜合測試模擬試題含答案
- 甘肅有色冶金職業(yè)技術(shù)學院《綜合日語(Ⅲ)(上)》2023-2024學年第二學期期末試卷
- 青島幼兒師范高等??茖W?!墩Z言翻譯實踐》2023-2024學年第二學期期末試卷
- 2022年鹽城市交通投資建設(shè)控股集團有限公司招聘筆試真題
- 招標工作管理制度
- 盟史簡介12.10.18課件
- 控制性詳細規(guī)劃技術(shù)路線(圖文)
- 加臭機加臭作業(yè)風險點辨識及控制措施表JSA
- 第四節(jié)道亨slw2d架空送電線路評斷面處理及定位設(shè)計系統(tǒng)部分操作說明
- 常用漢字3000個按使用頻率排序
- GB/T 3860-2009文獻主題標引規(guī)則
- GB/T 2912.3-2009紡織品甲醛的測定第3部分:高效液相色譜法
- 詩詞大會訓練題庫-十二宮格課件
- 胚胎工程的應(yīng)用及前景說課課件
評論
0/150
提交評論