您的位置:首页 > 其它

进程隐藏与进程保护(SSDT Hook 实现)(三)

2017-06-08 11:27 423 查看



文章目录:

                 

1. 引子:

2. 获取当前系统下所有进程:

3. 服务管理(安装,启动,停止,卸载):

4. 应用程序和内核程序通信:

5. 小结:

                

1. 引子:

                         

关于这个 SSDT Hook 实现进程隐藏和进程保护呢,这是最后一篇博文了,

在文章的结尾处呢你可以下载到整个项目的实例程序以及代码,

程序可以在 XP、Server、Win7 上运行的,当然我是说的 32 位操作系统。       

                  

《进程隐藏与进程保护(SSDT Hook 实现)(一)》呢把 SSDT Hook 的原理说得差不多了,

博文地址:http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html

《进程隐藏与进程保护(SSDT Hook 实现)(二)》则把 SSDT Hook 的实现说得差不多了,

博文地址:http://www.cnblogs.com/BoyXiao/archive/2011/09/04/2166596.html

             

这一篇博文介绍的则是在 Ring3 下编写 MFC 应用程序,并且让应用程序与内核程序通信,

即由应用程序将需要隐藏的进程或者是需要保护的进程的 PID 传递给内核程序,

然后在内核程序中就会将传递进来的这个 PID 进行隐藏或者保护 ~

在这里再给出这个应用程序的一张截图:





        

                

2. 获取当前系统下所有进程:

                 

前面提到过,要想获取到系统下的所有进程,有三种方法,

第一种即是使用 ToolHelp 来获取,

第二种则是使用 PSAPI 来获取,

第三种则是使用 ntdll.dll 中的未文档化的 NtQuerySystemInformation 之类的 API 来获取(比较麻烦)。

而在这里我使用最简单的方式,即通过 PSAPI 中的 EnumProcesses 这个 API 来获取,

EnumProcesses API 可以获取到当前系统下所有进程的 PID,并且将 PID 存放在作为输出参数的数组当中,

其原型如下(可以看 MSDN):

1:  BOOL WINAPI EnumProcesses(

2:    __out         DWORD* pProcessIds,

3:    __inDWORD cb,

4:    __out         DWORD* pBytesReturned

5:  );

6:   


     

代码中使用(将获取到所有的 PID,然后将 PID 保存到 vector 容器中):

1:  //遍历当前所有的进程,并且将进程 ID 填充到容器 vectorPID 中

2:  void CSSDTProcessDlg::FillPIDVector()

3:  {

4:      DWORD dwPIDArray[MAX_PROCESS_COUNT];

5:      DWORD dwNeededBytes;

6:      DWORD dwProcCount;

7:   

8:      dwNeededBytes = 0;

9:      dwProcCount = 0;

10:      memset(dwPIDArray, 0, sizeof(DWORD) * MAX_PROCESS_COUNT);

11:      if(NULL != EnumProcesses(dwPIDArray, sizeof(dwPIDArray), &dwNeededBytes))

12:      {

13:dwProcCount = dwNeededBytes / sizeof(DWORD);

14:      }

15:   

16:      BubbleSort(dwPIDArray, dwProcCount);

17:   

18:      ClearVector();

19:      for(int i=0; i<dwProcCount; i++)

20:      {

21:PROCESS_BIND procBind;

22:procBind.dwPID = dwPIDArray[i];

23:if(dwPIDArray[i] == 0)

24:{

25:    procBind.state = ProcessStateUnknown;

26:}

27:else

28:{

29:    procBind.state = ProcessStateGeneral;

30:}

31:this->m_vctAllProcess.push_back(procBind);

32:      }

33:  }


        

                

3. 服务管理(安装,启动,停止,卸载):

             

在 Windows 内核程序中,现在大体可以分为三类了,

第一类是 NT 式驱动程序;

第二类为 WDM 驱动程序;

第三类为 WDF 驱动程序;

其中,对于 NT 式驱动程序,其安装方式是很简单的,因为你可以将 NT 式驱动程序看做一个服务,

