乱解 API 函数 -- API 绝密档案系列之二 通向高手之路
上一篇介绍了两个结构,今后你会发现,在系统程序中,对这两个结构的引用无所不在,理解这两个结构对正确理解系统程序有莫大的帮助。
这一篇加了副标题【通向高手之路】你可能要问,我是什么“手”,抱歉,我最多只能称“中手”,那么“中手”是什么意思? 是高手之下,低手之上的那只手? 错,“中手”--乃中国之手--乃国手也,哈哈。
那么我们为什么要研究这些底层的东西呢?这个问题提的好。一般来说有两个目的,第一是深刻的理解这些函数是如何工作的,在今后的开发中你也许可以借鉴这些底层的某些用法,来优化你的程序。其二就是弄一些自己的函数来取代这些函数从而达到某些特殊的目的。
对于如何用自己的代码来取代这些函数也有许多方法,最简单的就是将这些代码直接拷贝到你的程序中去,但这可能不是很好的办法,因为这些代码中,有许多函数是内部函数,不能直接调用,因而你不得不将这些所有的过程都搬到你的程序中。另一种方法就是在真正理解这些函数的基础上彻底改写。系统函数一般来讲都比较全面,各种情况都要考虑,所以比较繁琐,而你可能只是在某些特定的环境下使用这些函数,这就有可能简化这些函数,将你认为不需要的地方去掉,这样代码精炼,而且隐蔽性更好,如果更上一层楼,可以使用其他的方法达到相同的目的,这样就是最高境界了。
我在这里以 GetModuleHandle 函数为例,介绍了如何分析理解这些函数,Windows 从公开的到未公开的函数多如牛毛,不可能每一个的介绍,我没时间写,你也没有时间看,所以这里只是以这个函数为例,让你了解一下这些分析的过程,希望你能掌握这一思路和方法,自己去分析底层函数,这样你就有可能成为真正的高手。
首先我们来看看GetModuleHandleW的代码:(这里给出的是 TimeDateStamp=0x42A0669B 版)
77E80C27 ; Exported entry 320. GetModuleHandleW
77E80C27 ; ======== S U B R O U T I N E ====================
77E80C27 ; HMODULE __stdcall GetModuleHandleW(LPCWSTR lpModuleName)
77E80C27 public _GetModuleHandleW@4
77E80C27 _GetModuleHandleW@4 proc near
77E80C27 UnicodeModuleName= dword ptr -10h
77E80C27 ModuleName= byte ptr -8
77E80C27 MemoryPointer= dword ptr -4
77E80C27 lpModuleName= dword ptr 8
77E80C27 push ebp
77E80C28 mov ebp, esp
77E80C2A sub esp, 10h
77E80C2D cmp [ebp+lpModuleName], 0
77E80C31 push esi
77E80C32 jnz short IfNotSelfModuleHandle
77E80C34 mov eax, large fs:18h ; TEB.NT_TIB.Self
77E80C3A mov eax, [eax+TEB.Peb]
77E80C3D mov eax, [eax+PEB.ImageBaseAddress]
77E80C40 jmp short GetModHandleExit
77E80C42 IfNotSelfModuleHandle: ; SourceString
77E80C42 push [ebp+lpModuleName]
77E80C45 lea eax, [ebp+UnicodeModuleName]
77E80C48 push eax ; DestinationString
77E80C49 call ds:__imp__RtlInitUnicodeString@8 ; 将 CWSTR 转成 Unicode
77E80C4F cmp _gDoDllRedirection, 0 ; Dll 重定向标志
77E80C56 jz short DontDllRedir
;_gDoDllRedirection = TRUE
77E80C58 lea eax, [ebp+ModuleName]
77E80C5B push eax ; returnName
77E80C5C lea eax, [ebp+UnicodeModuleName]
77E80C5F push eax ; lpModuleName
77E80C60 call _ComputeRedirectedDllName@8 ; 所谓重定向就是到用户程序的目录下加载同名的dll,
77E80C60 ; _ComputeRedirectedDllName函数将用户输入的ModuleName
77E80C60 ; 前面加上用户的目录(用户程序启动的目录)。
77E80C65 test eax, eax
77E80C67 jge short IfGetRedirDllName
77E80C69 push eax ; Status
77E80C6A call _BaseSetLastNTError@4 ; BaseSetLastNTError(x)
77E80C6F xor eax, eax
77E80C71 jmp short GetModHandleExit
77E80C73 IfGetRedirDllName:
77E80C73 lea eax, [ebp+ModuleName]
77E80C76 push eax ; lpModuleName
77E80C77 call _GetModuleHandleForUnicodeString@4 ; GetModuleHandleForUnicodeString(x)
77E80C7C mov esi, eax
77E80C7E mov eax, large fs:18h ; TEB.NT_TIB.Self
77E80C84 push [ebp+MemoryPointer] ; MemoryPointer
77E80C87 mov eax, [eax+TEB.Peb]
77E80C8A push 0 ; Flags
77E80C8C push [eax+PEB.ProcessHeap] ; HeapHandle
77E80C8F call ds:__imp__RtlFreeHeap@12 ; __declspec(dllimport) RtlFreeHeap(x,x,x)
77E80C95 test esi, esi
77E80C97 jz short DontDllRedir
77E80C99 mov eax, esi
77E80C9B jmp short GetModHandleExit
;_gDoDllRedirection = FALSE
77E80C9D DontDllRedir:
77E80C9D lea eax, [ebp+UnicodeModuleName]
77E80CA0 push eax ; lpModuleName
77E80CA1 call _GetModuleHandleForUnicodeString@4 ; GetModuleHandleForUnicodeString(x)
77E80CA6 GetModHandleExit:
77E80CA6 pop esi
77E80CA7 leave
77E80CA8 retn 4
77E80CA8 _GetModuleHandleW@4 endp
77E80C34 mov eax, large fs:18h ; TEB.NT_TIB.Self
77E80C3A mov eax, [eax+TEB.Peb]
77E80C3D mov eax, [eax+PEB.ImageBaseAddress]
77E80C4F cmp _gDoDllRedirection, 0 ; Dll 重定向标志
77E80C56 jz short DontDllRedir
77E67A40 ; NTSTATUS __stdcall BaseDllInitialize(HINSTANCE hInstance,DWORD dwReason,LPVOID lpReserved)
77E67A40 _BaseDllInitialize@12 proc near
77E67A40 pObjectDirectory= dword ptr -210h
77E67A40 PEB = dword ptr -10h
77E67A40 pConnectionInfo= dword ptr -0Ch
77E67A40 @ConnectionInfoSize= dword ptr -8
77E67A40 @ServerToServerCall= dword ptr -1
77E67A40 hInstance= dword ptr 8
77E67A40 dwReason= dword ptr 0Ch
77E67A40 lpReserved= dword ptr 10h
77E67A40
77E67A40 push ebp
77E67A41 mov ebp, esp
77E67A43 sub esp, 210h
... ...
... ...
77E67C66 loc_77E67C66:
77E67C66 push 104h ; nSize
77E67C6B push offset _BaseDefaultPathBuffer ; lpFilename
77E67C70 push esi ; hModule esi=Null 获取当前用户带路径的程序名
77E67C71 call _GetModuleFileNameW@12 ; GetModuleFileNameW(x,x,x)
77E67C76 movzx eax, ax ; 返回文件名的长度
77E67C79 mov esi, offset a_local ; ".Local"
77E67C7E lea edi, _BaseDefaultPathBuffer[eax*2] ;将指针移到文件名字符串的尾部
77E67C85 movsd ;添加 ".Local"
77E67C86 movsd
77E67C87 movsd
77E67C88 movsw
77E67C8A mov esi, offset _BaseDefaultPathBuffer
77E67C8F push esi ; FileName
77E67C90 call ds:__imp__RtlDoesFileExists_U@4 ; __declspec(dllimport) RtlDoesFileExists_U(x)
77E67C96 mov edi, src
77E67C9C mov [b]_gDoDllRedirection[/b], al
... ...
... ...
C:\MyDir\MyProc.exe.Local (Nt格式的文件名,注意,是 WSTR 字符串)
77F895D0 ; Exported entry 430. RtlDoesFileExists_U
77F895D0 ; ======== S U B R O U T I N E ====================
77F895D0 ; BOOLEAN __stdcall RtlDoesFileExists_U(PWSTR FileName)
77F895D0 public _RtlDoesFileExists_U@4
77F895D0 _RtlDoesFileExists_U@4 proc near
77F895D0 FileName= dword ptr 4
77F895D0 push TRUE ; AllowShare
77F895D2 push [esp+4+FileName] ; FileName
77F895D6 call _RtlDoesFileExists_UEx
77F895DB retn 4
77F895DB _RtlDoesFileExists_U@4 endp
[注意]APP应用上架合规检测服务,协助应用顺利上架!