此系列是本人一个字一个字码出来的,包括示例和实验截图。可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
前言你如果是从中间插过来看的,请仔细阅读 羽夏逆向指引——序 ,方便学习本教程。
本篇讲解常见的在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
结构体的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
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可