既然是服务的话,自然在 Windows 中可以通过 SCM API 来完成其安装,启动,停止和卸载等功能 ~

而至于 WDM 和 WDF 的话,如果其中涉及到了设备的话,还必须使用 INF 文件来实现安装 ~

而我们前面的那个 SSDT 内核程序就是基于 NT 式的驱动程序,所以可以通过 SCM API 来实现上面的这些功能,

至于如何使用 SCM API 来完成服务的安装、启动、停止和卸载功能的话,

可以参见笔者的另外一篇博文《Windows 服务(附服务开发辅助工具)》,

博文地址为:http://www.cnblogs.com/BoyXiao/archive/2011/08/07/2130208.html

下面就只是将服务的安装 API、启动 API、停止 API 和卸载 API 贴出来了 ~

至于这些代码的细细道来的话,可以参加上面给出的那篇博文的 ~

1:  //=====================================================================================//

2:  //Name: bool InstallSvc()  //

3:  //     //

4:  //Descripion: 安装服务     //

5:  //  lpszSvcName 为服务名称,         //

6:  //  lpszDisplay 为显示在服务控制管理器中的名称,         //

7:  //  lpszSvcBinaryPath 为服务映像文件所在路径, //

8:  //  dwSvcType 为服务类型   //

9:  //  dwStartType 为服务启动类型       //

10:  //=====================================================================================//

11:  bool CSSDTProcessDlg::InstallSvc(LPTSTR lpszSvcName, LPTSTR lpszDisplayName,

12:LPTSTR lpszSvcBinaryPath, DWORD dwSvcType, DWORD dwStartType)

13:  {

14:      SC_HANDLE hSCM = NULL;

15:      SC_HANDLE hSvc = NULL;

16:   

17:      AdjustProcessTokenPrivilege();

18:   

19:      hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

20:      if(NULL == hSCM)

21:      {

22:OutputErrorMessage(TEXT("InstallSvc - OpenSCManager Failed , Error Code Is %d , Error Message Is %s !"));

23:   

24:return FALSE;

25:      }

26:   

27:      for(int i = 0; i < 3 && (NULL == hSvc); i++)

28:      {

29://SERVICE_WIN32_OWN_PROCESS  | SERVICE_INTERACTIVE_PROCESS

30:hSvc = CreateService(hSCM, lpszSvcName, lpszDisplayName, SERVICE_ALL_ACCESS,

31:    dwSvcType, dwStartType, SERVICE_ERROR_NORMAL,

32:    lpszSvcBinaryPath, NULL, NULL, NULL, NULL, NULL);

33:if(NULL != hSvc)

34:{

35:    if(NULL != hSvc)

36:    {

37:        CloseServiceHandle(hSvc);

38:    }

39:    CloseServiceHandle(hSCM);

40:    return TRUE;

41:}

42:      }

43:   

44:      OutputErrorMessage(TEXT("InstallSvc - CreateService Failed , Error Code Is %d , Error Message Is %s !"));

45:   

46:      CloseServiceHandle(hSCM);

47:   

48:      return FALSE;

49:  }

50:   

51:   

52:  //=====================================================================================//

53:  //Name: bool UnInstallSvc()//

54:  //     //

55:  //Descripion: 实现卸载服务 //

56:  //=====================================================================================//

57:  bool CSSDTProcessDlg::UnInstallSvc(LPTSTR lpszSvcName)

58:  {

59:      SC_HANDLE hSCM = NULL;

60:      SC_HANDLE hSvc = NULL;

61:      bool rtResult = FALSE;

62:   

63:      AdjustProcessTokenPrivilege();

64:   

65:      hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

66:      if(NULL == hSCM)

67:      {

68:OutputErrorMessage(TEXT("UnInstallSvc - OpenSCManager Failed , Error Code Is %d , Error Message Is %s !"));

69:   

70:return FALSE;

71:      }

72:   

73:      hSvc = OpenService(hSCM, lpszSvcName, SERVICE_ALL_ACCESS);

74:      if(NULL == hSvc)

75:      {

76:OutputErrorMessage(TEXT("UnInstallSvc - OpenService Failed , Error Code Is %d , Error Message Is %s !"));

77:   

78:CloseServiceHandle(hSCM);

79:   

80:return FALSE;

81:      }

82:   

83:      rtResult = DeleteService(hSvc);

84:   

85:      CloseServiceHandle(hSvc);

86:      CloseServiceHandle(hSCM);

87:   

88:      return rtResult;

89:  }

