进程隐藏与进程保护(SSDT Hook 实现)(三)
文章目錄:
?????????????????
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: __in DWORD 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: __in HANDLE hDevice, 3: __in DWORD dwIoControlCode, 4: __in LPVOID lpInBuffer, 5: __in DWORD nInBufferSize, 6: __out LPVOID lpOutBuffer, 7: __in DWORD nOutBufferSize, 8: __out LPDWORD lpBytesReturned, 9: __in LPOVERLAPPED 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 - 夢想的天空
總結
以上是生活随笔為你收集整理的进程隐藏与进程保护(SSDT Hook 实现)(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程隐藏与进程保护(SSDT Hook
- 下一篇: 逆向工程核心原理读书笔记-代码注入