




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
選題要求:在Linux內(nèi)核中增加一個(gè)系統(tǒng)調(diào)用,并編寫對應(yīng)的linux應(yīng)用程序。利用該系統(tǒng)調(diào)用能夠遍歷系統(tǒng)當(dāng)前所有進(jìn)程的任務(wù)描述符,并按進(jìn)程父子關(guān)系將這些描述符所對應(yīng)的進(jìn)程id(PID)組織成樹形結(jié)構(gòu)顯示。
目錄TOC\h\z\t"一,1,1,2,1.1,3,1.1.1,4"一.程序的主要設(shè)計(jì)思路,實(shí)現(xiàn)方式 11.1添加系統(tǒng)調(diào)用的兩種方法 11.1.1編譯內(nèi)核法 11.1.2內(nèi)核模塊法 11.2程序的主要設(shè)計(jì)思路 11.3環(huán)境 2二.程序的模塊劃分,及對每個(gè)模塊的說明 22.1通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用 22.1.1修改系統(tǒng)調(diào)用的模塊 22.1.2獲取sys_call_table的地址 22.1.3清除內(nèi)存區(qū)域的寫保護(hù) 32.2編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用 42.2.1內(nèi)核的初始化函數(shù) 42.2.2自己的系統(tǒng)調(diào)用服務(wù)例程 42.2.3移除內(nèi)核模塊時(shí),將原有的系統(tǒng)調(diào)用進(jìn)行還原 62.2.4模塊注冊相關(guān) 62.3編寫用戶態(tài)的測試程序 62.4編寫Makefile文件 7三.所遇到的問題及解決的方法 83.1進(jìn)程個(gè)數(shù)確定 83.2被更改的系統(tǒng)調(diào)用號(hào)的選擇 83.3獲取系統(tǒng)調(diào)用表的地址 83.4內(nèi)核和用戶態(tài)數(shù)據(jù)交換 8四.程序運(yùn)行結(jié)果及使用說明 84.1將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中 84.2通過dmesg查看輸出信息是否正確 94.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖) 94.4卸載自定義模塊 10五.附錄 115.1內(nèi)核模塊程序hello.c 115.2測試程序hello_test.c 145.3Makefile文件 14
一.程序的主要設(shè)計(jì)思路,實(shí)現(xiàn)方式1.1添加系統(tǒng)調(diào)用的兩種方法1.1.1編譯內(nèi)核法編寫好源碼之后修改內(nèi)核的系統(tǒng)調(diào)用庫函數(shù)/usr/include/asm-generic/unistd.h,在這里面可以使用在syscall_table中沒有用到的223號(hào)添加系統(tǒng)調(diào)用號(hào),讓系統(tǒng)根據(jù)這個(gè)號(hào),去找到syscall_table中的相應(yīng)表項(xiàng)。在/arch/x86/kernel/syscall_table_32.s文件中添加系統(tǒng)調(diào)用號(hào)和調(diào)用函數(shù)的對應(yīng)關(guān)系接著就是my_syscall的實(shí)現(xiàn)了,在這里有兩種方法:第一種方法是在kernel下自己新建一個(gè)目錄添加自己的文件,但是要編寫Makefile,而且要修改全局的Makefile。第二種比較簡便的方法是,在kernel/sys.c中添加自己的服務(wù)函數(shù),這樣子不用修改Makefile.以上準(zhǔn)備工作做完之后,然后就要進(jìn)行編譯內(nèi)核了,以下是編譯內(nèi)核的一個(gè)過程1.makemenuconfig(使用圖形化的工具,更新.config文件)2.make-j3bzImage(編譯,-j3指的是同時(shí)使用3個(gè)cpu來編譯,bzImage指的是更新grub,以便重新引導(dǎo))3.makemodules(對模塊進(jìn)行編譯)4.makemodules_install(安裝編譯好的模塊)5.depmod(進(jìn)行依賴關(guān)系的處理)6.reboot(重啟看到自己編譯好的內(nèi)核)1.1.2內(nèi)核模塊法內(nèi)核模塊可以作為獨(dú)立程序來編譯的函數(shù)和數(shù)據(jù)類型的集合。之所以提供模塊機(jī)制,是因?yàn)長inux本身是一個(gè)單內(nèi)核。單內(nèi)核由于所有內(nèi)容都集成在一起,效率很高,但可擴(kuò)展性和可維護(hù)性相對較差,模塊機(jī)制可以彌補(bǔ)這一缺陷。Linux模塊可以通過靜態(tài)或動(dòng)態(tài)的方法加載到內(nèi)核空間,靜態(tài)加載是指在內(nèi)核啟動(dòng)過程中加載;動(dòng)態(tài)加載是指在內(nèi)核運(yùn)行的過程中隨時(shí)加載。一個(gè)模塊被加載到內(nèi)核中時(shí),就成為內(nèi)核代碼的一部分。模塊加載入系統(tǒng)時(shí),系統(tǒng)修改內(nèi)核中的符號(hào)表,將新加載的模塊提供的資源和符號(hào)添加到內(nèi)核符號(hào)表中,以便模塊間通信。這種方法是采用系統(tǒng)調(diào)用攔截的一種方式,改變某一個(gè)系統(tǒng)調(diào)用號(hào)對應(yīng)的服務(wù)程序?yàn)槲覀冏约旱木帉懙某绦?,從而相?dāng)于添加了我們自己的系統(tǒng)調(diào)用。下面的內(nèi)容,會(huì)詳述用內(nèi)核模塊法實(shí)現(xiàn)目標(biāo)的過程。1.2程序的主要設(shè)計(jì)思路程序分三部分,一部分是通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用,二是編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用,最后是編寫用戶態(tài)的測試程序。1.3環(huán)境Ubuntu14.04+3.13.0內(nèi)核版本內(nèi)核版本:二.程序的模塊劃分,及對每個(gè)模塊的說明2.1通過內(nèi)核模塊實(shí)現(xiàn)添加系統(tǒng)調(diào)用這種方法其實(shí)是系統(tǒng)調(diào)用攔截的實(shí)現(xiàn)。系統(tǒng)調(diào)用服務(wù)程序的地址是放在sys_call_table中通過系統(tǒng)調(diào)用號(hào)定位到具體的系統(tǒng)調(diào)用地址,那么我們通過編寫內(nèi)核模塊來修改sys_call_table中的系統(tǒng)調(diào)用的地址為我們自己定義的函數(shù)的地址,就可以實(shí)現(xiàn)系統(tǒng)調(diào)用的攔截。通過模塊加載時(shí),將系統(tǒng)調(diào)用表里面的那個(gè)系統(tǒng)調(diào)用號(hào)的那個(gè)系統(tǒng)調(diào)用號(hào)對應(yīng)的系統(tǒng)調(diào)用服務(wù)例程改為我們自己實(shí)現(xiàn)的系統(tǒng)歷程函數(shù)地址。2.1.1修改系統(tǒng)調(diào)用的模塊在/usr/include/i386-linux-gnu/asm/unistd_32.h文件中查看系統(tǒng)調(diào)用序號(hào):找到結(jié)果(部分截圖):可以看到,222號(hào)和223號(hào)系統(tǒng)調(diào)用是空的,因此選取223作為新的系統(tǒng)調(diào)用號(hào)。2.1.2獲取sys_call_table的地址在/boot/System.map-3.16.0-30-generic查看系統(tǒng)調(diào)用表的內(nèi)存地址:找到結(jié)果:為0xc165e1402.1.3清除內(nèi)存區(qū)域的寫保護(hù)得到了sys_call_table的地址,該符號(hào)對應(yīng)的內(nèi)存區(qū)域是只讀的。所以我們要修改它,必須對它進(jìn)行清除寫保護(hù),這里介紹兩種方法:第一種方法:我們知道控制寄存器cr0的第16位是寫保護(hù)位。cr0的第16位置為了禁止超級(jí)權(quán)限,若清零了則允許超級(jí)權(quán)限往內(nèi)核中寫入數(shù)據(jù),這樣我們可以再寫入之前,將那一位清零,使我們可以寫入。然后寫完后,又將那一位復(fù)原就行了。//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)
unsignedintclear_and_return_cr0(void)//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)
unsignedintclear_and_return_cr0(void){unsignedintcr0=0;unsignedintret;asm("movl%%cr0,%%eax":"=a"(cr0));//將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中ret=cr0;cr0&=0xfffeffff;//將cr0變量的第17位清0asm("movl%%eax,%%cr0"::"a"(cr0));//將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中returnret;}
//讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中改變內(nèi)核地址空間參數(shù)
voidsetback_cr0(unsignedintval){
asmvolatile("movl%%eax,%%cr0"::"a"(val));}第二種方法:通過設(shè)置虛擬地址對應(yīng)的也表項(xiàng)的讀寫屬性來設(shè)置。intmake_rw(unsignedlongaddress){unsignedintlevel;pte_t*pte=lookup_address(address,&level);//查找虛擬地址所在的頁表地址if(pte->pte&~_PAGE_RW)//設(shè)置頁表讀寫屬性pte->pte|=_PAGE_RW;return0;}intmake_ro(unsignedlongaddress){unsignedintlevel;pte_t*pte=lookup_address(address,&level);pte->pte&=~_PAGE_RW;//設(shè)置只讀屬性return0;}2.2編寫系統(tǒng)調(diào)用指定自己的系統(tǒng)調(diào)用2.2.1內(nèi)核的初始化函數(shù)此函數(shù)內(nèi)采用的是2.1.3中的第一種方法。staticint__initinit_addsyscall(void)
{
printk("hello,yinyukernel\n");
//獲取系統(tǒng)調(diào)用服務(wù)首地址
sys_call_table=(unsignedlong*)sys_call_table_address;printk("%x\n",sys_call_table);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用
anything_saved=(int(*)(void))(sys_call_table[my_syscall_num]);
//使內(nèi)核地址空間可寫orig_cr0=clear_and_return_cr0();//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用
sys_call_table[my_syscall_num]=(unsignedlong)&sys_mycall;
//使內(nèi)核地址空間不可寫setback_cr0(orig_cr0);
return0;}2.2.2自己的系統(tǒng)調(diào)用服務(wù)例程部分一:創(chuàng)建進(jìn)程樹voidprocesstree(structtask_struct*p,intb);結(jié)果需要以樹狀形式展示所有進(jìn)程的父子關(guān)系。為此,我們定義processtree()遞歸函數(shù)來訪問遍歷,并且將結(jié)果存儲(chǔ)在數(shù)組中,以便提供給用戶態(tài)訪問。voidprocesstree(structtask_struct*p,intb)//創(chuàng)建進(jìn)程樹(進(jìn)程,深度)
{
structlist_head*l;
a[counter].pid=p->pid;
a[counter].depth=b;
counter++;
for(l=p->children.next;l!=&(p->children);l=l->next)
{
structtask_struct*t=list_entry(l,structtask_struct,sibling);
processtree(t,b+1);
}}其中,特別使用了宏:#definelist_entry(ptr,type,member)/((type*)((char*)(ptr)-(unsignedlong)(&((type*)0)->member)))ptr是指向list_head類型鏈表的指針;type為一個(gè)結(jié)構(gòu);member為結(jié)構(gòu)type中的一個(gè)域,類型為list_head;這個(gè)宏返回指向type結(jié)構(gòu)的指針。目的:從一個(gè)結(jié)構(gòu)的成員指針找到其容器的指針部分二:創(chuàng)建自己的系統(tǒng)調(diào)用服務(wù)asmlinkagelongsys_mycall(char__user*buf);在sys_mycall()中,從當(dāng)前進(jìn)程開始,遞歸調(diào)用processtree()函數(shù),將進(jìn)程信息存儲(chǔ)在數(shù)組中。然后利用copy_to_user函數(shù)將內(nèi)核信息傳遞給用戶態(tài)下,用戶態(tài)下的測試程序?qū)Y(jié)果進(jìn)行展示。asmlinkagelongsys_mycall(char__user*buf)
{
intb=0;
structtask_struct*p;
printk("Thisisyinyu_syscall!\n");
for(p=current;p!=&init_task;p=p->parent);
processtree(p,b);
if(copy_to_user((structprocess*)buf,a,512*sizeof(structprocess)))return-EFAULT;
else
returnsizeof(a);
}2.2.3移除內(nèi)核模塊時(shí),將原有的系統(tǒng)調(diào)用進(jìn)行還原staticvoid__exitexit_addsyscall(void)
{
//設(shè)置cr0中對sys_call_table的更改權(quán)限。
orig_cr0=clear_and_return_cr0();
//恢復(fù)原有的中斷向量表中的函數(shù)指針的值。
sys_call_table[my_syscall_num]=(unsignedlong)anything_saved;
//恢復(fù)原有的cr0的值
setback_cr0(orig_cr0);
printk("callyinyuexit\n");}2.2.4模塊注冊相關(guān)模塊構(gòu)造函數(shù)module_init(init_addsyscall);執(zhí)行insmod或modprobe指令加載內(nèi)核模塊時(shí)會(huì)調(diào)用的初始化函數(shù)。函數(shù)原型必須是module_init(),內(nèi)是函數(shù)指針。模塊析構(gòu)函數(shù)module_exit(exit_addsyscall);執(zhí)行rmmod指令卸載模塊時(shí)調(diào)用的函數(shù)。函數(shù)原型是module_exit();模塊許可聲明MODULE_LICENSE("GPL");函數(shù)原型是MODULE_LICENSE(),告訴內(nèi)核程序使用的許可證,不然在加載時(shí)它會(huì)提示該模塊污染內(nèi)核。一般會(huì)寫GPL。2.3編寫用戶態(tài)的測試程序#include<linux/unistd.h>
#include<syscall.h>//asmlinkage
#include<sys/types.h>
#include<stdio.h>
structprocess
{
intpid;
intdepth;
};
structprocessa[512];
intmain()
{
inti,j;
//在內(nèi)核中將223本來對應(yīng)的系統(tǒng)調(diào)用,臨時(shí)鏈到我們自定義的sys_mycall()中。通過該系統(tǒng)調(diào)用后獲得數(shù)組a
printf("theresultis:%d\n",syscall(223,&a));
for(i=0;i<512;i++)
{
for(j=0;j<a[i].depth;j++)
printf("|-");
printf("%d\n",a[i].pid);
if(a[i+1].pid==0)
break;
}
return0;}2.4編寫Makefile文件KVERS=$(shelluname-r)
#Kernelmodules
obj-m+=hello.o
#Specifyflagsforthemodulecompilation.
#EXTRA_CFLAGS=-g-O0
build:kernel_modulesuser_test
kernel_modules:
make-C/lib/modules/$(KVERS)/buildM=$(CURDIR)modules
user_test:
gcc-ohello_testhello_test.c
clean:make-C/lib/modules/$(KVERS)/buildM=$(CURDIR)clean三.所遇到的問題及解決的方法3.1進(jìn)程個(gè)數(shù)確定系統(tǒng)可運(yùn)行的最大進(jìn)程數(shù),通過ulimit–u查看有7863個(gè)我們通過ps–ef|wc–l命令實(shí)際查看當(dāng)前運(yùn)行進(jìn)程數(shù)量為191個(gè)存儲(chǔ)進(jìn)程信息的數(shù)組大小為512是夠用的。3.2被更改的系統(tǒng)調(diào)用號(hào)的選擇見2.1.1。3.3獲取系統(tǒng)調(diào)用表的地址見2.1.2。3.4內(nèi)核和用戶態(tài)數(shù)據(jù)交換我們在內(nèi)核模塊程序中,將進(jìn)程遍歷信息存儲(chǔ)在數(shù)組中,然后需要將其傳遞給用戶態(tài)下。采用copy_from_user()和copy_to_user()這兩個(gè)函數(shù),這兩個(gè)函數(shù)負(fù)責(zé)在用戶空間和內(nèi)核空間傳遞數(shù)據(jù)。因此我們在測試程序中,將空數(shù)組a的地址作為參數(shù)傳遞給內(nèi)核模塊程序,在內(nèi)核中使用copy_to_user()函數(shù)將內(nèi)核中的數(shù)組信息傳遞給用戶態(tài)下的地址。四.程序運(yùn)行結(jié)果及使用說明4.1將編譯出來的內(nèi)核模塊hello.ko加載到內(nèi)核中加載內(nèi)核模塊命令:insmodhello.ko4.2通過dmesg查看輸出信息是否正確4.3運(yùn)行測試程序,輸出樹狀打印結(jié)果(部分結(jié)果截圖)4.4卸載自定義模塊卸載內(nèi)核模塊命令:insmodhello.ko
五.附錄5.1內(nèi)核模塊程序hello.c#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>//list_head
#include<linux/unistd.h>
#include<asm/uaccess.h>
#include<linux/sched.h>//task_struct
#definemy_syscall_num223
#definesys_call_table_address0xc165e140
staticintcounter=0;
structprocess
{
intpid;
intdepth;
};
structprocessa[512];
unsignedintclear_and_return_cr0(void);
voidsetback_cr0(unsignedintval);
asmlinkagelongsys_mycall(char__user*buf);intorig_cr0;
unsignedlong*sys_call_table=0;
staticint(*anything_saved)(void);
voidprocesstree(structtask_struct*p,intb)//創(chuàng)建進(jìn)程樹(進(jìn)程,深度)
{
structlist_head*l;
a[counter].pid=p->pid;
a[counter].depth=b;
counter++;
for(l=p->children.next;l!=&(p->children);l=l->next)
{
structtask_struct*t=list_entry(l,structtask_struct,sibling);
processtree(t,b+1);
}
}
unsignedintclear_and_return_cr0(void)//使cr0寄存器的第17位設(shè)置為0(即是內(nèi)核空間可寫)
{
unsignedintcr0=0;
unsignedintret;
asm("movl%%cr0,%%eax":"=a"(cr0));
//將cr0寄存器的值移動(dòng)到eax寄存器中,同時(shí)輸出到cr0變量中
ret=cr0;
cr0&=0xfffeffff;//將cr0變量的第17位清0
asm("movl%%eax,%%cr0"::"a"(cr0));
//將cr0變量的值放入寄存器eax中,并且放入cr0寄存器中
returnret;
}
voidsetback_cr0(unsignedintval)//讀取val的值到eax寄存器,再將eax寄存器的值放入cr0寄存器中改變內(nèi)核地址空間參數(shù)
{
asmvolatile("movl%%eax,%%cr0"::"a"(val));
}
staticint__initinit_addsyscall(void)//保存原來系統(tǒng)調(diào)用表中此地址中的系統(tǒng)調(diào)用
{
printk("hello,yinyukernel\n");
sys_call_table=(unsignedlong*)sys_call_table_address;//獲取系統(tǒng)調(diào)用服務(wù)首地址
printk("%x\n",sys_call_table);
anything_saved=(int(*)(void))(sys_call_table[my_syscall_num]);//保存系統(tǒng)調(diào)用表中的NUM位置上的系統(tǒng)調(diào)用
orig_cr0=clear_and_return_cr0();//使內(nèi)核地址空間可寫
sys_call_table[my_syscall_num]=(unsignedlong)&sys_mycall;//用自己的系統(tǒng)調(diào)用替換NUM位置上的系統(tǒng)調(diào)用
setback_cr0(orig_cr0);//使內(nèi)核地址空間不可寫
return0;
}
asmlinkagelongsys_mycall(char__user*buf)
{
intb=0;
structtask_struct*p;
printk("Thisisyinyu_syscall!\n");
for(p=current;p!=&init_task;p=p->parent);
processtree(p,b);
if(copy_to_user((structprocess*)buf,a,512*sizeof(structprocess)))//將內(nèi)核空間內(nèi)容復(fù)制到用戶空間
return-EFAULT;
else
returnsizeof(a);
}
staticvoid__exitexit_addsyscall(void)
{
//設(shè)置cr0中對sys_call_table的更改權(quán)限。
orig_cr0=clear_and_return_cr0();
//恢復(fù)原有的中斷向量表中的函數(shù)指針的值。
sys_call_table[my_syscall_num]=(unsignedlong)anything_saved;
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 勞動(dòng)合同法在企業(yè)的實(shí)施調(diào)查報(bào)告(2025年版)
- 2025年股權(quán)轉(zhuǎn)讓框架協(xié)議
- 2025年離婚協(xié)議書兩個(gè)小孩模板
- 【單元重點(diǎn)難點(diǎn)】譯林版(三起)英語三年級(jí)上冊Unit-7-單元復(fù)習(xí)(知識(shí)梳理檢測)-(含解析)
- 2025年河北省邢臺(tái)市單招職業(yè)適應(yīng)性測試題庫學(xué)生專用
- 2024年運(yùn)載火箭遙測系統(tǒng)檢測設(shè)備項(xiàng)目資金需求報(bào)告代可行性研究報(bào)告
- 第九章 第2節(jié) 液體的壓強(qiáng)(教學(xué)設(shè)計(jì))2024-2025學(xué)年人教版(2024)物理八年級(jí)下冊
- 2025年??谑袉握新殬I(yè)傾向性測試題庫參考答案
- 2025年廣東省外語藝術(shù)職業(yè)學(xué)院單招職業(yè)適應(yīng)性測試題庫一套
- 《小數(shù)除法-誰打電話時(shí)間長》(教學(xué)設(shè)計(jì))-2024-2025學(xué)年五年級(jí)上冊數(shù)學(xué)北師大版
- 十堰2024年湖北十堰市茅箭區(qū)教育局所屬學(xué)校招聘教師134人筆試歷年典型考題及考點(diǎn)附答案解析
- 《陸上風(fēng)電場工程概算定額》NBT 31010-2019
- 展會(huì)展中營銷方案
- 2024屆遼寧省沈陽市名校中考四?;瘜W(xué)試題含答案解析
- 2024年新高考改革方案政策
- 2024年許昌職業(yè)技術(shù)學(xué)院單招職業(yè)技能測試題庫及答案解析
- 《新媒體創(chuàng)意短視頻制作》課件-運(yùn)動(dòng)短視頻制作關(guān)鍵技術(shù)
- JTGT F20-2015 公路路面基層施工技術(shù)細(xì)則
- 7S培訓(xùn)管理教材課件(-28張)
- 過橋資金計(jì)劃書
- 小學(xué)數(shù)學(xué)計(jì)算練習(xí)-一年級(jí)上學(xué)期口算練習(xí)(600題打印版)
評論
0/150
提交評論