90:   

91:   

92:  //=====================================================================================//

93:  //Name: bool StartSvc()    //

94:  //     //

95:  //Descripion: 实现启动服务 //

96:  //=====================================================================================//

97:  bool CSSDTProcessDlg::StartSvc(LPTSTR lpszSvcName)

98:  {

99:      SC_HANDLE hSCM = NULL;

100:      SC_HANDLE hSvc = NULL;

101:      bool rtResult = FALSE;

102:   

103:      AdjustProcessTokenPrivilege();

104:   

105:      hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

106:      if(NULL == hSCM)

107:      {

108:OutputErrorMessage(TEXT("StartSvc - OpenSCManager Failed , Error Code Is %d , Error Message Is %s !"));

109:   

110:return FALSE;

111:      }

112:   

113:      hSvc = OpenService(hSCM, lpszSvcName, SERVICE_ALL_ACCESS);

114:      if(NULL == hSvc)

115:      {

116:OutputErrorMessage(TEXT("StartSvc - OpenService Failed , Error Code Is %d , Error Message Is %s !"));

117:   

118:CloseServiceHandle(hSCM);

119:   

120:return FALSE;

121:      }

122:   

123:      rtResult = StartService(hSvc, NULL, NULL);

124:   

125:      CloseServiceHandle(hSvc);

126:      CloseServiceHandle(hSCM);

127:   

128:      if(FALSE == rtResult)

129:      {

130:if(ERROR_SERVICE_ALREADY_RUNNING == GetLastError())

131:{

132:    return TRUE;

133:}

134:else

135:{

136:    OutputErrorMessage(TEXT("StartSvc - StartService Failed , Error Code Is %d , Error Message Is %s !"));

137:   

138:    return FALSE;

139:}

140:      }

141:      else

142:      {

143:return TRUE;

144:      }

145:  }

146:   

147:   

148:  //=====================================================================================//

149:  //Name: bool StopSvc()     //

150:  //     //

151:  //Descripion: 实现停止服务 //

152:  //=====================================================================================//

153:  bool CSSDTProcessDlg::StopSvc(LPTSTR lpszSvcName)

154:  {

155:      SC_HANDLE hSCM = NULL;

156:      SC_HANDLE hSvc = NULL;

157:      bool rtResult = FALSE;

158:   

159:      SERVICE_STATUS svcStatus;

160:   

161:      AdjustProcessTokenPrivilege();

162:   

163:      hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

164:      if(NULL == hSCM)

165:      {

166:OutputErrorMessage(TEXT("StopSvc - OpenSCManager Failed , Error Code Is %d , Error Message Is %s !"));

167:   

168:return FALSE;

169:      }

170:   

171:      hSvc = OpenService(hSCM, lpszSvcName, SERVICE_ALL_ACCESS);

172:      if(NULL == hSvc)

173:      {

174:OutputErrorMessage(TEXT("StopSvc - OpenService Failed , Error Code Is %d , Error Message Is %s !"));

175:   

176:CloseServiceHandle(hSCM);

177:   

178:return FALSE;

179:      }

180:   

181:      rtResult = ControlService(hSvc, SERVICE_CONTROL_STOP, &svcStatus);

182:      if(rtResult == FALSE)

183:      {

184:OutputErrorMessage(TEXT("StopSvc - ControlService Failed , Error Code Is %d , Error Message Is %s !"));

185:      }

186:      CloseServiceHandle(hSvc);

187:      CloseServiceHandle(hSCM);

188:   

189:      return rtResult;

190:  }


       

那么服务的安装和启动放在那里比较合适,而服务的关闭和卸载又放在那里比较合适呢 ?

由于这个应用程序采用 MFC 开发,自然可以在 OnInitDialog()中安装和启动服务比较合适,

