360检测杀软分析
------------看雪 elianmeng
要求:
知道这个功能是由AVCheck.dll 做的,那么就主要分析这个dll了
本次分析 很累,因为大量的类交叉,但是我还是坚持给代码看了一遍了,
步骤:
第一步:想办法让我们可以调试这个dll
第二步:分析360里面一个可用的函数
第三步:一静一动开始分析
第四步:360识别杀软库的代码解码
第五步:获取本地软件列表替换%guid%
第六步:Fileexist,fileversion,regvalueexist,regvalue,regkeyexist,osversionsp,productcodeexist,regkeyfileexist,regpathaddfile,fileisnotexist,regkeyisnotexist,regkeyvalueequal,处理
第七步:自己写代码调用360库进行查杀软(未实现)
第一步:od加载dll方法:
看他导出函数只有一个InitAntiVirusScanEng 函数 没有办法进去看看吧
.text:100014BB
.text:100014BB loc_100014BB: ; CODE XREF: InitAntiVirusScanEng+24 j
.text:100014BB mov dword ptr [eax+4], offset InitAntiVirusScanEng
.text:100014C2 mov dword ptr [eax+8], offset sub_1000100F
.text:100014C9 mov dword ptr [eax+0Ch], offset sub_100010B1
.text:100014D0 mov dword ptr [eax+10h], offset FileContent1
.text:100014D7 mov dword ptr [eax+14h], offset sub_1000120D
.text:100014DE mov dword ptr [eax+18h], offset sub_1000126D
.text:100014E5 mov dword ptr [eax+1Ch], offset sub_100012CB
.text:100014EC mov dword ptr [eax+20h], offset IsFindSoft
.text:100014F3 mov dword ptr [eax+28h], offset sub_10001385
.text:100014FA mov dword ptr [eax+24h], offset sub_10001397
.text:10001501 mov dword ptr [eax+2Ch], offset sub_100013A9
.text:10001508 mov dword ptr [eax+30h], offset sub_10001129
.text:1000150F mov dword ptr [eax+34h], offset sub_100013BB
.text:10001516 mov dword ptr [eax+38h], offset sub_1000141B
初步认为 让让你传入一个类的指针进去,紧着他给类的成员函数帮你填写上
但是我怎么知道类的结构啊
无所谓啊 定义一个0x100的buffer 传进去给他当类吧
进过ida慢慢查看 终于找到一个和我们要的内容相关的函数
.text:100014EC mov dword ptr [eax+20h], offset IsFindSoft
这个 ,好的 我们现在开始写代码调试这个函数,得到我们要的秘密
我们打开vs2008 写个程序 让程序调用dll 吧
如下是我的测试代码
#include <Windows.h>
int __cdecl IsFindSoft (LPVOID lpClass, int iIndex, int, int, int)
{
return 0;
}
void main()
{
LoadLibrary(L"AVCheck.dll");
LPVOID lpbuffer1=malloc(0x100);
LPVOID lpBuffer=malloc(0x100);
memset(lpBuffer,0,0x100);
memset(lpbuffer1,0,0x100);
*((DWORD *)((char *)lpBuffer+0x40))=(DWORD)((char *)lpbuffer1+0x40);
IsFindSoft (lpBuffer,2,0xb,NULL,NULL);
}
有的人看到上面的测试 代码好奇,你都没有调进dll函数里面
你说对了,我是这样想的 用od加载后 接着直接给这个地址修改为我们要调试的10001321 地址 就ok了 ,免得麻烦
第一步完成
第二步:
IsFindSoft (lpBuffer,2,0xb,NULL,NULL); 这个就是那个函数的参数
第一个参数是一个类
第二个参数是:杀软的大类
第三个参数:是大类中的小类
后面二个参数 没有用
这个函数测试成功的前提是:你要安装eset 安全 5 杀毒软件
这个函数就返回true
好的 第二步分析完成
现在进行第三步:
开始调试
原理分析:
360这个dll由于用c++写的 类的一堆
所以 我只列出关键地方啊
进入sub_1000BC5E 这个函数
接着进入
.text:10009985 ; int __stdcall ParserDat(int *Error)
.text:10009985 ParserDat proc near ; CODE XREF: sub_10009A9C+4C p
.text:10009985 ; sub_1000BC5E+22 p
.text:10009985
.text:10009985 strFilePath = byte ptr -428h
.text:10009985 var_18 = byte ptr -18h
.text:10009985 dwResult = dword ptr -10h
.text:10009985 var_C = dword ptr -0Ch
.text:10009985 var_4 = dword ptr -4
.text:10009985 Error = dword ptr 8
.text:10009985
.text:10009985 mov eax, offset sub_1001A700
.text:1000998A call __EH_prolog
.text:1000998F sub esp, 41Ch
这个函数
再进入sub_10008646 函数
再进入
text:100091B5 ; int __stdcall GetOrgXml(LPCWSTR hFile)
.text:100091B5 GetOrgXml proc near ; CODE XREF: sub_10008646+77 p
.text:100091B5
.text:100091B5 var_2C = byte ptr -2Ch
.text:100091B5 var_20 = byte ptr -20h
.text:100091B5 NumberOfBytesRead= dword ptr -14h
.text:100091B5 var_10 = dword ptr -10h
.text:100091B5 var_C = dword ptr -0Ch
.text:100091B5 var_4 = dword ptr -4
.text:100091B5 hFile = dword ptr 8
.text:100091B5
函数
现在我们可以到他是怎么紧密他的那个杀软库的
看关键代码
.text:10009296 DecryptStr proc near ; CODE XREF: GetOrgXml+A6 p
.text:10009296
.text:10009296 arg_0 = dword ptr 4
.text:10009296 arg_4 = dword ptr 8
.text:10009296
.text:10009296 mov ecx, [esp+arg_4]
.text:1000929A test ecx, ecx
.text:1000929C jle short locret_100092AC
.text:1000929E mov eax, [esp+arg_0]
.text:100092A2
.text:100092A2 loc_100092A2: ; CODE XREF: DecryptStr+14 j
.text:100092A2 mov dl, [eax]
.text:100092A4 not dl
.text:100092A6 mov [eax], dl
.text:100092A8 inc eax
.text:100092A9 dec ecx
.text:100092AA jnz short loc_100092A2
.text:100092AC
.text:100092AC locret_100092AC: ; CODE XREF: DecryptStr+6 j
.text:100092AC retn
.text:100092AC DecryptStr endp
呵呵 看到了吧
第四步:
好的 我们现在写代码来还原一下360的杀软库
#include <Windows.h>
void main()
{
HANDLE hFileRead=INVALID_HANDLE_VALUE;
DWORD dwRead=0;
char * lpReadBuffer=NULL;
HANDLE hFileWrite=INVALID_HANDLE_VALUE;
DWORD dwSize=0;
hFileRead=CreateFile(L"d:\\AVLib.dat",FILE_ALL_ACCESS,NULL,NULL,OPEN_ALWAYS,NULL,NULL);
if (hFileRead==INVALID_HANDLE_VALUE)
{
return;
}
dwSize=GetFileSize(hFileRead,NULL);
lpReadBuffer=(char *)malloc(dwSize+2);
memset(lpReadBuffer,0,dwSize+2);
ReadFile(hFileRead,lpReadBuffer,dwSize,&dwRead,NULL);
CloseHandle(hFileRead);
for (int i=0;i<dwSize;i++)
{
lpReadBuffer[i]=~lpReadBuffer[i];
}
hFileWrite=CreateFile(L"d:\\123.dat",FILE_ALL_ACCESS,NULL,NULL,OPEN_ALWAYS,NULL,NULL);
if (hFileWrite==INVALID_HANDLE_VALUE)
{
return;
}
WriteFile(hFileWrite,lpReadBuffer,dwSize,&dwRead,NULL);
CloseHandle(hFileWrite);
}
好的 我们看一下还原后的代码
<allcompany Index="66"><company><antivirusid>1</antivirusid><products Index="19"><product><productid>1</productid><type>0</type><productname>卡巴斯基杀毒软件</productname><language></language><productvertype>0103</productvertype><securitycentername>Kaspersky;卡巴斯基</securitycentername><guidname>卡巴斯基;Kaspersky</guidname><getinfopath><installpath></installpath><installdate></installdate><libversion></libversion><lastscan></lastscan><lastupdate></lastupdate><languageid></languageid><uninstallcmd>reguninstall|HKLM|SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall|%guid%;Kaspersky Anti-Virus Personal|UninstallString|</uninstallcmd><mainfilever></mainfilever></getinfopath><expression>V1||V2||V3||V4||V5||V6||V7||V8||V9||V10||V11||V12||V13</expression><conditions><condition>V0|regkeyexist|HKLM|SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall|%guid%|</condition><condition>V1|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\InstalledProducts\Kaspersky Anti-Virus for Workstation|Folder|KWSProd.exe|</condition><condition>V2|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\AVP6\environment|ProductRoot|avp.exe|</condition><condition>V3|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\AVP7\environment|ProductRoot|avp.exe|</condition><condition>V4|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\AVP8\environment|ProductRoot|avp.exe|</condition><condition>V5|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP80\environment|ProductRoot|avp.exe|</condition><condition>V6|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\InstalledProducts\Kaspersky Anti-Virus Personal|Folder|kav.exe|</condition><condition>V7|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP9\environment|ProductRoot|avp.exe|</condition><condition>V8|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\InstalledProducts\Kaspersky Anti-Virus for Workstation|Folder|avpcc.exe|</condition><condition>V9|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP12\environment|ProductRoot|avp.exe|</condition><condition>V10|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP11\environment|ProductRoot|avp.exe|</condition><condition>V11|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP13\environment|ProductRoot|avp.exe|</condition><condition>V12|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP14\environment|ProductRoot|avp.exe|</condition><condition>V13|regkeyfileexist|HKLM|SOFTWARE\KasperskyLab\protected\AVP15
简单的截个图
好的 这个搞定了 现在还有一个问题
<installdate>reginstalldate|HKLM|SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%guid%|InstallDate|</installdate><libversion></libversion><lastscan></lastscan><lastupdate></lastupdate><languageid>reglanguageid|HKLM|SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%guid%|Language|</languageid><uninstallcmd>reguninstall|HKLM|SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\%guid%
看这里 有个%guid% 对吧
第五步:获取本地软件列表替换%guid%
这个360是怎么知道的啊
下面就代码看一下
.text:1000488A ; int __stdcall GetInstallList(LPVOID lpClass)
.text:1000488A GetInstallList proc near ; CODE XREF: sub_1000A26B+3C p
.text:1000488A ; sub_1000BC5E+4F p
.text:1000488A
.text:1000488A pszSubKey = byte ptr -0BE0h
.text:1000488A var_7D0 = byte ptr -7D0h
.text:1000488A var_7CC = byte ptr -7CCh
.text:1000488A Args = byte ptr -5BCh
.text:1000488A hkey = dword ptr -3B4h
.text:1000488A lpUninstallStr = byte ptr -3B0h
.text:1000488A strGuid = byte ptr -1A8h
.text:1000488A lpszInstallName = byte ptr -0E0h
.text:1000488A dwSize = dword ptr -18h
.text:1000488A dwIndex = dword ptr -14h
.text:1000488A var_10 = byte ptr -10h
.text:1000488A var_C = dword ptr -0Ch
.text:1000488A var_4 = dword ptr -4
.text:1000488A lpClass = dword ptr 8
.text:1000488A
.text:1000488A mov eax, offset sub_1001A2BF
.text:1000488F call __EH_prolog
.text:10004894 sub esp, 0BD4h
.text:1000489A mov ecx, [ebp+lpClass]
.text:1000489D push ebx
.text:1000489E push esi
.text:1000489F push edi
.text:100048A0 call sub_1000C712
.text:100048A5 xor ebx, ebx
.text:100048A7 xor esi, esi
.text:100048A9
.text:100048A9 loc_100048A9: ; CODE XREF: GetInstallList+8E j
.text:100048A9 push 190h ; Size
.text:100048AE lea eax, [ebp+strGuid]
.text:100048B4 push ebx ; Val
.text:100048B5 push eax ; Dst
.text:100048B6 call _memset
.text:100048BB add esp, 0Ch
.text:100048BE call IsLoadLibrary
.text:100048C3 mov eax, [eax+4] ; MsiEnumProductsW
.text:100048C6 cmp eax, ebx
.text:100048C8 jz short loc_1000491A
.text:100048CA lea ecx, [ebp+strGuid]
.text:100048D0 push ecx
.text:100048D1 push esi
.text:100048D2 call eax
.text:100048D4 cmp eax, ebx
.text:100048D6 jnz short loc_1000491A
.text:100048D8 mov [ebp+dwSize], 64h
.text:100048DF call IsLoadLibrary
.text:100048E4 mov eax, [eax+8] ; MsiGetProductInfoW
.text:100048E7 cmp eax, ebx
.text:100048E9 jz short loc_10004917
.text:100048EB lea ecx, [ebp+dwSize]
.text:100048EE push ecx
.text:100048EF lea ecx, [ebp+lpszInstallName]
.text:100048F5 push ecx
.text:100048F6 lea ecx, [ebp+strGuid]
.text:100048FC push offset aInstalledprodu ; "InstalledProductName"
.text:10004901 push ecx
.text:10004902 call eax
.text:10004904 cmp eax, ebx
.text:10004906 jnz short loc_10004917
.text:10004908 mov ecx, [ebp+lpClass]
.text:1000490B lea eax, [ebp+strGuid]
.text:10004911 push eax ; lpszGuid
.text:10004912 call WriteGuid
.text:10004917
.text:10004917 loc_10004917: ; CODE XREF: GetInstallList+5F j
.text:10004917 ; GetInstallList+7C j
.text:10004917 inc esi
.text:10004918 jmp short loc_100048A9
.text:1000491A ; ---------------------------------------------------------------------------
.text:1000491A
.text:1000491A loc_1000491A: ; CODE XREF: GetInstallList+3E j
.text:1000491A ; GetInstallList+4C j
.text:1000491A push offset szStr ; "SOFTWARE\\Microsoft\\Windows\\CurrentVersi"...
.text:1000491F push 80000002h ; hKey
.text:10004924 lea ecx, [ebp+var_7D0]
.text:1000492A call sub_10002B98
.text:1000492F mov ecx, 103h
.text:10004934 xor eax, eax
.text:10004936 lea edi, [ebp+pszSubKey+2]
.text:1000493C mov word ptr [ebp+pszSubKey], bx
.text:10004943 rep stosd
.text:10004945 stosw
.text:10004947 mov ecx, 81h
.text:1000494C xor eax, eax
.text:1000494E lea edi, [ebp+lpUninstallStr+2]
.text:10004954 mov word ptr [ebp+lpUninstallStr], bx
.text:1000495B rep stosd
.text:1000495D lea ecx, [ebp+var_10]
.text:10004960 mov [ebp+var_4], ebx
.text:10004963 stosw
.text:10004965 call sub_10009760
.text:1000496A push ebx ; dwIndex
.text:1000496B lea ecx, [ebp+var_7D0]
.text:10004971 mov byte ptr [ebp+var_4], 1
.text:10004975 mov [ebp+dwIndex], ebx
.text:10004978 call PNFEnumReg
.text:1000497D test eax, eax
.text:1000497F jz loc_10004A96
.text:10004985 mov edi, ds:StrChrW
.text:1000498B mov esi, 0C8h
.text:10004990
.text:10004990 loc_10004990: ; CODE XREF: GetInstallList+206 j
.text:10004990 push 190h ; Size
.text:10004995 lea eax, [ebp+strGuid]
.text:1000499B push ebx ; Val
.text:1000499C push eax ; Dst
.text:1000499D call _memset
.text:100049A2 add esp, 0Ch
.text:100049A5 lea eax, [ebp+var_7CC]
.text:100049AB push eax
.text:100049AC lea eax, [ebp+Args]
.text:100049B2 push eax ; Args
.text:100049B3 push offset aSSInstallprope ; "%s\\%s\\InstallProperties"
.text:100049B8 lea eax, [ebp+pszSubKey]
.text:100049BE push 410h ; int
.text:100049C3 push eax ; Dest
.text:100049C4 call PNFsprintf
.text:100049C9 add esp, 14h
.text:100049CC lea eax, [ebp+lpszInstallName]
.text:100049D2 lea ecx, [ebp+var_10]
.text:100049D5 push esi ; pcbData
.text:100049D6 push eax ; pvData
.text:100049D7 push 1 ; pdwType
.text:100049D9 lea eax, [ebp+pszSubKey]
.text:100049DF push offset aDisplayname ; "DisplayName"
.text:100049E4 push eax ; pszSubKey
.text:100049E5 push [ebp+hkey] ; hkey
.text:100049EB call GetRegData
.text:100049F0 test eax, eax
.text:100049F2 jz loc_10004A7D
.text:100049F8 lea eax, [ebp+lpUninstallStr]
.text:100049FE push 208h ; pcbData
.text:10004A03 push eax ; pvData
.text:10004A04 push 1 ; pdwType
.text:10004A06 lea eax, [ebp+pszSubKey]
.text:10004A0C push offset aUninstallstrin ; "UninstallString"
.text:10004A11 push eax ; pszSubKey
.text:10004A12 lea ecx, [ebp+var_10]
.text:10004A15 push [ebp+hkey] ; hkey
.text:10004A1B call GetRegData
.text:10004A20 test eax, eax
.text:10004A22 jz short loc_10004A7D
.text:10004A24 lea eax, [ebp+lpUninstallStr]
.text:10004A2A push '{'
.text:10004A2C push eax
.text:10004A2D call edi ; StrChrW
.text:10004A2F mov ebx, eax
.text:10004A31 lea eax, [ebp+lpUninstallStr]
.text:10004A37 push '}'
.text:10004A39 push eax
.text:10004A3A call edi ; StrChrW
.text:10004A3C test ebx, ebx
.text:10004A3E jz short loc_10004A7B
.text:10004A40 test eax, eax
.text:10004A42 jz short loc_10004A7B
.text:10004A44 sub eax, ebx
.text:10004A46 sar eax, 1
.text:10004A48 lea eax, [eax+eax+2]
.text:10004A4C push eax
.text:10004A4D push ebx
.text:10004A4E lea eax, [ebp+strGuid]
.text:10004A54 push esi
.text:10004A55 push eax
.text:10004A56 call PNFstrcpy
.text:10004A5B lea eax, [ebp+strGuid]
.text:10004A61 push eax ; Str
.text:10004A62 call _wcslen
.text:10004A67 test eax, eax
.text:10004A69 pop ecx
.text:10004A6A jz short loc_10004A7B
.text:10004A6C mov ecx, [ebp+lpClass]
.text:10004A6F lea eax, [ebp+strGuid]
.text:10004A75 push eax ; lpszGuid
.text:10004A76 call WriteGuid
.text:10004A7B
.text:10004A7B loc_10004A7B: ; CODE XREF: GetInstallList+1B4 j
.text:10004A7B ; GetInstallList+1B8 j ...
.text:10004A7B xor ebx, ebx
.text:10004A7D
.text:10004A7D loc_10004A7D: ; CODE XREF: GetInstallList+168 j
.text:10004A7D ; GetInstallList+198 j
.text:10004A7D inc [ebp+dwIndex]
.text:10004A80 lea ecx, [ebp+var_7D0]
.text:10004A86 push [ebp+dwIndex] ; dwIndex
.text:10004A89 call PNFEnumReg
.text:10004A8E test eax, eax
.text:10004A90 jnz loc_10004990
.text:10004A96
.text:10004A96 loc_10004A96: ; CODE XREF: GetInstallList+F5 j
.text:10004A96 and byte ptr [ebp+var_4], 0
.text:10004A9A lea ecx, [ebp+var_10]
.text:10004A9D call sub_10009785
.text:10004AA2 or [ebp+var_4], 0FFFFFFFFh
.text:10004AA6 lea ecx, [ebp+var_7D0]
.text:10004AAC call PNFReg
.text:10004AB1 mov ecx, [ebp+var_C]
.text:10004AB4 pop edi
.text:10004AB5 pop esi
.text:10004AB6 pop ebx
.text:10004AB7 mov large fs:0, ecx
.text:10004ABE leave
.text:10004ABF retn 4
.text:10004ABF GetInstallList endp
.text:10004ABF
.text:10004AC2
.进入这个函数 查看
他先获取本地的杀软列表
他先用MsiEnumProductsW,MsiGetProductInfo 等函数 得到guid和名字 ,再到注册表搜索一遍 就得到一个列表
再和自己的库匹配,再填写%guid%
第六步:
Fileexist,fileversion,regvalueexist,regvalue,regkeyexist,osversionsp,productcodeexist,regkeyfileexist,regpathaddfile,fileisnotexist,regkeyisnotexist,regkeyvalueequal,
处理
其实这个过程 他都放到一个总函数里面
我们进入
.text:10006969 ; int __stdcall ControlCenter(int Str1)
.text:10006969 ControlCenter proc near ; CODE XREF: sub_1000AB9B+8E p
.text:10006969 ; sub_1000BDE0+36 p
.text:10006969
.text:10006969 var_28 = dword ptr -28h
.text:10006969 var_24 = dword ptr -24h
.text:10006969 var_20 = dword ptr -20h
.text:10006969 var_1C = dword ptr -1Ch
.text:10006969 var_18 = dword ptr -18h
.text:10006969 var_14 = dword ptr -14h
.text:10006969 var_10 = dword ptr -10h
.text:10006969 var_C = dword ptr -0Ch
.text:10006969 var_4 = dword ptr -4
.text:10006969 Str1 = dword ptr 8
.text:10006969
.text:10006969 mov eax, offset sub_1001A39C
.text:1000696E call __EH_prolog
.text:10006973 sub esp, 1Ch
.text:10006976 push ebx
.text:10006977 push esi
.text:10006978 mov esi, [ebp+Str1]
.text:1000697B xor ebx, ebx
.text:1000697D push edi
.text:1000697E cmp esi, ebx
.text:10006980 mov edi, ecx
.text:10006982 jnz short loc_1000698B
.text:10006984 xor eax, eax
.text:10006986 jmp loc_10006DC9
.text:1000698B ; ---------------------------------------------------------------------------
.text:1000698B
.text:1000698B loc_1000698B: ; CODE XREF: ControlCenter+19 j
.text:1000698B mov [ebp+var_28], ebx
.text:1000698E mov [ebp+var_24], ebx
.text:10006991 mov [ebp+var_20], ebx
.text:10006994 mov eax, [esi]
.text:10006996 lea ecx, [ebp+var_28]
.text:10006999 push ecx ; int
.text:1000699A push eax ; Src
.text:1000699B mov ecx, edi
.text:1000699D mov [ebp+var_4], ebx
.text:100069A0 call sub_10007787
.text:100069A5 test eax, eax
.text:100069A7 jz loc_10006DBB
.text:100069AD cmp [ebp+var_24], 3
.text:100069B1 jl loc_10006DBB
.text:100069B7 mov eax, [ebp+var_28]
.text:100069BA mov ecx, [eax]
.text:100069BC cmp [ecx-8], ebx
.text:100069BF jz loc_10006DBB
.text:100069C5 mov ecx, [eax+4]
.text:100069C8 cmp [ecx-8], ebx
.text:100069CB jz loc_10006DBB
.text:100069D1 push eax
.text:100069D2 lea ecx, [esi+4]
.text:100069D5 call sub_10002124
.text:100069DA mov eax, [ebp+var_28]
.text:100069DD push offset aFileexist ; "fileexist"
.text:100069E2 mov eax, [eax+4]
.text:100069E5 push eax ; Str1
这个函数就一目了然了
余下就是单独分析了
后面就没有什么了,我就不贴代码了
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!