如何防止軟件被hook?高手進 200分(200分)

  • 主题发起人 主题发起人 sdv
  • 开始时间 开始时间
S

sdv

Unregistered / Unconfirmed
GUEST, unregistred user!
就這麽一個簡單的問題,<br>禁止hook api<br>如何能做到呢?<br><br>我現在有一個簡單的想法<br>把程序内關鍵的hook的jmp讀取出來用另外一個綫程監控<br>這個方法是否好呢?
 
没有办法防系统的HOOK
 
沒有可行辦法麽?<br>現在常用 的修改iat方法一定是去修改軟件運行内存<br>這樣來説應該可以用我的方案作檢索檢查把?<br><br>各位高手能否指導一下?
 
这种东西肯定没有办法防住的.
 
看來需要自己努力尋找答案了
 
抛開全局的不管,就程序調用時候api不被hook有什麽好方案呢?
 
帮你找的资料,希望能帮得上忙<br>http://soft.yesky.com/SoftChannel/72356695560421376/20050523/1951575.shtml<br>防止全局钩子的入侵
 
只是增加难度,完全不被hook的不可能有
 
偶找到的,恢复被Hook的中断的真实地址,全局Hook的克星,可以对付3721的Minkp.sys<br>等修改ssdt的驱动.(原作者很谨慎,其实修改一下原程序的版本检测可适用至win2003)<br><br>//*********************************************************************************************<br>// SDTrestore (Proof-of-Concept) <br>// Version 0.2<br>// by SIG^2 G-TEC Lab<br>//<br>// Coded by Chew Keong TAN<br>//<br>// Permission is hereby granted, free of charge, to any person obtaining a<br>// copy of this software and associated documentation files (the<br>// &quot;Software&quot;), to deal in the Software without restriction, including<br>// without limitation the rights to use, copy, modify, merge, publish,<br>// distribute, and/or sell copies of the Software, and to permit persons<br>// to whom the Software is furnished to do so, provided that the above<br>// copyright notice(s) and this permission notice appear in all copies of<br>// the Software and that both the above copyright notice(s) and this<br>// permission notice appear in supporting documentation.<br>//<br>// THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS<br>// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF<br>// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT<br>// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR<br>// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL<br>// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING<br>// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,<br>// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION<br>// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.<br>//<br>// This program needs to access /device/physicalmemory, so you must be <br>// running as Administrator when using this.<br>//<br>// CHANGES<br>// -------<br>// Version 0.2<br>// Thanks to 90210 for the suggestion of a more stable way to find the <br>// address of KiServiceTable from the disk image of ntoskrnl.exe<br>// http://www.rootkit.com/newsread.php?newsid=176<br>//<br>//*********************************************************************************************<br><br>#include &lt;stdio.h&gt;<br>#include &lt;stdlib.h&gt;<br>#include &lt;windows.h&gt;<br>#include &lt;aclapi.h&gt;<br><br><br>#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)<br>#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)<br>#define OBJ_CASE_INSENSITIVE 0x00000040L<br>#define PAGE_READONLY 0x02<br>#define PAGE_READWRITE 0x04<br>#define DEF_KERNEL_BASE 0x80400000L<br>#define SystemModuleInformation 11<br>#define PROT_MEMBASE 0x80000000<br><br>typedef LONG NTSTATUS;<br>typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;<br><br>DWORD gWinVersion;<br><br>typedef struct _STRING {<br> &nbsp;USHORT &nbsp;Length;<br> &nbsp;USHORT &nbsp;MaximumLength;<br> &nbsp;PCHAR &nbsp;Buffer;<br>} ANSI_STRING, *PANSI_STRING;<br><br>typedef struct _UNICODE_STRING {<br> &nbsp;USHORT &nbsp;Length;<br> &nbsp;USHORT &nbsp;MaximumLength;<br> &nbsp;PWSTR &nbsp;Buffer;<br>} UNICODE_STRING, *PUNICODE_STRING;<br><br>typedef struct _OBJECT_ATTRIBUTES {<br> &nbsp; &nbsp;ULONG Length;<br> &nbsp; &nbsp;HANDLE RootDirectory;<br> &nbsp; &nbsp;PUNICODE_STRING ObjectName;<br> &nbsp; &nbsp;ULONG Attributes;<br> &nbsp; &nbsp;PVOID SecurityDescriptor; &nbsp; &nbsp; &nbsp; &nbsp;// Points to type SECURITY_DESCRIPTOR<br> &nbsp; &nbsp;PVOID SecurityQualityOfService; &nbsp;// Points to type SECURITY_QUALITY_OF_SERVICE<br>} OBJECT_ATTRIBUTES;<br>typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;<br><br>typedef enum _SECTION_INHERIT {<br> &nbsp; &nbsp;ViewShare = 1,<br> &nbsp; &nbsp;ViewUnmap = 2<br>} SECTION_INHERIT;<br><br>typedef struct _SYSTEM_MODULE_INFORMATION<br>{<br> ULONG Reserved[2];<br> PVOID Base;<br> ULONG Size;<br> ULONG Flags;<br> USHORT Index;<br> USHORT Unknown;<br> USHORT LoadCount;<br> USHORT ModuleNameOffset;<br> CHAR ImageName[256];<br>} SYSTEM_MODULE_INFORMATION;<br><br>NTSTATUS (WINAPI * _RtlAnsiStringToUnicodeString)<br> (PUNICODE_STRING &nbsp;DestinationString,<br> IN PANSI_STRING &nbsp;SourceString,<br> IN BOOLEAN);<br><br>VOID (WINAPI *_RtlInitAnsiString)<br> (IN OUT PANSI_STRING &nbsp;DestinationString,<br> IN PCHAR &nbsp;SourceString);<br><br>VOID (WINAPI * _RtlFreeUnicodeString)<br> (IN PUNICODE_STRING &nbsp;UnicodeString);<br><br>NTSTATUS (WINAPI *_NtOpenSection)<br> (OUT PHANDLE &nbsp;SectionHandle,<br> IN ACCESS_MASK &nbsp;DesiredAccess,<br> IN POBJECT_ATTRIBUTES &nbsp;ObjectAttributes);<br><br>NTSTATUS (WINAPI *_NtMapViewOfSection)<br> (IN HANDLE &nbsp;SectionHandle,<br> IN HANDLE &nbsp;ProcessHandle,<br> IN OUT PVOID &nbsp;*BaseAddress,<br> IN ULONG &nbsp;ZeroBits,<br> IN ULONG &nbsp;CommitSize,<br> IN OUT PLARGE_INTEGER &nbsp;SectionOffset, /* optional */<br> IN OUT PULONG &nbsp;ViewSize,<br> IN SECTION_INHERIT &nbsp;InheritDisposition,<br> IN ULONG &nbsp;AllocationType,<br> IN ULONG &nbsp;Protect);<br><br>NTSTATUS (WINAPI *_NtUnmapViewOfSection)<br> (IN HANDLE ProcessHandle,<br> IN PVOID BaseAddress);<br><br>NTSTATUS (WINAPI * _NtQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);<br><br>//*******************************************************************************************************<br>// PE File structure declarations<br>//<br>//*******************************************************************************************************<br><br>struct PE_Header <br>{<br> unsigned long signature;<br> unsigned short machine;<br> unsigned short numSections;<br> unsigned long timeDateStamp;<br> unsigned long pointerToSymbolTable;<br> unsigned long numOfSymbols;<br> unsigned short sizeOfOptionHeader;<br> unsigned short characteristics;<br>};<br><br>struct PE_ExtHeader<br>{<br> unsigned short magic;<br> unsigned char majorLinkerVersion;<br> unsigned char minorLinkerVersion;<br> unsigned long sizeOfCode;<br> unsigned long sizeOfInitializedData;<br> unsigned long sizeOfUninitializedData;<br> unsigned long addressOfEntryPoint;<br> unsigned long baseOfCode;<br> unsigned long baseOfData;<br> unsigned long imageBase;<br> unsigned long sectionAlignment;<br> unsigned long fileAlignment;<br> unsigned short majorOSVersion;<br> unsigned short minorOSVersion;<br> unsigned short majorImageVersion;<br> unsigned short minorImageVersion;<br> unsigned short majorSubsystemVersion;<br> unsigned short minorSubsystemVersion;<br> unsigned long reserved1;<br> unsigned long sizeOfImage;<br> unsigned long sizeOfHeaders;<br> unsigned long checksum;<br> unsigned short subsystem;<br> unsigned short DLLCharacteristics;<br> unsigned long sizeOfStackReserve;<br> unsigned long sizeOfStackCommit;<br> unsigned long sizeOfHeapReserve;<br> unsigned long sizeOfHeapCommit;<br> unsigned long loaderFlags;<br> unsigned long numberOfRVAAndSizes;<br> unsigned long exportTableAddress;<br> unsigned long exportTableSize;<br> unsigned long importTableAddress;<br> unsigned long importTableSize;<br> unsigned long resourceTableAddress;<br> unsigned long resourceTableSize;<br> unsigned long exceptionTableAddress;<br> unsigned long exceptionTableSize;<br> unsigned long certFilePointer;<br> unsigned long certTableSize;<br> unsigned long relocationTableAddress;<br> unsigned long relocationTableSize;<br> unsigned long debugDataAddress;<br> unsigned long debugDataSize;<br> unsigned long archDataAddress;<br> unsigned long archDataSize;<br> unsigned long globalPtrAddress;<br> unsigned long globalPtrSize;<br> unsigned long TLSTableAddress;<br> unsigned long TLSTableSize;<br> unsigned long loadConfigTableAddress;<br> unsigned long loadConfigTableSize;<br> unsigned long boundImportTableAddress;<br> unsigned long boundImportTableSize;<br> unsigned long importAddressTableAddress;<br> unsigned long importAddressTableSize;<br> unsigned long delayImportDescAddress;<br> unsigned long delayImportDescSize;<br> unsigned long COMHeaderAddress;<br> unsigned long COMHeaderSize;<br> unsigned long reserved2;<br> unsigned long reserved3;<br>};<br><br><br>struct SectionHeader<br>{<br> unsigned char sectionName[8];<br> unsigned long virtualSize;<br> unsigned long virtualAddress;<br> unsigned long sizeOfRawData;<br> unsigned long pointerToRawData;<br> unsigned long pointerToRelocations;<br> unsigned long pointerToLineNumbers;<br> unsigned short numberOfRelocations;<br> unsigned short numberOfLineNumbers;<br> unsigned long characteristics;<br>};<br><br>struct MZHeader<br>{<br> unsigned short signature;<br> unsigned short partPag;<br> unsigned short pageCnt;<br> unsigned short reloCnt;<br> unsigned short hdrSize;<br> unsigned short minMem;<br> unsigned short maxMem;<br> unsigned short reloSS;<br> unsigned short exeSP;<br> unsigned short chksum;<br> unsigned short exeIP;<br> unsigned short reloCS;<br> unsigned short tablOff;<br> unsigned short overlay;<br> unsigned char reserved[32];<br> unsigned long offsetToPE;<br>};<br><br><br>struct ImportDirEntry<br>{<br> DWORD importLookupTable;<br> DWORD timeDateStamp;<br> DWORD fowarderChain;<br> DWORD nameRVA;<br> DWORD importAddressTable;<br>};<br><br><br>DWORD myStrlenA(char *ptr)<br>{<br> DWORD len = 0;<br> while(*ptr)<br> {<br> len++;<br> ptr++;<br> }<br><br> return len;<br>}<br><br><br>BOOL myStrcmpA(char *str1, char *str2)<br>{<br> while(*str1 && *str2)<br> {<br> if(*str1 == *str2)<br> {<br> str1++;<br> str2++;<br> }<br> else<br> {<br> return FALSE;<br> }<br> }<br><br> if(*str1 && !*str2)<br> {<br> return FALSE;<br> }<br> else if(*str2 && !*str1)<br> {<br> return FALSE;<br> }<br><br> return TRUE; <br>}<br><br>//*******************************************************************************************************<br>// Fills the various structures with info of a PE image. &nbsp;The PE image is located at modulePos.<br>//<br>//*******************************************************************************************************<br><br>bool readPEInfo(char *modulePos, MZHeader *outMZ, PE_Header *outPE, PE_ExtHeader *outpeXH,<br> SectionHeader **outSecHdr)<br>{<br> // read MZ Header<br> MZHeader *mzH;<br> mzH = (MZHeader *)modulePos;<br><br> if(mzH-&gt;signature != 0x5a4d) // MZ<br> {<br> printf(&quot;File does not have MZ header/n&quot;);<br> return false;<br> }<br><br> // read PE Header<br> PE_Header *peH;<br> peH = (PE_Header *)(modulePos + mzH-&gt;offsetToPE);<br><br> if(peH-&gt;sizeOfOptionHeader != sizeof(PE_ExtHeader))<br> {<br> printf(&quot;Unexpected option header size./n&quot;);<br> <br> return false;<br> }<br><br> // read PE Ext Header<br> PE_ExtHeader *peXH;<br> peXH = (PE_ExtHeader *)((char *)peH + sizeof(PE_Header));<br><br> // read the sections<br> SectionHeader *secHdr = (SectionHeader *)((char *)peXH + sizeof(PE_ExtHeader));<br><br> *outMZ = *mzH;<br> *outPE = *peH;<br> *outpeXH = *peXH;<br> *outSecHdr = secHdr;<br><br> return true;<br>}<br><br><br>//*******************************************************************************************************<br>// Returns the total size required to load a PE image into memory<br>//<br>//*******************************************************************************************************<br><br>int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,<br> &nbsp; &nbsp; &nbsp; SectionHeader *inSecHdr)<br>{<br> int result = 0;<br> int alignment = inpeXH-&gt;sectionAlignment;<br><br> if(inpeXH-&gt;sizeOfHeaders % alignment == 0)<br> result += inpeXH-&gt;sizeOfHeaders;<br> else<br> {<br> int val = inpeXH-&gt;sizeOfHeaders / alignment;<br> val++;<br> result += (val * alignment);<br> }<br> for(int i = 0; i &lt; inPE-&gt;numSections; i++)<br> {<br> if(inSecHdr.virtualSize)<br> {<br> if(inSecHdr.virtualSize % alignment == 0)<br> result += inSecHdr.virtualSize;<br> else<br> {<br> int val = inSecHdr.virtualSize / alignment;<br> val++;<br> result += (val * alignment);<br> }<br> }<br> }<br><br> return result;<br>}<br><br><br>//*******************************************************************************************************<br>// Returns the aligned size of a section<br>//<br>//*******************************************************************************************************<br><br>unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)<br>{ <br> if(curSize % alignment == 0)<br> return curSize;<br> else<br> {<br> int val = curSize / alignment;<br> val++;<br> return (val * alignment);<br> }<br>}<br><br>//*******************************************************************************************************<br>// Copy a PE image from exePtr to ptrLoc with proper memory alignment of all sections<br>//<br>//*******************************************************************************************************<br><br>bool loadPE(char *exePtr, MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,<br> SectionHeader *inSecHdr, LPVOID ptrLoc)<br>{<br> char *outPtr = (char *)ptrLoc;<br> <br> memcpy(outPtr, exePtr, inpeXH-&gt;sizeOfHeaders);<br> outPtr += getAlignedSize(inpeXH-&gt;sizeOfHeaders, inpeXH-&gt;sectionAlignment);<br><br> for(int i = 0; i &lt; inPE-&gt;numSections; i++)<br> {<br> if(inSecHdr.sizeOfRawData &gt; 0)<br> {<br> unsigned long toRead = inSecHdr.sizeOfRawData;<br> if(toRead &gt; inSecHdr.virtualSize)<br> toRead = inSecHdr.virtualSize;<br><br> memcpy(outPtr, exePtr + inSecHdr.pointerToRawData, toRead);<br><br> outPtr += getAlignedSize(inSecHdr.virtualSize, inpeXH-&gt;sectionAlignment);<br> }<br> }<br><br> return true;<br>}<br><br><br>//*******************************************************************************************************<br>// Loads the DLL into memory and align it<br>//<br>//*******************************************************************************************************<br><br>LPVOID loadDLL(char *dllName)<br>{<br> char moduleFilename[MAX_PATH + 1];<br> LPVOID ptrLoc = NULL;<br> MZHeader mzH2;<br> PE_Header peH2;<br> PE_ExtHeader peXH2;<br> SectionHeader *secHdr2;<br><br> GetSystemDirectory(moduleFilename, MAX_PATH);<br> if((myStrlenA(moduleFilename) + myStrlenA(dllName)) &gt;= MAX_PATH)<br> return NULL;<br><br> strcat(moduleFilename, dllName);<br><br> // load this EXE into memory because we need its original Import Hint Table<br><br> HANDLE fp;<br> fp = CreateFile(moduleFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);<br> <br> if(fp != INVALID_HANDLE_VALUE)<br> {<br> BY_HANDLE_FILE_INFORMATION fileInfo;<br> GetFileInformationByHandle(fp, &fileInfo);<br><br> DWORD fileSize = fileInfo.nFileSizeLow;<br> //printf(&quot;Size = %d/n&quot;, fileSize);<br> if(fileSize)<br> {<br> LPVOID exePtr = HeapAlloc(GetProcessHeap(), 0, fileSize);<br> if(exePtr)<br> {<br> DWORD read;<br><br> if(ReadFile(fp, exePtr, fileSize, &read, NULL) && read == fileSize)<br> { <br> if(readPEInfo((char *)exePtr, &mzH2, &peH2, &peXH2, &secHdr2))<br> {<br> int imageSize = calcTotalImageSize(&mzH2, &peH2, &peXH2, secHdr2); <br><br> //ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);<br> ptrLoc = HeapAlloc(GetProcessHeap(), 0, imageSize);<br> if(ptrLoc)<br> { <br> loadPE((char *)exePtr, &mzH2, &peH2, &peXH2, secHdr2, ptrLoc);<br> }<br> }<br><br> }<br> HeapFree(GetProcessHeap(), 0, exePtr);<br> }<br> }<br> CloseHandle(fp);<br> }<br><br> return ptrLoc;<br>}<br><br><br>DWORD procAPIExportAddr(DWORD hModule, char *apiName)<br>{ <br> if(!hModule || !apiName)<br> return 0;<br><br> char *ptr = (char *)hModule;<br> ptr += 0x3c; // offset 0x3c contains offset to PE header<br> <br> ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // offset 78h into PE header contains addr of export table<br><br> ptr = (char *)(*(DWORD *)ptr) + hModule; // ptr now points to export directory table<br><br> // offset 24 into the export directory table == number of entries in the Export Name Pointer Table<br> // table<br> DWORD numEntries = *(DWORD *)(ptr + 24);<br> //printf(&quot;NumEntries = %d/n&quot;, numEntries);<br><br> DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); &nbsp;// offset 32 into export directory contains offset to Export Name Pointer Table <br> <br> DWORD ordinalBase = *((DWORD *)(ptr + 16));<br> //printf(&quot;OrdinalBase is %d/n&quot;, ordinalBase);<br><br><br> WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule); // offset 36 into export directory contains offset to Ordinal Table<br> DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule); // offset 28 into export directory contains offset to Export Addr Table<br><br> for(DWORD i = 0; i &lt; numEntries; i++)<br> { <br> char *exportName = (char *)(ExportNamePointerTable + hModule);<br><br> if(myStrcmpA(exportName, apiName) == TRUE)<br> { <br> WORD ordinal = ExportOrdinalTable;<br> //printf(&quot;%s (i = %d) Ordinal = %d at %X/n&quot;, exportName, i, ordinal, ExportAddrTable[ordinal]);<br><br> return (DWORD)(ExportAddrTable[ordinal]);<br> } <br> }<br><br> return 0;<br>}<br><br>//*******************************************************************************************************<br>// -- END PE File support functions --<br>//<br>//*******************************************************************************************************<br><br><br>//*********************************************************************************************<br>// Builds a table of native API names using the export table of ntdll.dll<br>//<br>//*********************************************************************************************<br><br>BOOL buildNativeAPITable(DWORD hModule, char *nativeAPINames[], DWORD numNames)<br>{<br> if(!hModule)<br> return FALSE;<br><br> char *ptr = (char *)hModule;<br> ptr += 0x3c; // offset 0x3c contains offset to PE header<br> <br> ptr = (char *)(*(DWORD *)ptr) + hModule + 0x78; // offset 78h into PE header contains addr of export table<br><br> ptr = (char *)(*(DWORD *)ptr) + hModule; // ptr now points to export directory table<br><br> <br> // offset 24 into the export directory table == number of entries in the Name Pointer Table<br> // table<br> DWORD numEntries = *(DWORD *)(ptr + 24); <br> <br> DWORD *ExportNamePointerTable = (DWORD *)(*(DWORD *)(ptr + 32) + hModule); &nbsp;// offset 32 into export directory contains offset to Export Name Pointer Table <br><br> DWORD ordinalBase = *((DWORD *)(ptr + 16));<br><br> WORD *ExportOrdinalTable = (WORD *)((*(DWORD *)(ptr + 36)) + hModule); // offset 36 into export directory contains offset to Ordinal Table<br> DWORD *ExportAddrTable = (DWORD *)((*(DWORD *)(ptr + 28)) + hModule); // offset 28 into export directory contains offset to Export Addr Table<br><br><br> for(DWORD i = 0; i &lt; numEntries; i++)<br> { <br> // i now contains the index of the API in the Ordinal Table<br> // ptr points to Export directory table<br><br> WORD ordinalValue = ExportOrdinalTable; <br> DWORD apiAddr = (DWORD)ExportAddrTable[ordinalValue] + hModule;<br> char *exportName = (char *)(ExportNamePointerTable + hModule);<br> <br> // Win2K<br> if(gWinVersion == 0 &&<br> &nbsp; *((unsigned char *)apiAddr) == 0xB8 && <br> &nbsp; *((unsigned char *)apiAddr + 9) == 0xCD && <br> &nbsp; *((unsigned char *)apiAddr + 10) == 0x2E)<br> {<br> DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1);<br> if(serviceNum &lt; numNames)<br> {<br> nativeAPINames[serviceNum] = exportName;<br> }<br> //printf(&quot;%X - %s/n&quot;, serviceNum, exportName);<br> }<br><br> // WinXP<br> else if(gWinVersion == 1 &&<br> *((unsigned char *)apiAddr) == 0xB8 && <br> *((unsigned char *)apiAddr + 5) == 0xBA && <br> *((unsigned char *)apiAddr + 6) == 0x00 &&<br> *((unsigned char *)apiAddr + 7) == 0x03 &&<br> *((unsigned char *)apiAddr + 8) == 0xFE &&<br> *((unsigned char *)apiAddr + 9) == 0x7F)<br> {<br> DWORD serviceNum = *(DWORD *)((char *)apiAddr + 1);<br> if(serviceNum &lt; numNames)<br> {<br> nativeAPINames[serviceNum] = exportName;<br> }<br> //printf(&quot;%X - %s/n&quot;, serviceNum, exportName);<br> }<br> }<br><br> return TRUE;<br>}<br><br><br>//*******************************************************************************************************<br>// Gets address of native API's that we'll be using<br>//<br>//*******************************************************************************************************<br><br>BOOL getNativeAPIs(void)<br>{<br> HMODULE hntdll;<br><br> hntdll = GetModuleHandle(&quot;ntdll.dll&quot;);<br> <br> *(FARPROC *)&_RtlAnsiStringToUnicodeString = <br> GetProcAddress(hntdll, &quot;RtlAnsiStringToUnicodeString&quot;);<br><br> *(FARPROC *)&_RtlInitAnsiString = <br> GetProcAddress(hntdll, &quot;RtlInitAnsiString&quot;);<br><br> *(FARPROC *)&_RtlFreeUnicodeString = <br> GetProcAddress(hntdll, &quot;RtlFreeUnicodeString&quot;);<br><br> *(FARPROC *)&_NtOpenSection =<br> GetProcAddress(hntdll, &quot;NtOpenSection&quot;);<br><br> *(FARPROC *)&_NtMapViewOfSection =<br> GetProcAddress(hntdll, &quot;NtMapViewOfSection&quot;);<br><br> *(FARPROC *)&_NtUnmapViewOfSection =<br> GetProcAddress(hntdll, &quot;NtUnmapViewOfSection&quot;);<br><br> *(FARPROC *)&_NtQuerySystemInformation =<br> GetProcAddress(hntdll, &quot;ZwQuerySystemInformation&quot;);<br><br> if(_RtlAnsiStringToUnicodeString && _RtlInitAnsiString && _RtlFreeUnicodeString &&<br> _NtOpenSection && _NtMapViewOfSection && _NtUnmapViewOfSection && _NtQuerySystemInformation)<br> {<br> return TRUE;<br> }<br> return FALSE;<br>}<br><br><br>//*******************************************************************************************************<br>// Obtain a handle to /device/physicalmemory<br>//<br>//*******************************************************************************************************<br><br>HANDLE openPhyMem()<br>{<br> HANDLE hPhyMem;<br> OBJECT_ATTRIBUTES oAttr;<br><br> ANSI_STRING aStr;<br> <br> _RtlInitAnsiString(&aStr, &quot;//device//physicalmemory&quot;);<br> <br> UNICODE_STRING uStr;<br><br> if(_RtlAnsiStringToUnicodeString(&uStr, &aStr, TRUE) != STATUS_SUCCESS)<br> { <br> return INVALID_HANDLE_VALUE; <br> }<br><br> &nbsp; &nbsp;oAttr.Length = sizeof(OBJECT_ATTRIBUTES);<br> &nbsp; &nbsp;oAttr.RootDirectory = NULL;<br> &nbsp; &nbsp;oAttr.Attributes = OBJ_CASE_INSENSITIVE;<br> &nbsp; &nbsp;oAttr.ObjectName = &uStr;<br> &nbsp; &nbsp;oAttr.SecurityDescriptor = NULL;<br> &nbsp; &nbsp;oAttr.SecurityQualityOfService = NULL;<br><br> if(_NtOpenSection(&hPhyMem, SECTION_MAP_READ | SECTION_MAP_WRITE, &oAttr ) != STATUS_SUCCESS)<br> { <br> return INVALID_HANDLE_VALUE;<br> }<br><br> return hPhyMem;<br>}<br><br><br>//*******************************************************************************************************<br>// Map in a section of physical memory into this process's virtual address space.<br>//<br>//*******************************************************************************************************<br><br>BOOL mapPhyMem(HANDLE hPhyMem, DWORD *phyAddr, DWORD *length, PVOID *virtualAddr)<br>{<br> NTSTATUS ntStatus;<br> PHYSICAL_ADDRESS viewBase;<br><br> *virtualAddr = 0;<br> viewBase.QuadPart = (ULONGLONG) (*phyAddr);<br><br> ntStatus = _NtMapViewOfSection(hPhyMem, (HANDLE)-1, virtualAddr, 0,<br> *length, &viewBase, length,<br> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ViewShare, 0, PAGE_READWRITE );<br><br> if(ntStatus != STATUS_SUCCESS)<br> {<br> printf(&quot;Failed to map physical memory view of length %X at %X!&quot;, *length, *phyAddr);<br> return FALSE; <br> }<br><br> *phyAddr = viewBase.LowPart;<br> return TRUE;<br>}<br><br><br>//*******************************************************************************************************<br>// Unmap section of physical memory<br>//<br>//*******************************************************************************************************<br><br>void unmapPhyMem(DWORD virtualAddr)<br>{<br> NTSTATUS status;<br><br> status = _NtUnmapViewOfSection((HANDLE)-1, (PVOID)virtualAddr);<br> if(status != STATUS_SUCCESS)<br> {<br> printf(&quot;Unmapping view failed!/n&quot;);<br> }<br>}<br><br><br>//*******************************************************************************************************<br>// Assign SECTION_MAP_WRITE assess of /device/physicalmemory to current user.<br>//<br>//*******************************************************************************************************<br><br>BOOL assignACL(void)<br>{<br> HANDLE hPhyMem;<br> OBJECT_ATTRIBUTES oAttr;<br> BOOL result = FALSE;<br><br> ANSI_STRING aStr;<br> <br> _RtlInitAnsiString(&aStr, &quot;//device//physicalmemory&quot;);<br> <br> UNICODE_STRING uStr;<br><br> if(_RtlAnsiStringToUnicodeString(&uStr, &aStr, TRUE) != STATUS_SUCCESS)<br> { <br> return FALSE;<br> }<br><br> &nbsp; &nbsp;oAttr.Length = sizeof(OBJECT_ATTRIBUTES);<br> &nbsp; &nbsp;oAttr.RootDirectory = NULL;<br> &nbsp; &nbsp;oAttr.Attributes = OBJ_CASE_INSENSITIVE;<br> &nbsp; &nbsp;oAttr.ObjectName = &uStr;<br> &nbsp; &nbsp;oAttr.SecurityDescriptor = NULL;<br> &nbsp; &nbsp;oAttr.SecurityQualityOfService = NULL;<br><br> if(_NtOpenSection(&hPhyMem, READ_CONTROL | WRITE_DAC, &oAttr ) != STATUS_SUCCESS)<br> { <br> return FALSE;<br> }<br> else<br> {<br> PACL dacl;<br> PSECURITY_DESCRIPTOR sd;<br> <br> if(GetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,<br> &dacl, NULL, &sd) == ERROR_SUCCESS)<br> {<br> EXPLICIT_ACCESS ea;<br> char userName[MAX_PATH];<br> DWORD userNameSize = MAX_PATH-1;<br><br> GetUserName(userName, &userNameSize);<br> ea.grfAccessPermissions = SECTION_MAP_WRITE;<br> ea.grfAccessMode = GRANT_ACCESS;<br> ea.grfInheritance = NO_INHERITANCE;<br> ea.Trustee.pMultipleTrustee = NULL;<br> ea.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;<br> ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;<br> ea.Trustee.TrusteeType = TRUSTEE_IS_USER;<br> ea.Trustee.ptstrName = userName;<br><br> PACL newDacl;<br> if(SetEntriesInAcl(1, &ea, dacl, &newDacl) == ERROR_SUCCESS)<br> {<br> if(SetSecurityInfo(hPhyMem, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL,<br> newDacl, NULL) == ERROR_SUCCESS)<br> { <br> result = TRUE;<br> }<br><br> LocalFree(newDacl);<br> }<br> }<br> }<br><br> return result; <br>}<br><br><br>//*******************************************************************************************************<br>// Gets the kernel base address<br>//<br>//*******************************************************************************************************<br><br>DWORD getKernelBase(void)<br>{<br> HANDLE hHeap = GetProcessHeap();<br> <br> NTSTATUS Status;<br> &nbsp; &nbsp;ULONG cbBuffer = 0x8000;<br> &nbsp; &nbsp;PVOID pBuffer = NULL;<br> DWORD retVal = DEF_KERNEL_BASE;<br><br> &nbsp; &nbsp;do<br> &nbsp; &nbsp;{<br> pBuffer = HeapAlloc(hHeap, 0, cbBuffer);<br> if (pBuffer == NULL)<br> return DEF_KERNEL_BASE;<br><br> Status = _NtQuerySystemInformation(SystemModuleInformation,<br> pBuffer, cbBuffer, NULL);<br><br> if(Status == STATUS_INFO_LENGTH_MISMATCH)<br> {<br> HeapFree(hHeap, 0, pBuffer);<br> cbBuffer *= 2;<br> }<br> else if(Status != STATUS_SUCCESS)<br> {<br> HeapFree(hHeap, 0, pBuffer);<br> return DEF_KERNEL_BASE;<br> }<br> &nbsp; &nbsp;}<br> &nbsp; &nbsp;while (Status == STATUS_INFO_LENGTH_MISMATCH);<br><br> DWORD numEntries = *((DWORD *)pBuffer);<br> SYSTEM_MODULE_INFORMATION *smi = (SYSTEM_MODULE_INFORMATION *)((char *)pBuffer + sizeof(DWORD));<br><br> for(DWORD i = 0; i &lt; numEntries; i++)<br> {<br> if(strcmpi(smi-&gt;ImageName, &quot;ntoskrnl.exe&quot;))<br> {<br> //printf(&quot;%.8X - %s/n&quot;, smi-&gt;Base, smi-&gt;ImageName);<br> retVal = (DWORD)(smi-&gt;Base);<br> break;<br> }<br> smi++;<br> }<br><br> HeapFree(hHeap, 0, pBuffer);<br><br> return retVal;<br>}<br><br><br>struct FixupBlock<br>{<br> unsigned long pageRVA;<br> unsigned long blockSize;<br>};<br><br><br><br>BOOL checkKiServiceTableAddr(PVOID exeAddr, DWORD chkAddr, PE_ExtHeader *peXH2)<br>{<br> if(peXH2-&gt;relocationTableAddress && peXH2-&gt;relocationTableSize)<br> {<br> FixupBlock *fixBlk = (FixupBlock *)((char *)exeAddr + peXH2-&gt;relocationTableAddress); <br><br> while(fixBlk-&gt;blockSize)<br> {<br> int numEntries = (fixBlk-&gt;blockSize - sizeof(FixupBlock)) &gt;&gt; 1;<br> <br> unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);<br><br> for(int i = 0; i &lt; numEntries; i++)<br> { <br> int relocType = (*offsetPtr & 0xF000) &gt;&gt; 12;<br> <br> if(relocType == 3)<br> {<br> DWORD *codeLoc = (DWORD *)((char *)exeAddr + fixBlk-&gt;pageRVA + (*offsetPtr & 0x0FFF));<br> <br> if(fixBlk-&gt;pageRVA + (*offsetPtr & 0x0FFF) + peXH2-&gt;imageBase == chkAddr)<br> {<br> return TRUE;<br> }<br> }<br> offsetPtr++;<br> }<br> fixBlk = (FixupBlock *)offsetPtr;<br> }<br> }<br> return FALSE;<br>}<br><br><br>// Thanks to 90210 for this excellent way of getting the KiServiceTable address from the disk image of<br>// ntoskrnl.exe<br>// http://www.rootkit.com/newsread.php?newsid=176<br><br>DWORD getKiServiceTableAddr(PVOID exeAddr, DWORD sdtAddr, PE_ExtHeader *peXH2)<br>{<br> if(peXH2-&gt;relocationTableAddress && peXH2-&gt;relocationTableSize)<br> {<br> FixupBlock *fixBlk = (FixupBlock *)((char *)exeAddr + peXH2-&gt;relocationTableAddress); <br><br> while(fixBlk-&gt;blockSize)<br> {<br> //printf(&quot;Addr = %X/n&quot;, fixBlk-&gt;pageRVA);<br> //printf(&quot;Size = %X/n&quot;, fixBlk-&gt;blockSize);<br><br> int numEntries = (fixBlk-&gt;blockSize - sizeof(FixupBlock)) &gt;&gt; 1;<br> //printf(&quot;Num Entries = %d/n&quot;, numEntries);<br><br> unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);<br><br> for(int i = 0; i &lt; numEntries; i++)<br> { <br> int relocType = (*offsetPtr & 0xF000) &gt;&gt; 12;<br> <br> //printf(&quot;Val = %X/n&quot;, *offsetPtr);<br> //printf(&quot;Type = %X/n&quot;, relocType);<br><br> if(relocType == 3)<br> {<br> DWORD *codeLoc = (DWORD *)((char *)exeAddr + fixBlk-&gt;pageRVA + (*offsetPtr & 0x0FFF)); <br><br> if(*codeLoc == sdtAddr + peXH2-&gt;imageBase &&<br> *(WORD *)((DWORD)codeLoc - 2) == 0x05c7)<br> {<br> DWORD kiServiceTableAddr = *(DWORD *)((DWORD)codeLoc + 4);<br> <br> // checks for presence of found address in the relocation table<br> if(checkKiServiceTableAddr(exeAddr, kiServiceTableAddr, peXH2))<br> {<br> return kiServiceTableAddr - peXH2-&gt;imageBase;<br> }<br> } <br> }<br><br> offsetPtr++;<br> }<br><br> fixBlk = (FixupBlock *)offsetPtr;<br> }<br> }<br> return 0;<br>}<br><br>//*******************************************************************************************************<br>// Program entry point<br>// No commandline arguments required.<br>//<br>//*******************************************************************************************************<br><br>int main(int argc, char* argv[])<br>{<br> MZHeader mzH2;<br> PE_Header peH2;<br> PE_ExtHeader peXH2;<br> SectionHeader *secHdr2;<br><br> printf(&quot;SDTrestore Version 0.2 Proof-of-Concept by SIG^2 G-TEC (www.security.org.sg)/n/n&quot;);<br><br> OSVERSIONINFO ov;<br> ov.dwOSVersionInfoSize = sizeof(ov);<br> GetVersionEx(&ov);<br> if(ov.dwMajorVersion != 5) &nbsp;//主版本号<br> {<br> printf(&quot;Sorry, this version supports only Win2K and WinXP./n&quot;);<br> return 1;<br> }<br><br> if(ov.dwMinorVersion != 0 && ov.dwMinorVersion != 1) //副版本<br> {<br> printf(&quot;Sorry, this version supports only Win2K and WinXP./n&quot;);<br> return 1;<br> }<br> gWinVersion = ov.dwMinorVersion;<br><br> if(!getNativeAPIs()) &nbsp;//取得要用的NT类函数地址<br> {<br> printf(&quot;Failed to get addresses of Native APIs!/n&quot;);<br> return 1;<br> }<br><br> assignACL();<br> HANDLE hPhyMem = openPhyMem();<br> if(hPhyMem == INVALID_HANDLE_VALUE)<br> assignACL();<br><br> hPhyMem = openPhyMem();<br> if(hPhyMem == INVALID_HANDLE_VALUE)<br> {<br> printf(&quot;Could not open physical memory device!/nMake sure you are running as Administrator./n&quot;);<br> return 1;<br> }<br><br> PVOID exeAddr = loadDLL(&quot;//ntoskrnl.exe&quot;);<br> if(!exeAddr)<br> {<br> printf(&quot;Failed to load ntoskrnl.exe!/n&quot;);<br> return 1;<br> }<br><br> DWORD sdtAddr = procAPIExportAddr((DWORD)exeAddr, &quot;KeServiceDescriptorTable&quot;);<br> if(!sdtAddr)<br> {<br> printf(&quot;Failed to get address of KeServiceDescriptorTable!/n&quot;);<br> return 1;<br> }<br> <br> if(!readPEInfo((char *)exeAddr, &mzH2, &peH2, &peXH2, &secHdr2))<br> {<br> printf(&quot;Failed to get PE header of ntoskrnl.exe!/n&quot;);<br> return 1;<br> }<br><br> DWORD kernelPhyBase = getKernelBase() - PROT_MEMBASE;<br> DWORD kernelOffset = kernelPhyBase - peXH2.imageBase;<br><br> printf(&quot;KeServiceDescriptorTable/t/t%X/n&quot;, sdtAddr + kernelPhyBase + PROT_MEMBASE);<br><br> unsigned char *ptr = NULL;<br> DWORD pAddr = sdtAddr + kernelPhyBase;<br> DWORD wantedAddr = pAddr;<br> DWORD len = 0x2000;<br><br> // map in page containing KeServiceDecriptorTable<br> if(mapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr))<br> {<br> DWORD start = wantedAddr - pAddr;<br> DWORD serviceTableAddr, sdtCount; <br> DWORD wantedBytes = len - start;<br> if(wantedBytes &gt;= 4)<br> {<br> serviceTableAddr = *((DWORD *)(&ptr[start]));<br> printf(&quot;KeServiceDecriptorTable.ServiceTable/t%X/n&quot;, serviceTableAddr);<br> if(wantedBytes &gt;= 12)<br> {<br> sdtCount = *(((DWORD *)(&ptr[start])) + 2);<br> printf(&quot;KeServiceDescriptorTable.ServiceLimit/t%d/n&quot;, sdtCount);<br> }<br> }<br> else<br> {<br> printf(&quot;Sorry, an unexpected situation occurred!/n&quot;);<br> return 1;<br> }<br><br> unmapPhyMem((DWORD)ptr);<br> printf(&quot;/n&quot;);<br><br> if(sdtCount &gt;= 300)<br> {<br> printf(&quot;Sorry, an unexpected error occurred! SDT Count &gt; 300???/n&quot;);<br> return 1;<br> }<br><br> pAddr = serviceTableAddr - PROT_MEMBASE;<br> wantedAddr = pAddr;<br> ptr = NULL;<br> len = 0x2000;<br> if(mapPhyMem(hPhyMem, &pAddr, &len, (LPVOID *)&ptr))<br> {<br> start = wantedAddr - pAddr;<br> DWORD numEntries = (len - start) &gt;&gt; 2;<br> if(numEntries &gt;= sdtCount)<br> {<br> char **nativeApiNames = NULL;<br> nativeApiNames = (char **)malloc(sizeof(char *) * sdtCount);<br> if(!nativeApiNames)<br> {<br> printf(&quot;Failed to allocate memory for Native API name table./n&quot;);<br> return 1;<br> }<br> memset(nativeApiNames, 0, sizeof(char *) * sdtCount);<br><br> PVOID ntdll = loadDLL(&quot;//ntdll.dll&quot;);<br> if(!ntdll)<br> {<br> printf(&quot;Failed to load ntdll.dll!/n&quot;);<br> return 1;<br> }<br><br> buildNativeAPITable((DWORD)ntdll, nativeApiNames, sdtCount);<br><br> DWORD *serviceTable = (DWORD *)(&ptr[start]);<br> DWORD *fileServiceTable = (DWORD *)((DWORD)exeAddr + wantedAddr - kernelOffset - peXH2.imageBase);<br> <br> // calculate address based on 90210's suggestion<br> DWORD fileAddr2 = (DWORD)exeAddr + getKiServiceTableAddr(exeAddr, sdtAddr, &peXH2); <br> <br> if(fileAddr2 && (DWORD)fileServiceTable != fileAddr2)<br> {<br> printf(&quot;Two possible addresses of KiServiceTable were found./n/n&quot;);<br> printf(&quot;1 - %.8X/n&quot;, fileServiceTable);<br> printf(&quot;2 - %.8X (using method suggested by 90210)/n/n&quot;, fileAddr2);<br> printf(&quot;Select One (1-2): &quot;);<br><br> char choice[10];<br> memset(choice, 0, sizeof(choice));<br> fgets(choice, sizeof(choice) - 1, stdin);<br> printf(&quot;/n&quot;); <br> int intChoice = atoi(choice);<br> if(intChoice &lt; 1 || intChoice &gt; 2)<br> {<br> printf(&quot;Invalid selection!/n&quot;);<br> unmapPhyMem((DWORD)ptr);<br> return 1;<br> }<br> else if(intChoice == 2)<br> fileServiceTable = (DWORD *)fileAddr2;<br> }<br> <br> if(!IsBadReadPtr(fileServiceTable, sizeof(DWORD)) && <br> &nbsp; !IsBadReadPtr(&fileServiceTable[sdtCount-1], sizeof(DWORD)))<br> {<br> DWORD hookCount = 0;<br> for(DWORD i = 0; i &lt; sdtCount; i++)<br> { <br> if((serviceTable - PROT_MEMBASE - kernelOffset) != fileServiceTable)<br> {<br> printf(&quot;%-25s %3X --[hooked by unknown at %X]--/n&quot;, <br> &nbsp;(nativeApiNames ? nativeApiNames : &quot;Unknown API&quot;), <br> &nbsp;i, serviceTable);<br> hookCount++;<br> }<br> <br> }<br> printf(&quot;/nNumber of Service Table entries hooked = %u/n&quot;, hookCount);<br> <br> if(hookCount)<br> {<br> printf(&quot;/nWARNING: &nbsp;THIS IS EXPERIMENTAL CODE. &nbsp;FIXING THE SDT MAY HAVE GRAVE/n&quot;<br> &nbsp; &quot;CONSEQUENCES, SUCH AS SYSTEM CRASH, DATA LOSS OR SYSTEM CORRUPTION./n&quot;<br> &nbsp; &quot;PROCEED AT YOUR OWN RISK. &nbsp;YOU HAVE BEEN WARNED./n&quot;);<br> printf(&quot;/nFix SDT Entries (Y/N)? : &quot;);<br> <br> char inputReply[10];<br> memset(inputReply, 0, sizeof(inputReply));<br> fgets(inputReply, sizeof(inputReply) - 1, stdin);<br> printf(&quot;/n&quot;);<br> <br> if(stricmp(inputReply, &quot;y/n&quot;) == 0)<br> {<br> for(DWORD i = 0; i &lt; sdtCount; i++)<br> {<br> if((serviceTable - PROT_MEMBASE - kernelOffset) != fileServiceTable)<br> {<br> serviceTable = fileServiceTable + PROT_MEMBASE + kernelOffset;<br> printf(&quot;[+] Patched SDT entry %.2X to %.8X/n&quot;, i, <br> fileServiceTable + PROT_MEMBASE + kernelOffset);<br> }<br> }<br> }<br> else<br> printf(&quot;[-] SDT Entries NOT fixed./n&quot;);<br> }<br> }<br> else<br> {<br> printf(&quot;It's likely that the SDT service table has been relocated./n&quot;<br> &nbsp; &quot;This POC code cannot support patching of relocated SDT service table./n&quot;);<br> }<br><br> }<br> unmapPhyMem((DWORD)ptr);<br> }<br> }<br><br> return 0;<br>}
 
绕过系统的API,直接调用NativeAPI.<br>应用软件-&gt;系统API-&gt;NativeAPI.<br>所有的API都是对系统的NativeAPI的包装.直接调用NativeAPI好了.这样就能躲过对对系统API的Hook.对于NativeAPI的Hook就要通过RootKit技术来实现了.对于RootKit和防RootKit的技术现在是交替占上风.这个就涉及到黑客和安全技术的顶级了,如果能动用了RootKit技术Hook了NativeAPI的在DFW上都找不出一个半个吧.就算是有能力写这个的黑客也不是很多啊.
 
wangsea给出的就是类似RootKit技术恢复API地址的方式.<br>其实还有一招.<br>就是我们调用的函数是导入表中所指向的函数地址.正常调用的时候Jmp这个函数<br>HOOK技术呢就是替换这个地址.<br>我们也可以抢在Hook之前得到这些函数的地址.保存起来.以后通过函数指针的方式调用.<br>或者干脆把LoadLibrary方式动态加载系统.DLL.GetProcAddress方式得到函数地址.通过函数指针调用.这样这些函数就不存在于应用程序的导入表中了.也没办法被HOOK了.至于LoadLibrary, GetProcAddress函数地址的动态确定病毒用的较多.<br>一种方式就是应编码方式.根据操作系统不同写死几种判断.另一种方式就是暴力搜索的方式确定这两个函数的地址..
 
感谢楼上各位的支持<br>我正在实行中<br>其实我的目的很简单<br>就是想尝试对其他软件做一个防护<br>确保我软件进行api hook后其他软件无法hook
 
后退
顶部