而后可以在对话框类的析构函数中关闭和卸载掉服务 ~

安装和启动服务:

1:      wstring wStrSysPath = GetSysFilePath();

2:      BOOL bResult = InstallSvc(((LPTSTR)(LPCTSTR)SSDT01_SERVICE_NAME),

3:  ((LPTSTR)(LPCTSTR)SSDT01_SERVICE_NAME),

4:  ((LPTSTR)(LPCTSTR)wStrSysPath.c_str()),

5:  SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START);

6:      if(FALSE == bResult)

7:      {

8:MessageBox(_TEXT(" Install SSDT Service Failed , Application Auto Exit !  "),

9: _TEXT("Application Error"), MB_OK | MB_ICONSTOP);

10:CDialogEx::OnCancel();

11:return FALSE;

12:      }

13:      else

14:      {

15:bResult = StartSvc(SSDT01_SERVICE_NAME);

16:if(FALSE == bResult)

17:{

18:    MessageBox(_TEXT(" Start SSDT Service Failed , Application Auto Exit !  "),

19:     _TEXT("Application Error"), MB_OK | MB_ICONSTOP);

20:    CDialogEx::OnCancel();

21:    return FALSE;

22:}

23:      }


           

停止并且将服务卸载掉:

1:      ~CSSDTProcessDlg()

2:      {

3://在析构函数中关闭 SSDT 设备句柄

4:if(this->m_hDevice)

5:{

6:    CloseHandle(this->m_hDevice);

7:}

8:   

9://当发生析构函数时,停止服务并且卸载服务

10:StopSvc(SSDT01_SERVICE_NAME);

11:UnInstallSvc(SSDT01_SERVICE_NAME);

12:      }


        

                

4. 应用程序和内核程序通信:

           

由前面的第二篇博文,可以知道,应用程序和内核程序的通信我是通过 DeviceIoControl 来完成的,

开发过内核程序的都清楚,应用程序和内核程序的通信最普遍的也就通过三个 API 来实现,

一个 ReadFile,一个 WriteFile,一个 DeviceIoContrl,

当然其中属 DeviceIoControl 功能最为强大,完全可以用其替换掉 ReadFile 和 WriteFile,

DeviceIoControl 原型(详细信息可以参考 MSDN):

1:  BOOL WINAPI DeviceIoControl(

2:    __inHANDLE hDevice,

3:    __inDWORD dwIoControlCode,

4:    __inLPVOID lpInBuffer,

5:    __inDWORD nInBufferSize,

6:    __out         LPVOID lpOutBuffer,

7:    __inDWORD nOutBufferSize,

8:    __out         LPDWORD lpBytesReturned,

9:    __inLPOVERLAPPED lpOverlapped

10:  );

11:   


         

至于如何实现应用程序和内核程序的通信的话,在我的 Demo 中是这样做处理的,

首先在 OnInitDialog 事件中通过 CreateFile 打开我们所安装的服务中创建的设备,

(在 NT 式驱动程序中我创建了一个设备,这个设备用来实现应用程序和内核程序的通信),

然后在对话框类中保存有一个全局变量,这个全局变量即代表所打开的这个设备的句柄,





       

既然这个全局变量是保存的我们的设备的句柄,自然我们需要来获取到设备的句柄,并且将句柄赋值给该全局变量,

而这个呢,又是在 OnInitDialog 中完成的 ~

