寫在前面
此係列是本人一個字一個字碼出來的,包括示例和實驗截圖。可能有錯誤或者不全面的地方,如有錯誤,歡迎批評指正,本教程將會長期更新。 如有好的建議,歡迎反饋。碼字不易,如果本篇文章有幫助你的,如有閒錢,可以打賞支援我的創作。如想轉載,請把我的轉載資訊附在文章後面,並宣告我的個人資訊和本人部落格地址即可,但必須事先通知我。
你如果是從中間插過來看的,請仔細閱讀 羽夏逆向指引——序 ,方便學習本教程。
前言
本篇講解常見的在3環的破解者常見的破解措施以及反制,不要跟我講0環的,在0環我可以隨便搞3環的程式,許可權大壓死等級低的程式。本篇主要是介紹常見的,故不能涵蓋絕大多數的。
隨機基址
英文縮寫為ASLR
,它是一種針對緩衝區溢位的安全保護技術,通過對堆、棧、共享庫對映等線性區佈局的隨機化,通過增加攻擊者預測目的地址的難度,防止攻擊者直接定位攻擊程式碼位置,達到阻止溢位攻擊的目的的一種技術。
怎麼知道一個exe
程式啟用了ASLR
呢?它位於PE
結構的一個成員,如下圖所示:
我們可以將該成員改為0就可以取消隨機基址保護,但是如果程式有自校驗,且我們無法幹掉,如何獲得程式基地址呢?答案就是從TEB
獲取,如下是其結構體:
kd> dt _teb
nt!_TEB
+0x000 NtTib : _NT_TIB
+0x01c EnvironmentPointer : Ptr32 Void
+0x020 ClientId : _CLIENT_ID
+0x028 ActiveRpcHandle : Ptr32 Void
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
+0x034 LastErrorValue : Uint4B
+0x038 CountOfOwnedCriticalSections : Uint4B
+0x03c CsrClientThread : Ptr32 Void
+0x040 Win32ThreadInfo : Ptr32 Void
+0x044 User32Reserved : [26] Uint4B
+0x0ac UserReserved : [5] Uint4B
+0x0c0 WOW32Reserved : Ptr32 Void
+0x0c4 CurrentLocale : Uint4B
+0x0c8 FpSoftwareStatusRegister : Uint4B
+0x0cc SystemReserved1 : [54] Ptr32 Void
+0x1a4 ExceptionCode : Int4B
+0x1a8 ActivationContextStack : _ACTIVATION_CONTEXT_STACK
+0x1bc SpareBytes1 : [24] UChar
+0x1d4 GdiTebBatch : _GDI_TEB_BATCH
+0x6b4 RealClientId : _CLIENT_ID
+0x6bc GdiCachedProcessHandle : Ptr32 Void
+0x6c0 GdiClientPID : Uint4B
+0x6c4 GdiClientTID : Uint4B
+0x6c8 GdiThreadLocalInfo : Ptr32 Void
+0x6cc Win32ClientInfo : [62] Uint4B
+0x7c4 glDispatchTable : [233] Ptr32 Void
+0xb68 glReserved1 : [29] Uint4B
+0xbdc glReserved2 : Ptr32 Void
+0xbe0 glSectionInfo : Ptr32 Void
+0xbe4 glSection : Ptr32 Void
+0xbe8 glTable : Ptr32 Void
+0xbec glCurrentRC : Ptr32 Void
+0xbf0 glContext : Ptr32 Void
+0xbf4 LastStatusValue : Uint4B
+0xbf8 StaticUnicodeString : _UNICODE_STRING
+0xc00 StaticUnicodeBuffer : [261] Uint2B
+0xe0c DeallocationStack : Ptr32 Void
+0xe10 TlsSlots : [64] Ptr32 Void
+0xf10 TlsLinks : _LIST_ENTRY
+0xf18 Vdm : Ptr32 Void
+0xf1c ReservedForNtRpc : Ptr32 Void
+0xf20 DbgSsReserved : [2] Ptr32 Void
+0xf28 HardErrorsAreDisabled : Uint4B
+0xf2c Instrumentation : [16] Ptr32 Void
+0xf6c WinSockData : Ptr32 Void
+0xf70 GdiBatchCount : Uint4B
+0xf74 InDbgPrint : UChar
+0xf75 FreeStackOnTermination : UChar
+0xf76 HasFiberData : UChar
+0xf77 IdealProcessor : UChar
+0xf78 Spare3 : Uint4B
+0xf7c ReservedForPerf : Ptr32 Void
+0xf80 ReservedForOle : Ptr32 Void
+0xf84 WaitingOnLoaderLock : Uint4B
+0xf88 Wx86Thread : _Wx86ThreadState
+0xf94 TlsExpansionSlots : Ptr32 Ptr32 Void
+0xf98 ImpersonationLocale : Uint4B
+0xf9c IsImpersonating : Uint4B
+0xfa0 NlsCache : Ptr32 Void
+0xfa4 pShimData : Ptr32 Void
+0xfa8 HeapVirtualAffinity : Uint4B
+0xfac CurrentTransactionHandle : Ptr32 Void
+0xfb0 ActiveFrame : Ptr32 _TEB_ACTIVE_FRAME
+0xfb4 SafeThunkCall : UChar
+0xfb5 BooleanSpare : [3] UChar
要取得我們想要的程式基地址,我們需要從TEB
獲取PEB
,也就是0x30
偏移處的成員,如下是其結構:
kd> dt _peb
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 SpareBool : UChar
+0x004 Mutant : Ptr32 Void
+0x008 ImageBaseAddress : Ptr32 Void
+0x00c Ldr : Ptr32 _PEB_LDR_DATA
+0x010 ProcessParameters : Ptr32 _RTL_USER_PROCESS_PARAMETERS
+0x014 SubSystemData : Ptr32 Void
+0x018 ProcessHeap : Ptr32 Void
+0x01c FastPebLock : Ptr32 _RTL_CRITICAL_SECTION
+0x020 FastPebLockRoutine : Ptr32 Void
+0x024 FastPebUnlockRoutine : Ptr32 Void
+0x028 EnvironmentUpdateCount : Uint4B
+0x02c KernelCallbackTable : Ptr32 Void
+0x030 SystemReserved : [1] Uint4B
+0x034 AtlThunkSListPtr32 : Uint4B
+0x038 FreeList : Ptr32 _PEB_FREE_BLOCK
+0x03c TlsExpansionCounter : Uint4B
+0x040 TlsBitmap : Ptr32 Void
+0x044 TlsBitmapBits : [2] Uint4B
+0x04c ReadOnlySharedMemoryBase : Ptr32 Void
+0x050 ReadOnlySharedMemoryHeap : Ptr32 Void
+0x054 ReadOnlyStaticServerData : Ptr32 Ptr32 Void
+0x058 AnsiCodePageData : Ptr32 Void
+0x05c OemCodePageData : Ptr32 Void
+0x060 UnicodeCaseTableData : Ptr32 Void
+0x064 NumberOfProcessors : Uint4B
+0x068 NtGlobalFlag : Uint4B
+0x070 CriticalSectionTimeout : _LARGE_INTEGER
+0x078 HeapSegmentReserve : Uint4B
+0x07c HeapSegmentCommit : Uint4B
+0x080 HeapDeCommitTotalFreeThreshold : Uint4B
+0x084 HeapDeCommitFreeBlockThreshold : Uint4B
+0x088 NumberOfHeaps : Uint4B
+0x08c MaximumNumberOfHeaps : Uint4B
+0x090 ProcessHeaps : Ptr32 Ptr32 Void
+0x094 GdiSharedHandleTable : Ptr32 Void
+0x098 ProcessStarterHelper : Ptr32 Void
+0x09c GdiDCAttributeList : Uint4B
+0x0a0 LoaderLock : Ptr32 Void
+0x0a4 OSMajorVersion : Uint4B
+0x0a8 OSMinorVersion : Uint4B
+0x0ac OSBuildNumber : Uint2B
+0x0ae OSCSDVersion : Uint2B
+0x0b0 OSPlatformId : Uint4B
+0x0b4 ImageSubsystem : Uint4B
+0x0b8 ImageSubsystemMajorVersion : Uint4B
+0x0bc ImageSubsystemMinorVersion : Uint4B
+0x0c0 ImageProcessAffinityMask : Uint4B
+0x0c4 GdiHandleBuffer : [34] Uint4B
+0x14c PostProcessInitRoutine : Ptr32 void
+0x150 TlsExpansionBitmap : Ptr32 Void
+0x154 TlsExpansionBitmapBits : [32] Uint4B
+0x1d4 SessionId : Uint4B
+0x1d8 AppCompatFlags : _ULARGE_INTEGER
+0x1e0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x1e8 pShimData : Ptr32 Void
+0x1ec AppCompatInfo : Ptr32 Void
+0x1f0 CSDVersion : _UNICODE_STRING
+0x1f8 ActivationContextData : Ptr32 Void
+0x1fc ProcessAssemblyStorageMap : Ptr32 Void
+0x200 SystemDefaultActivationContextData : Ptr32 Void
+0x204 SystemAssemblyStorageMap : Ptr32 Void
+0x208 MinimumStackCommit : Uint4B
ImageBaseAddress
就是映象載入地址,通過它我們就可以獲得程式被載入到記憶體的首地址。
上面巴拉巴拉一大堆沒說TEB
如何獲取,它儲存在fs
暫存器中,我們可以用如下程式碼進行獲取:
mov eax,dword ptr fs:[0x30];
mov eax,dword ptr ds:[eax+0x8];
注意,如上是在32位系統下才有效,對於64位系統下執行64位程式的TEB
和PEB
結構體發生了一些變化:
PEB
結構體:
kd> dt _PEB
nt!_PEB
+0x000 InheritedAddressSpace : UChar
+0x001 ReadImageFileExecOptions : UChar
+0x002 BeingDebugged : UChar
+0x003 BitField : UChar
+0x003 ImageUsesLargePages : Pos 0, 1 Bit
+0x003 IsProtectedProcess : Pos 1, 1 Bit
+0x003 IsImageDynamicallyRelocated : Pos 2, 1 Bit
+0x003 SkipPatchingUser32Forwarders : Pos 3, 1 Bit
+0x003 IsPackagedProcess : Pos 4, 1 Bit
+0x003 IsAppContainer : Pos 5, 1 Bit
+0x003 IsProtectedProcessLight : Pos 6, 1 Bit
+0x003 IsLongPathAwareProcess : Pos 7, 1 Bit
+0x004 Padding0 : [4] UChar
+0x008 Mutant : Ptr64 Void
+0x010 ImageBaseAddress : Ptr64 Void
+0x018 Ldr : Ptr64 _PEB_LDR_DATA
+0x020 ProcessParameters : Ptr64 _RTL_USER_PROCESS_PARAMETERS
+0x028 SubSystemData : Ptr64 Void
+0x030 ProcessHeap : Ptr64 Void
+0x038 FastPebLock : Ptr64 _RTL_CRITICAL_SECTION
+0x040 AtlThunkSListPtr : Ptr64 _SLIST_HEADER
+0x048 IFEOKey : Ptr64 Void
+0x050 CrossProcessFlags : Uint4B
+0x050 ProcessInJob : Pos 0, 1 Bit
+0x050 ProcessInitializing : Pos 1, 1 Bit
+0x050 ProcessUsingVEH : Pos 2, 1 Bit
+0x050 ProcessUsingVCH : Pos 3, 1 Bit
+0x050 ProcessUsingFTH : Pos 4, 1 Bit
+0x050 ProcessPreviouslyThrottled : Pos 5, 1 Bit
+0x050 ProcessCurrentlyThrottled : Pos 6, 1 Bit
+0x050 ProcessImagesHotPatched : Pos 7, 1 Bit
+0x050 ReservedBits0 : Pos 8, 24 Bits
+0x054 Padding1 : [4] UChar
+0x058 KernelCallbackTable : Ptr64 Void
+0x058 UserSharedInfoPtr : Ptr64 Void
+0x060 SystemReserved : Uint4B
+0x064 AtlThunkSListPtr32 : Uint4B
+0x068 ApiSetMap : Ptr64 Void
+0x070 TlsExpansionCounter : Uint4B
+0x074 Padding2 : [4] UChar
+0x078 TlsBitmap : Ptr64 Void
+0x080 TlsBitmapBits : [2] Uint4B
+0x088 ReadOnlySharedMemoryBase : Ptr64 Void
+0x090 SharedData : Ptr64 Void
+0x098 ReadOnlyStaticServerData : Ptr64 Ptr64 Void
+0x0a0 AnsiCodePageData : Ptr64 Void
+0x0a8 OemCodePageData : Ptr64 Void
+0x0b0 UnicodeCaseTableData : Ptr64 Void
+0x0b8 NumberOfProcessors : Uint4B
+0x0bc NtGlobalFlag : Uint4B
+0x0c0 CriticalSectionTimeout : _LARGE_INTEGER
+0x0c8 HeapSegmentReserve : Uint8B
+0x0d0 HeapSegmentCommit : Uint8B
+0x0d8 HeapDeCommitTotalFreeThreshold : Uint8B
+0x0e0 HeapDeCommitFreeBlockThreshold : Uint8B
+0x0e8 NumberOfHeaps : Uint4B
+0x0ec MaximumNumberOfHeaps : Uint4B
+0x0f0 ProcessHeaps : Ptr64 Ptr64 Void
+0x0f8 GdiSharedHandleTable : Ptr64 Void
+0x100 ProcessStarterHelper : Ptr64 Void
+0x108 GdiDCAttributeList : Uint4B
+0x10c Padding3 : [4] UChar
+0x110 LoaderLock : Ptr64 _RTL_CRITICAL_SECTION
+0x118 OSMajorVersion : Uint4B
+0x11c OSMinorVersion : Uint4B
+0x120 OSBuildNumber : Uint2B
+0x122 OSCSDVersion : Uint2B
+0x124 OSPlatformId : Uint4B
+0x128 ImageSubsystem : Uint4B
+0x12c ImageSubsystemMajorVersion : Uint4B
+0x130 ImageSubsystemMinorVersion : Uint4B
+0x134 Padding4 : [4] UChar
+0x138 ActiveProcessAffinityMask : Uint8B
+0x140 GdiHandleBuffer : [60] Uint4B
+0x230 PostProcessInitRoutine : Ptr64 void
+0x238 TlsExpansionBitmap : Ptr64 Void
+0x240 TlsExpansionBitmapBits : [32] Uint4B
+0x2c0 SessionId : Uint4B
+0x2c4 Padding5 : [4] UChar
+0x2c8 AppCompatFlags : _ULARGE_INTEGER
+0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER
+0x2d8 pShimData : Ptr64 Void
+0x2e0 AppCompatInfo : Ptr64 Void
+0x2e8 CSDVersion : _UNICODE_STRING
+0x2f8 ActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA
+0x300 ProcessAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP
+0x308 SystemDefaultActivationContextData : Ptr64 _ACTIVATION_CONTEXT_DATA
+0x310 SystemAssemblyStorageMap : Ptr64 _ASSEMBLY_STORAGE_MAP
+0x318 MinimumStackCommit : Uint8B
+0x320 SparePointers : [4] Ptr64 Void
+0x340 SpareUlongs : [5] Uint4B
+0x358 WerRegistrationData : Ptr64 Void
+0x360 WerShipAssertPtr : Ptr64 Void
+0x368 pUnused : Ptr64 Void
+0x370 pImageHeaderHash : Ptr64 Void
+0x378 TracingFlags : Uint4B
+0x378 HeapTracingEnabled : Pos 0, 1 Bit
+0x378 CritSecTracingEnabled : Pos 1, 1 Bit
+0x378 LibLoaderTracingEnabled : Pos 2, 1 Bit
+0x378 SpareTracingBits : Pos 3, 29 Bits
+0x37c Padding6 : [4] UChar
+0x380 CsrServerReadOnlySharedMemoryBase : Uint8B
+0x388 TppWorkerpListLock : Uint8B
+0x390 TppWorkerpList : _LIST_ENTRY
+0x3a0 WaitOnAddressHashTable : [128] Ptr64 Void
+0x7a0 TelemetryCoverageHeader : Ptr64 Void
+0x7a8 CloudFileFlags : Uint4B
+0x7ac CloudFileDiagFlags : Uint4B
+0x7b0 PlaceholderCompatibilityMode : Char
+0x7b1 PlaceholderCompatibilityModeReserved : [7] Char
+0x7b8 LeapSecondData : Ptr64 _LEAP_SECOND_DATA
+0x7c0 LeapSecondFlags : Uint4B
+0x7c0 SixtySecondEnabled : Pos 0, 1 Bit
+0x7c0 Reserved : Pos 1, 31 Bits
+0x7c4 NtGlobalFlag2 : Uint4B
TEB
結構體:
kd> dt _TEB
nt!_TEB
+0x000 NtTib : _NT_TIB
+0x038 EnvironmentPointer : Ptr64 Void
+0x040 ClientId : _CLIENT_ID
+0x050 ActiveRpcHandle : Ptr64 Void
+0x058 ThreadLocalStoragePointer : Ptr64 Void
+0x060 ProcessEnvironmentBlock : Ptr64 _PEB
+0x068 LastErrorValue : Uint4B
+0x06c CountOfOwnedCriticalSections : Uint4B
+0x070 CsrClientThread : Ptr64 Void
+0x078 Win32ThreadInfo : Ptr64 Void
+0x080 User32Reserved : [26] Uint4B
+0x0e8 UserReserved : [5] Uint4B
+0x100 WOW32Reserved : Ptr64 Void
+0x108 CurrentLocale : Uint4B
+0x10c FpSoftwareStatusRegister : Uint4B
+0x110 ReservedForDebuggerInstrumentation : [16] Ptr64 Void
+0x190 SystemReserved1 : [30] Ptr64 Void
+0x280 PlaceholderCompatibilityMode : Char
+0x281 PlaceholderHydrationAlwaysExplicit : UChar
+0x282 PlaceholderReserved : [10] Char
+0x28c ProxiedProcessId : Uint4B
+0x290 _ActivationStack : _ACTIVATION_CONTEXT_STACK
+0x2b8 WorkingOnBehalfTicket : [8] UChar
+0x2c0 ExceptionCode : Int4B
+0x2c4 Padding0 : [4] UChar
+0x2c8 ActivationContextStackPointer : Ptr64 _ACTIVATION_CONTEXT_STACK
+0x2d0 InstrumentationCallbackSp : Uint8B
+0x2d8 InstrumentationCallbackPreviousPc : Uint8B
+0x2e0 InstrumentationCallbackPreviousSp : Uint8B
+0x2e8 TxFsContext : Uint4B
+0x2ec InstrumentationCallbackDisabled : UChar
+0x2ed UnalignedLoadStoreExceptions : UChar
+0x2ee Padding1 : [2] UChar
+0x2f0 GdiTebBatch : _GDI_TEB_BATCH
+0x7d8 RealClientId : _CLIENT_ID
+0x7e8 GdiCachedProcessHandle : Ptr64 Void
+0x7f0 GdiClientPID : Uint4B
+0x7f4 GdiClientTID : Uint4B
+0x7f8 GdiThreadLocalInfo : Ptr64 Void
+0x800 Win32ClientInfo : [62] Uint8B
+0x9f0 glDispatchTable : [233] Ptr64 Void
+0x1138 glReserved1 : [29] Uint8B
+0x1220 glReserved2 : Ptr64 Void
+0x1228 glSectionInfo : Ptr64 Void
+0x1230 glSection : Ptr64 Void
+0x1238 glTable : Ptr64 Void
+0x1240 glCurrentRC : Ptr64 Void
+0x1248 glContext : Ptr64 Void
+0x1250 LastStatusValue : Uint4B
+0x1254 Padding2 : [4] UChar
+0x1258 StaticUnicodeString : _UNICODE_STRING
+0x1268 StaticUnicodeBuffer : [261] Wchar
+0x1472 Padding3 : [6] UChar
+0x1478 DeallocationStack : Ptr64 Void
+0x1480 TlsSlots : [64] Ptr64 Void
+0x1680 TlsLinks : _LIST_ENTRY
+0x1690 Vdm : Ptr64 Void
+0x1698 ReservedForNtRpc : Ptr64 Void
+0x16a0 DbgSsReserved : [2] Ptr64 Void
+0x16b0 HardErrorMode : Uint4B
+0x16b4 Padding4 : [4] UChar
+0x16b8 Instrumentation : [11] Ptr64 Void
+0x1710 ActivityId : _GUID
+0x1720 SubProcessTag : Ptr64 Void
+0x1728 PerflibData : Ptr64 Void
+0x1730 EtwTraceData : Ptr64 Void
+0x1738 WinSockData : Ptr64 Void
+0x1740 GdiBatchCount : Uint4B
+0x1744 CurrentIdealProcessor : _PROCESSOR_NUMBER
+0x1744 IdealProcessorValue : Uint4B
+0x1744 ReservedPad0 : UChar
+0x1745 ReservedPad1 : UChar
+0x1746 ReservedPad2 : UChar
+0x1747 IdealProcessor : UChar
+0x1748 GuaranteedStackBytes : Uint4B
+0x174c Padding5 : [4] UChar
+0x1750 ReservedForPerf : Ptr64 Void
+0x1758 ReservedForOle : Ptr64 Void
+0x1760 WaitingOnLoaderLock : Uint4B
+0x1764 Padding6 : [4] UChar
+0x1768 SavedPriorityState : Ptr64 Void
+0x1770 ReservedForCodeCoverage : Uint8B
+0x1778 ThreadPoolData : Ptr64 Void
+0x1780 TlsExpansionSlots : Ptr64 Ptr64 Void
+0x1788 DeallocationBStore : Ptr64 Void
+0x1790 BStoreLimit : Ptr64 Void
+0x1798 MuiGeneration : Uint4B
+0x179c IsImpersonating : Uint4B
+0x17a0 NlsCache : Ptr64 Void
+0x17a8 pShimData : Ptr64 Void
+0x17b0 HeapData : Uint4B
+0x17b4 Padding7 : [4] UChar
+0x17b8 CurrentTransactionHandle : Ptr64 Void
+0x17c0 ActiveFrame : Ptr64 _TEB_ACTIVE_FRAME
+0x17c8 FlsData : Ptr64 Void
+0x17d0 PreferredLanguages : Ptr64 Void
+0x17d8 UserPrefLanguages : Ptr64 Void
+0x17e0 MergedPrefLanguages : Ptr64 Void
+0x17e8 MuiImpersonation : Uint4B
+0x17ec CrossTebFlags : Uint2B
+0x17ec SpareCrossTebBits : Pos 0, 16 Bits
+0x17ee SameTebFlags : Uint2B
+0x17ee SafeThunkCall : Pos 0, 1 Bit
+0x17ee InDebugPrint : Pos 1, 1 Bit
+0x17ee HasFiberData : Pos 2, 1 Bit
+0x17ee SkipThreadAttach : Pos 3, 1 Bit
+0x17ee WerInShipAssertCode : Pos 4, 1 Bit
+0x17ee RanProcessInit : Pos 5, 1 Bit
+0x17ee ClonedThread : Pos 6, 1 Bit
+0x17ee SuppressDebugMsg : Pos 7, 1 Bit
+0x17ee DisableUserStackWalk : Pos 8, 1 Bit
+0x17ee RtlExceptionAttached : Pos 9, 1 Bit
+0x17ee InitialThread : Pos 10, 1 Bit
+0x17ee SessionAware : Pos 11, 1 Bit
+0x17ee LoadOwner : Pos 12, 1 Bit
+0x17ee LoaderWorker : Pos 13, 1 Bit
+0x17ee SkipLoaderInit : Pos 14, 1 Bit
+0x17ee SpareSameTebBits : Pos 15, 1 Bit
+0x17f0 TxnScopeEnterCallback : Ptr64 Void
+0x17f8 TxnScopeExitCallback : Ptr64 Void
+0x1800 TxnScopeContext : Ptr64 Void
+0x1808 LockCount : Uint4B
+0x180c WowTebOffset : Int4B
+0x1810 ResourceRetValue : Ptr64 Void
+0x1818 ReservedForWdf : Ptr64 Void
+0x1820 ReservedForCrt : Uint8B
+0x1828 EffectiveContainerId : _GUID
並且存放TEB
的暫存器變了,變成了gs
,具體細節請自行學習 羽夏看Win系統核心 系列教程。所以程式碼發生了一些變化,但仍不復雜:
mov rax, qword ptr gs:[0x60];
mov rax, qword ptr ds:[rax+0x10];
有關隨機基址相關介紹,就這麼多。
軟體斷點反制
很多破解者找突破口的時候都會在關鍵函式下斷點,並且下的斷點都是普通的軟體斷點,有關斷點的介紹請自行閱讀 除錯篇——斷點與單步 裡面有比較與軟體斷點詳細的介紹,我就不在這裡贅述了,下面我們實現簡單的反制措施:
#include <iostream>
#include <Windows.h>
#include <stdlib.h>
using namespace std;
BYTE* proc = 0;
UINT WINAPI ThreadProc(LPVOID Param)
{
cout << "開始檢測中……" << endl;
while (true)
{
if (*proc == 0xCC)
cout << "檢測被下軟體斷點!!!" << endl;
}
return 0;
}
int main()
{
HMODULE lib = LoadLibrary(L"user32.dll");
FARPROC msgboxw = GetProcAddress(lib, "MessageBoxW");
proc = (BYTE*)msgboxw;
CloseHandle(CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadProc, NULL, 0, NULL)) ;
system("pause");
return 0;
}
如上是對MessageBoxW
進行檢測,如下是實驗效果:
硬體斷點反制
當軟體斷點暫時無法起作用或者被反檢測的時候,逆向者會嘗試使用硬體斷點,硬體斷點是基於硬體的,通過設定除錯暫存器來設定硬體斷點,具體詳情請閱讀 除錯篇——斷點與單步 ,這裡就不細說了。
軟體斷點也不是無法檢測,我們做個示例來展示一下:
#include <iostream>
#include <Windows.h>
#include <stdlib.h>
using namespace std;
int main(int argc, char* argv[])
{
cout << "開始檢測中……" << endl;
CONTEXT context;
context.ContextFlags = CONTEXT_ALL;
while (true)
{
GetThreadContext((HANDLE)-2, &context);
if (context.Dr0 || context.Dr1 || context.Dr2 || context.Dr3)
{
cout << "檢測被下硬體斷點!!!" << endl;
}
}
system("pause");
return 0;
}
如下是實驗效果:
遮蔽字串特徵
現在所有的註冊都是用圖形使用者介面程式,如果是註冊的話會有不同形式的提醒,如果直接使用字串的話,會大大提高被破解的概率,因為這是破解的一個非常重要的突破口,比如下面的程式碼:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int x = 0;
cout << "Please Input The Key:" << endl;
cin >> x;
if (x == 1234)
{
cout << "Successful,CNBLOG Only!!!" << endl;
}
else
{
cout << "Error,CNBLOG Only!!!" << endl;
}
system("pause");
return 0;
}
經過編譯後,上面的字串會直接被儲存在程式檔案中,我們很容易就能搜到字串定位破解點:
那麼我們如何隱藏它們呢,如果我把程式碼寫成這樣子:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int x = 0;
cout << "Please Input The Key:" << endl;
cin >> x;
char error[] = { 0x45, 0x72, 0x72, 0x6F, 0x72, 0xA3, 0xAC, 0x43, 0x4E, 0x42, 0x4C, 0x4F, 0x47, 0x20, 0x4F, 0x6E, 0x6C, 0x79,0x0 };
char success[] = { 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x66, 0x75, 0x6C, 0xA3, 0xAC, 0x43, 0x4E, 0x42, 0x4C, 0x4F, 0x47, 0x20, 0x4F, 0x6E, 0x6C, 0x79,0x0 };
if (x == 1234)
{
cout << success << endl;
}
else
{
cout << error << endl;
}
system("pause");
return 0;
}
如下是效果圖,你就會發現找不到了:
你就會發現,上面的程式碼被翻譯成這個模樣,當然直接搜是搜不到的:
自校驗檢測反制
自校驗無非就是建立一個執行緒對一塊程式碼區域進行校驗和,當然程式碼區塊的大小需要事後才能知道大小,也可以通過PE
的知識進行動態獲取,如果不知道的話請 羽夏筆記——PE結構(不包含.Net) 進行學習,可以採用任何形成校驗和的演算法進行校驗,比如MD5
、SHA
等,這裡我就不贅述了。
花指令
花指令主要是對抗靜態分析的,可以消耗逆向者的精力,如果花指令設計的十分巧妙且互不重複可以是十分有效的,下面我們實現簡單的花指令:
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
int x = 0;
cout << "Please Input The Key:" << endl;
cin >> x;
_asm
{
_emit 0xEB;
_emit 0x01;
_emit 0xE8;
}
if (x == 1234)
{
cout << "Successful,CNBLOG Only!!!" << endl;
}
else
{
cout << "Error,CNBLOG Only!!!" << endl;
}
system("pause");
return 0;
}
注意上面的彙編程式碼實現只能在32位進行內斂,64位是不允許的,我只是介紹實現原理,如果不懂的話,請仔細閱讀 羽夏筆記——硬編碼(32位) ,這裡就不贅述了。
TLS 反制
TLS
意為執行緒區域性儲存,英文全名為Thread Local Storage
,主要用於給執行緒獨立的傳值,由於執行緒不擁有程式的資源,所以同一程式的幾個執行緒需要獨立賦值時的需要通過TLS
技術。TLS
有一個特點,就是它通常在程式OEP
前就要執行,所以起始TLS
才是個程式真正的開始。利用這一特點,可以用來進行的程式的反除錯,下面我們用程式碼簡單的實現一下:
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#pragma comment(lib,"ntdll.lib")
using namespace std;
#pragma comment(linker,"/INCLUDE:__tls_used")
UINT isDebugging = 0;
#define ThreadHideFromDebugger 17
char error[] = { 0x55,0x62,0x62,0x7f,0x62,0x3c,0x53,0x5e,0x52,0x5c,0x5f,0x57,0x30,0x5f,0x7e,0x7c,0x69,0x31,0x31,0x31,0x0 };
char success[] = { 0x43,0x65,0x73,0x73,0x75,0x63,0x63,0x76,0x65,0x7c,0x3c,0x53,0x5e,0x52,0x5c,0x5f,0x57,0x30,0x5f,0x7e,0x7c,0x69,0x31,0x31,0x31,0x0 };
void NTAPI TLS_CALLBACK_3(PVOID DllHandle, DWORD Reason, PVOID Reserve);
void NTAPI TLS_CALLBACK_2(PVOID DllHandle, DWORD Reason, PVOID Reserve);
void NTAPI TLS_CALLBACK_1(PVOID DllHandle, DWORD Reason, PVOID Reserve);
#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK PTLS_CALLBACKS[] = { TLS_CALLBACK_1,TLS_CALLBACK_2,NULL };
#pragma data_seg()
void NTAPI TLS_CALLBACK_3(PVOID DllHandle, DWORD Reason, PVOID Reserve)
{
for (int i = 0; i < sizeof(error) - 1; i++)
{
error[i] ^= (0x10 | isDebugging);
}
for (int i = 0; i < sizeof(success) - 1; i++)
{
success[i] ^= (0x10 | isDebugging);
}
}
void NTAPI TLS_CALLBACK_2(PVOID DllHandle, DWORD Reason, PVOID Reserve)
{
_asm
{
_emit 0xEB;
_emit 0x01;
_emit 0xE8;
}
NtSetInformationThread((HANDLE)-2, (THREADINFOCLASS)ThreadHideFromDebugger, 0, 0);
}
void NTAPI TLS_CALLBACK_1(PVOID DllHandle, DWORD Reason, PVOID Reserve)
{
if (Reason == DLL_PROCESS_ATTACH)
{
DWORD old;
VirtualProtect(&PTLS_CALLBACKS[1], sizeof(UINT), PAGE_READWRITE, &old);
_asm
{
_emit 0xEB;
_emit 0x01;
_emit 0xE8;
}
NtQueryInformationProcess(HANDLE(-1), ProcessDebugPort, &isDebugging, sizeof(UINT), NULL);
if (!isDebugging)
{
PTLS_CALLBACKS[1] = TLS_CALLBACK_3;
}
VirtualProtect(&PTLS_CALLBACKS[1], sizeof(UINT), old, &old);
}
}
int main(int argc, char* argv[])
{
int x = 0;
cout << "Please Input The Key:" << endl;
cin >> x;
_asm
{
_emit 0xEB;
_emit 0x01;
_emit 0xE8;
}
if (x == 1234)
{
cout << success << endl;
}
else
{
cout << error << endl;
}
system("pause");
return 0;
}
如下程式碼就是使用偵錯程式逃逸,不讓偵錯程式除錯自己:
NtSetInformationThread((HANDLE)-2, (THREADINFOCLASS)ThreadHideFromDebugger, 0, 0);
如上程式碼使用了TLS
,使用了花指令和字串加密,如果處於除錯模式,就算你幹掉了偵錯程式逃逸,你也是無法獲得正確的除錯資訊的。
由於本篇為指引,如果要搞清楚具體細節的話,可能需要大量的基礎和補缺,可以參照我的所有博文。
PEB 反制
這裡的反制就是利用PEB
結構體的BeingDebugged
成員進行檢測,使用匯編對於32位和64位的檢測是不一樣的,我就不贅述了,但是Windows
提供了一個函式IsDebuggerPresent
,它的本質就是查這個成員。這種方法很容易被幹掉。
異常反制
偵錯程式是基於異常進行的,如果想要除錯程式,就必須構造何時的異常來觸發攔截。這裡我們就有一些思路,我們可以丟擲異常干擾偵錯程式除錯,但要保證不能阻礙程式執行的正常運轉。第二個思路就是對於下斷高頻區進行對抗,銷燬異常,不讓它觸發,但難度遠遠高於前者。這裡我們就介紹第一種方式進行:
#include <iostream>
#include <Windows.h>
using namespace std;
LONG WINAPI TOP_LEVEL_EXCEPTION_FILTER(struct _EXCEPTION_POINTERS* ExceptionInfo)
{
if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
{
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
DWORD WINAPI THREAD_START_ROUTINE(LPVOID lpThreadParameter)
{
while (true)
{
_asm
{
int 3;
}
}
return 0;
}
int main(int argc, char* argv[])
{
SetUnhandledExceptionFilter(TOP_LEVEL_EXCEPTION_FILTER);
CloseHandle(CreateThread(NULL, NULL, THREAD_START_ROUTINE, NULL, 0, NULL));
int x = 0;
cout << "Please Input The Key:" << endl;
cin >> x;
if (x == 1234)
{
cout << "Successful,CNBLOG Only!!!" << endl;
}
else
{
cout << "Error,CNBLOG Only!!!" << endl;
}
system("pause");
return 0;
}
通過上述程式碼,我認為製造了一個軟體斷點異常,如果有偵錯程式除錯該程式,則程式就無法執行正常的流程進行處理,從而達到反除錯的目的,如果具體細節不懂的請學習 羽夏看Win系統核心 的 異常篇 和 除錯篇 全部文章。
父程式檢測反制
該種方法針對於僅在桌面資料夾等雙擊執行跑起來的程式有效,否則對於允許其他程式啟動將會成為一種干擾,如下是實現方式:
#include <iostream>
#include <Windows.h>
#include <winternl.h>
#include <psapi.h>
#pragma comment(lib,"ntdll.lib")
using namespace std;
int main(int argc, char* argv[])
{
PROCESS_BASIC_INFORMATION pbi;
NTSTATUS status = NtQueryInformationProcess((HANDLE)-1, ProcessBasicInformation, (PVOID)&pbi,
sizeof(PROCESS_BASIC_INFORMATION), NULL);
if (!status)
{
auto p = (DWORD)pbi.Reserved3;
HANDLE h_Process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, p);
if (h_Process)
{
WCHAR path[MAX_PATH + 1] = { 0 };
GetModuleFileNameEx(h_Process, NULL, path, MAX_PATH + 1);
wstring str(path);
int offset = str.rfind(L"\\");
str = str.substr(offset);
auto b = str.find(L"cmd.exe"); //因為是控制檯程式,所以是 cmd.exe
if (b == wstring::npos)
{
cout << "檢測被除錯!!!" << endl;
}
}
}
system("pause");
return 0;
}
遍歷反制
就是使用API
進行遍歷系統程式,看看有沒有偵錯程式程式,由於程式遍歷網上一大把,我就不在這裡佔篇幅了。
窗體查詢反制
利用FindWindow
的方式來檢測偵錯程式,別覺得不太靠譜,因為現在的偵錯程式都是使用比較主流的,又會有指定的名稱,如果不改變窗體名稱的話,就會被檢測到,做出相應的措施,達到反除錯的目的。由於十分簡單,就不舉例了。
許可權查詢反制
有些偵錯程式會使用SeDebugPrivilege
許可權進行除錯,那麼我們可以使用許可權令牌進行查詢反制:
#include <iostream>
#include <Windows.h>
using namespace std;
int main(int argc, char* argv[])
{
HANDLE token = GetCurrentProcessToken();
DWORD ret;
if (!GetTokenInformation(token, TokenPrivileges, NULL, NULL, &ret))
{
TOKEN_PRIVILEGES* tps = (TOKEN_PRIVILEGES * )malloc(ret);
memset(tps, 0, ret);
LUID luid;
if (LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
{
if (GetTokenInformation(token, TokenPrivileges, tps, ret, &ret))
{
for (int i = 0; i < tps->PrivilegeCount; i++)
{
auto buffer = tps->Privileges[i].Luid;
if (buffer.HighPart == luid.HighPart && buffer.LowPart == luid.LowPart)
{
cout << "檢測到反除錯!!!" << endl;
}
}
}
}
free(tps);
}
system("pause");
return 0;
}
但是這個貌似不太起作用,因為該許可權是針對除錯系統安全程式和服務程式,如果偵錯程式沒有提升該許可權,就不會被這種措施檢測到。
其他反制
以上各種花式檢測方式,它的根源本質就是檢測找出處於除錯狀態下與正常執行模式下的不同之處,如果通過某些方式能夠獲取到這個不同之處的話,我們就可以利用這個資訊來進行反除錯。當然反制措施不僅僅這些,上面的一切的一切僅供初學者開拓眼界,形成自己的思路。
下一篇
羽夏逆向指引—— Hook