1:      this->m_hDevice = CreateFile(SSDT01_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, 0,

2:     NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

3:      if(INVALID_HANDLE_VALUE == this->m_hDevice)

4:      {

5:MessageBox(_TEXT(" Open SSDT Device Failed , Application Auto Exit !  "),

6: _TEXT("Application Error"), MB_OK | MB_ICONSTOP);

7:   

8:CDialogEx::OnCancel();

9:return FALSE;

10:      }


        

有了这个设备句柄,我们就可以通过其来实现和内核程序的通信了,

因为通过在应用程序中调用 DeviceIoControl 可以产生 IRP_MJ_DEVICE_CONTROL 的 IRP,

然后该 IRP 可以被驱动程序中的 DeviceIoControl 分发函数所处理 ~

我们的应用程序只需要将我们所要隐藏或者是需要保护的进程的 PID 通过 DeviceIoControl 传递给内核程序即可 !!!

所以我们在应用程序中只需要调用 DeviceIoContrl 即可 ~

下面给出的代码比较凌乱(重点请看 DeviceIoControl 的调用)

1:  //隐藏进程或者取消对进程的隐藏

2:  void CSSDTProcessDlg::OnBnClickedBtnHideorunhide()

3:  {

4:      int nIndex;

5:      DWORD dwPID;

6:      CString cStrText;

7:      CString cStrState;

8:

9:      DWORD dwOutput;

10:      BOOL bRet;

11:      CHAR inBuffer[10];

12:      CHAR outBuffer[10];

13:      memset(inBuffer, 0, 10);

14:      memset(outBuffer, 0, 10);

15:   

16:      dwPID = this->GetDlgItemInt(IDC_STATIC_SELECTED_PID);

17:      this->GetDlgItemText(ID_BTN_HIDEORUNHIDE, cStrText);

18:   

19:      ultoa(dwPID, inBuffer, 10);

20:   

21:      nIndex = QueryItemIndexByPID(dwPID);

22:      cStrState = this->m_ListCtrlProcess.GetItemText(nIndex, 4);

23:   

24:      if(cStrText.CompareNoCase(_TEXT("Hide")) == 0)

25:      {

26://隐藏 dwPID

27:bRet = DeviceIoControl(this->m_hDevice, IO_INSERT_HIDE_PROCESS, inBuffer, 10,

28:     &outBuffer, 10, &dwOutput, NULL);

29:if(bRet)

30:{

31:    this->SetDlgItemText(ID_BTN_HIDEORUNHIDE, _TEXT("UnHide"));

32:    if(cStrState.CompareNoCase(_TEXT("Protect")) == 0)

33:    {

34:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("HideAndProtect"));

35:    }

36:    else

37:    {

38:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("Hide"));

39:    }

40:    MessageBox(_TEXT(" Hide Process Sucess !  "), _TEXT("Information"), MB_OK |

41:   MB_ICONINFORMATION);

42:}

43:else

44:{

45:    MessageBox(_TEXT(" Hide Process Failed !  "), _TEXT("Warning"), MB_OK | MB_ICONERROR);

46:}

47:      }

48:      else

49:      {

50://解除 dwPID 隐藏

51:bRet = DeviceIoControl(this->m_hDevice, IO_REMOVE_HIDE_PROCESS, inBuffer, 10,

52:     &outBuffer, 10, &dwOutput, NULL);

53:if(bRet)

54:{

55:    this->SetDlgItemText(ID_BTN_HIDEORUNHIDE, _TEXT("Hide"));

56:    if(cStrState.CompareNoCase(_TEXT("Protect")) == 0 ||

57:       cStrState.CompareNoCase(_TEXT("HideAndProtect"))== 0)

58:    {

59:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("Protect"));

60:    }

61:    else

62:    {

63:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("General"));

64:    }

65:    MessageBox(_TEXT(" UnHide Process Sucess !  "), _TEXT("Information"), MB_OK |

66:MB_ICONINFORMATION);

67:}

68:else

69:{

70:    MessageBox(_TEXT(" UnHide Process Failed !  "), _TEXT("Warning"), MB_OK | MB_ICONERROR);

71:}

72:      }

73:  }

74:   

75:   

76:  //保护进程或者取消对进程的保护操作

77:  void CSSDTProcessDlg::OnBnClickedBtnProtectorunprotect()

78:  {

79:      int nIndex;

80:      DWORD dwPID;

81:      CString cStrText;

82:      CString cStrState;

83:   

84:      DWORD dwOutput;

85:      BOOL bRet;

86:      CHAR inBuffer[10];

87:      CHAR outBuffer[10];

88:      memset(inBuffer, 0, 10);

89:      memset(outBuffer, 0, 10);

90:   

91:      dwPID = this->GetDlgItemInt(IDC_STATIC_SELECTED_PID);

92:      this->GetDlgItemText(ID_BTN_PROTECTORUNPROTECT, cStrText);

93:   

94:      ultoa(dwPID, inBuffer, 10);

95:   

96:      nIndex = QueryItemIndexByPID(dwPID);

97:      cStrState = this->m_ListCtrlProcess.GetItemText(nIndex, 4);

98:   

99:      if(cStrText.CompareNoCase(_TEXT("Protect")) == 0)

100:      {

101://保护 dwPID 保护

102:bRet = DeviceIoControl(this->m_hDevice, IO_INSERT_PROTECT_PROCESS, inBuffer, 10,

103:     &outBuffer, 10, &dwOutput, NULL);

104:if(bRet)

105:{

106:    this->SetDlgItemText(ID_BTN_PROTECTORUNPROTECT, _TEXT("UnProtect"));

107:    if(cStrState.CompareNoCase(_TEXT("Hide"))== 0)

108:    {

109:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("HideAndProtect"));

110:    }

111:    else

112:    {

113:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("Protect"));

114:    }

115:    MessageBox(_TEXT(" Protect Process Sucess !  "), _TEXT("Information"), MB_OK |

116:MB_ICONINFORMATION);

117:}

118:else

119:{

120:    MessageBox(_TEXT(" Protect Process Failed !  "), _TEXT("Warning"), MB_OK | MB_ICONERROR);

121:}

122:      }

123:      else

124:      {

125://解除 dwPID 保护

126:bRet = DeviceIoControl(this->m_hDevice, IO_REMOVE_PROTECT_PROCESS, inBuffer, 10,

127:     &outBuffer, 10, &dwOutput, NULL);

128:if(bRet)

129:{

130:    this->SetDlgItemText(ID_BTN_PROTECTORUNPROTECT, _TEXT("Protect"));

131:    if(cStrState.CompareNoCase(_TEXT("Hide")) == 0 ||

132:         cStrState.CompareNoCase(_TEXT("HideAndProtect")) == 0)

133:    {

134:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("Hide"));

135:    }

136:    else

137:    {

138:        this->m_ListCtrlProcess.SetItemText(nIndex, 4, _TEXT("General"));

139:    }

140:    MessageBox(_TEXT(" UnProtect Process Sucess !  "), _TEXT("Information"), MB_OK |

141:MB_ICONINFORMATION);

142:}

143:else

144:{

145:    MessageBox(_TEXT(" UnProtect Process Failed !  "), _TEXT("Warning"), MB_OK | MB_ICONERROR);

146:}

147:      }

148:  }


 


5. 小结:

          

介绍这个应用程序呢,还真是不好写,因为感觉整个 Demo 里面却是没有什么好介绍的,

无非就是获取到所有的进程,然后通过一个 ListCtrl 来显示这些数据,

然后用户选择一个进程,单击一下隐藏呢,我就在这个按钮的消息处理函数中和内核程序通过 DeviceIoControl 通信一下,

将这个进程的 PID 传递给内核程序,其他的就都不需要理会了 ~ 所以转来转去的,也没什么好些的,干脆就写到这里得了,

等下将整个 Demo 打个包,直接提供下载,我这里说得口干舌燥也没什么用,感兴趣的自己下载了源码去慢慢玩得了
~

最后再总结一个 SSDT Hook 的优点,那就是 SSDT Hook 无论你是 Windows XP 还是 Server 或者 Vista 或者 Win7,

你都是可以很好的运行程序的,所以你下载的 Demo 你可以放心的在上面的这些操作系统上运行,当然 64 位的除外,

64 位的操作系统虽然我没有做过测试,但是我估摸着会蓝屏的 ~ 有兴趣的可以去蓝一次 ~

           

下载 Demo Source
Code

                                    
注:原文链接的源码下载地址失效了,我在其他地方下载到了源码,应该是另一位朋友找不到源码,按照博客的教程写出来的, 
http://download.csdn.net/detail/liujiayu2/9865256

           

版权所有,欢迎转载,但转载请注明: 转载自  Zachary.XiaoZhen
- 梦想的天空
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: