首页 电脑 电脑学堂 查看内容

菜鸟学破解之API函数

2006-3-5 02:44 817 0

摘要: 看到 qduwg 老师凭"【翻译】3个脱壳相关的重要函数介绍" 也"骗"到一个精,不觉心痒,也弄几个函数来介绍一下,不过我不是翻译,而是著作。我们就从 qduwg 老师介绍的几个函数入手。希望上面的话...
关键词: nbsp 结构 函数 uint 伊万 80 一个 lpModuleName void 77

看到 qduwg 老师凭"【翻译】3个脱壳相关的重要函数介绍" 也"骗"到一个精,不觉心痒,也弄几个函数来介绍一下,不过我不是翻译,而是著作。我们就从 qduwg 老师介绍的几个函数入手。希望上面的话没有伤到 qduwg 老师的自尊心。我看到你对别人评价的反应,知道你很在意这些。 1. HMODULE GetModuleHandle(  LPCTSTR lpModuleName   // address of module name to return handle                          // for);   GetModuleHandle 实际上分为两个函数    GetModuleHandleA--处理 Ansi 字符串  GetModuleHandleW--处理 Unicode 字符串  其实真正干活的函数是,GetModuleHandleW (指NT以后的系统),GetModuleHandleA 只不过将用户输入的 Ansi 字符串转成 Unicode 字符串然后就直接调用 GetModuleHandleW,所以在 OD 中如果下断拦截这个函数,直接拦截 GetModuleHandleW 就可以了,保险一个都跑不掉。我看一些新手写 Hook 程序时往往不厌其烦的将每个函数都“沟”一下,其实大可不必,随着我的文章深入,你会发现一个基本原则--千条江河归大海,大部分的调用其实最后都是进入最底层的 ntoskrnl.exe、Hal.dll、和其他几个内核程序。而我们平常打交道最多的 Kernel32.dll 和 ntdll.dll 其实只是对底层函数进行了包装(当然对于某些ring3层的特定功能还是自己完成的),相当于一个接口,起一个中转和隔离的作用,将用户的函数调用进行预处理,比如检测用户参数的合法性,对用户参数进行重新排序(底层函数的参数排列顺序有时和用户层不同,这样可以防止你轻易的弄明白底层在干什么),将用户的调用分离拆解为若干个更为细小的调用,因为底层函数分类往往更细,这样比较灵活。使用 GetModuleHandle 函数,在高级语言中可以直接写成:hMod = GetModuleHandle(lpModuleName); 编译器会根据参数的性质自行决定调用那一个函数,但在汇编中却不能这样使用,你必须明确的指明你要使用那一个函数。另外在OD中下断点,你也必须明确的指明对那一个函数下断,例如在OD中,进入CPU窗口,按Ctrl-G,然后输入GetModuleHandle,OD会告诉你 “未识的标识符”,你必须清楚的告诉 OD 你到底要在那一个函数下断,是 A 还是 W。按前面的说法其实你只要下 GetModuleHandleW 就可以了(当然有时候在A下断,比较容易知道程序是从那里调用的,如果程序使用的是A调用)。 俗话说,口说无凭。你怎么知道那两个还是最后变成了一个?看雪老大也在qduwg的文章后说: 引用:-------------------------------------------------------------------------------- 不过这三个函数的定义只需要Google就能找到相关文档,因为太常用,不光是脱壳,编程是经常用的。现在的强壳都不直接调用这些函数了,都自己实现。-------------------------------------------------------------------------------- 其后跟贴更有"牛B王"--nbw 惊人的评论: 引用:-------------------------------------------------------------------------------- 可以参考以下MSDN。写壳都是自己实现。 以前自己能写个GetProcAddress就觉得很不错了,后来看高手写虚拟机,半小时模拟出来LoadLibrary,那个让偶惊啊。。。。--------------------------------------------------------------------------------   我没有他们那样的水平,自己不会写,人都说半瓶醋晃荡,醋瓶满了就不响了,我这个瓶子还没有满,所以你们有幸能看到这些垃圾文章(当然也是为了骗一点"加精",风光风光),等有一天我的瓶子也满了(最好能够做到),也就只会“啊...”了。 既然如此,那就只好看看老盖是怎么做的,这个函数在 Kernel32.dll 中,你可能会问,你怎么知道在Kernel32.dll中的?qduwg 老师在翻译文中可惜漏掉了可能他认为不重要的信息: QuickInfo  Windows NT: Requires version 3.1 or later.  Windows: Requires Windows 95 or later.  Windows CE: Unsupported.  Header: Declared in winbase.h.  Import Library: Use kernel32.lib.    //这里指明了函数的归宿  Unicode: Implemented as Unicode and ANSI versions on Windows NT. See AlsoDynamic-Link Libraries Overview, Dynamic-Link Library Functions, FreeLibrary, GetModuleFileName, GetProcAddress, LoadLibrary,LoadResource 现在我们来看看 GetModuleHandleA 的具体操作: 代码:--------------------------------------------------------------------------------77E80B1A ; HMODULE __stdcall GetModuleHandleA(LPCSTR lpModuleName)77E80B1A           public GetModuleHandleA77E80B1A GetModuleHandleA proc near              ; CODE XREF: sub_77E684C2+2p77E80B1A                                         ; CreateRemoteThread+FBp ...77E80B1A77E80B1A lpModuleName= dword ptr  877E80B1A77E80B1A           push  ebp 77E80B1B           mov   ebp, esp77E80B1D           cmp   [ebp+lpModuleName], 077E80B21           jnz   short loc_77E80B31      ; lpModuleName 是有效的则转移77E80B23           mov   eax, large fs:18h       ; TEB.NT_TIB.Self77E80B29           mov   eax, [eax+30h]          ; TEB.Peb77E80B2C           mov   eax, [eax+8]            ; PEB.ImageBaseAddress77E80B2F           jmp   short loc_77E80B4577E80B3177E80B31 loc_77E80B31:                           ; CODE XREF: GetModuleHandleA+7j77E80B31           push  [ebp+lpModuleName]77E80B34           call  @AnsiStrToUnicodeStr    ; 将Ansi字符串转成Unicode字符串77E80B39           test  eax, eax77E80B3B           jz    short loc_77E80B4577E80B3D           push  dword ptr [eax+4]       ; lpModuleName77E80B40           call  GetModuleHandleW        ; 这里开始同流合污了。77E80B4577E80B45 loc_77E80B45:                           ; CODE XREF: GetModuleHandleA+15j77E80B45                                         ; GetModuleHandleA+21j77E80B45           pop   ebp77E80B46           retn  477E80B46 GetModuleHandleA endp-------------------------------------------------------------------------------- 这些结果是在 IDA 中分析的,在使用IDA分析这些内核模块时请注意一定要将那个符号文件PDB和调试符号文件DBG从老盖的网站上下载回来,和你要分析的文件放在一个目录下,这样,IDA就会自动加载这些符号文件,分析出的结果可读性大为提高,另外值得注意的是 windows 的这些系统模块和符号文件的版本一定要相同,大多数的情况下,符号文件的版本会相对低一点,这样你可以到你的目录下去寻找过去版本的系统模块,windows在update时会将这些旧的系统模块保存在其他目录,具体的版本对于可执行模块来讲,比较简单,就是PE文件头中的TimeDateStamp,使用任何工具查看都行。IMAGE_FILE_HEADER STRUCT  Machine               WORD    ?  NumberOfSections      WORD    ?  TimeDateStamp         DWORD   ?  //就是这里,不明白的话请看 qduwg 的翻译文章  PointerToSymbolTable  DWORD   ?  NumberOfSymbols       DWORD   ?  SizeOfOptionalHeader  WORD    ?  Characteristics       WORD    ?IMAGE_FILE_HEADER ENDS 对于PDB文件比较麻烦,我没有现成的工具来查看这个 TimeDateStamp ,用任何十六进制编辑器打开这个PDB文件在偏移 0x4404 的地方就是这个 TimeDateStamp 了,这两个TimeDateStamp一定要一样,否则宁愿不要使用。 下面对fs段加一些详细的说明,很多人可能不太熟悉这个段。 fs段在用户模式(Ring3)和系统模式(Ring0)分别指向两个最重要的系统结构:Ring3:fs --> TEB (Thread Environment Block)结构表 --> 7FFDE000即“线程环境块”。 Ring0:fs --> KPCR (Kernel Processor Control Region)结构表 --> 7FFDF000即“内核处理器控制域”。 在NT和2K和XP和2k+3,系统这个地址是固定的,随着版本的增大,TEB会有新的内容增加,在将来的windows系统中,老盖为了防止那些坏人(我可不是坏人,否则说不定也有能力做点坏事)太容易利用这个特性搞破坏,有可能将这个地址变成随机数,也就是每次启动后都会在不同的位置。 这些都是老盖没有公布的结构,所以如果你想使用这些结构,要特别小心,老盖为了防止小人搞破坏,每次Update都有可能修改这些结构而使你程序崩溃。让你吃免费蓝玻璃。 因为是所谓 UnDocument 的东西,所以最好的办法就是通过老盖的 WinDBG 或他的兄弟 KD 来获得这些结构,由于上面说的原因,这个结构以你自己看到的为准,至于如何使用 WinDBG 和 KD ,我想最好请那个叫 Themida flea老兄来写(这可不是我创出的名字,是这位Themida老兄自己的图标,Themida可是我非常欣赏的强壳,虽然不太稳定) 在本论坛的精品 “【原创】用WinDbg动态脱Reflector” 中,我说我学不会 WinDBG ,这位 Themida 老兄答到: 引用:-------------------------------------------------------------------------------- 一群笨蛋,笑翻。。。 windbg的GUI只是个DEMO,各位可以编写自己的windbgGUI -------------------------------------------------------------------------------- 由此可知,flea 老兄,高人也。flea 老兄不但可以教你们如何使用 WinDBG 和 KD,甚至可以教你们写一个带有自己特色的 debug,例如输入你自己开发的命令 allstruct,哇! 所有undocument 的结构都展现在你眼前,想想,那是什么境界。 下面这些是在我的机器上显示的结构,遗憾的是看不到TEB的完整结构,可以到4F提供的w2kundoc.inc中看到这个TEB的完整结构,不过我可不能保证是正确的。但对于我们所要讨论的问题,使用WinDBG提供的信息就足够了。使用WinDBG的内核模式或直接使用KD:(双机模式或虚拟机)输入下面的命令: 代码:--------------------------------------------------------------------------------kd> !tebTEB at 7FFDE000    ExceptionList:    12f830                \   0x0    Stack Base:       130000                |   0x4    Stack Limit:      126000                |   0x8    SubSystemTib:     0                     |   0xC     这里其实是 NT_TIB 的一个结构    FiberData:        1e00                  |   0x10    ArbitraryUser:    0                     |   0x14    Self:             7ffde000              /   0x18    EnvironmentPtr:   0                         0x1C    ClientId:         4cc.4c8                   0x20    Real ClientId:    4cc.4c8                   0x24    RpcHandle:        0                         0x28    Tls Storage:      0                         0x2C    PEB Address:      7ffdf000                  0x30  这里指向 PEB 表,即进程环境块    LastErrorValue:   0    LastStatusValue:  8000001a    Count Owned Locks:0    HardErrorsMode:   0-------------------------------------------------------------------------------- 这里是TEB表的部分,但已经够使用的了,如果想在开发程序中使用这些结构,如前所说,可以到4F的inc中去寻找,也可以自己到网上去google一把,但不一定有美满的结果。 代码:--------------------------------------------------------------------------------kd> !kdex2x86.strct peb  //如果 kdex2x86 已经加载,可以直接输入 !strct pebstruct   _PEB (sizeof=488)+000 byte     InheritedAddressSpace+001 byte     ReadImageFileExecOptions+002 byte     BeingDebugged                   //Debug运行标志+003 byte     SpareBool+004 void     *Mutant+008 void     *ImageBaseAddress               //这里就是程序加载的基地址了+00c struct   _PEB_LDR_DATA *Ldr+010 struct   _RTL_USER_PROCESS_PARAMETERS *ProcessParameters+014 void     *SubSystemData+018 void     *ProcessHeap+01c void     *FastPebLock+020 void     *FastPebLockRoutine+024 void     *FastPebUnlockRoutine+028 uint32   EnvironmentUpdateCount+02c void     *KernelCallbackTable+030 uint32   SystemReserved[2]+038 struct   _PEB_FREE_BLOCK *FreeList+03c uint32   TlsExpansionCounter+040 void     *TlsBitmap+044 uint32   TlsBitmapBits[2]+04c void     *ReadOnlySharedMemoryBase+050 void     *ReadOnlySharedMemoryHeap+054 void     **ReadOnlyStaticServerData+058 void     *AnsiCodePageData+05c void     *OemCodePageData+060 void     *UnicodeCaseTableData+064 uint32   NumberOfProcessors+068 uint32   NtGlobalFlag+070 union    _LARGE_INTEGER CriticalSectionTimeout+070    uint32   LowPart+074    int32    HighPart+070    struct   __unnamed3 u +070       uint32   LowPart+074       int32    HighPart+070    int64    QuadPart+078 uint32   HeapSegmentReserve+07c uint32   HeapSegmentCommit+080 uint32   HeapDeCommitTotalFreeThreshold+084 uint32   HeapDeCommitFreeBlockThreshold+088 uint32   NumberOfHeaps+08c uint32   MaximumNumberOfHeaps+090 void     **ProcessHeaps+094 void     *GdiSharedHandleTable+098 void     *ProcessStarterHelper+09c uint32   GdiDCAttributeList+0a0 void     *LoaderLock+0a4 uint32   OSMajorVersion+0a8 uint32   OSMinorVersion+0ac uint16   OSBuildNumber+0ae uint16   OSCSDVersion+0b0 uint32   OSPlatformId+0b4 uint32   ImageSubsystem+0b8 uint32   ImageSubsystemMajorVersion+0bc uint32   ImageSubsystemMinorVersion+0c0 uint32   ImageProcessAffinityMask+0c4 uint32   GdiHandleBuffer[34]+14c function *PostProcessInitRoutine+150 void     *TlsExpansionBitmap+154 uint32   TlsExpansionBitmapBits[32]+1d4 uint32   SessionId+1d8 void     *AppCompatInfo+1dc struct   _UNICODE_STRING CSDVersion+1dc    uint16   Length+1de    uint16   MaximumLength+1e0    uint16   *Buffer-------------------------------------------------------------------------------- 注意这些是WinDBG的扩展命令,由外部模块 kdex2x86.dll 提供,先觉条件是这个模块必须被装载。至于那个Ring0的KPCR结构如果我的兴趣还在,而且那个瓶子还没有满,再详细讨论,KPCR的结构巨大无比也复杂无比。前些日子 qduwg 老师发了题为 “For菜鸟文章:PE文件格式, qduwg翻译”,我没有留心说漏嘴,大概意思是 PE 结构拿过来使用不就完了,用得着去学吗。其实此话来源就是因为底层的这些结构,和底层的这些结构相比, 那真是小巫见大巫了(PE 文件头其实就是一个非常简单的结构),如果你一个 PE 文件头都要话这么大的力气去学,而且到处都可以找到详细的资料,那么底层的这些结构怎么办,其一是这些结构远比 PE 结构更加庞大,更加复杂,其二是根本就没有任何资料和解释,全部要靠你自己去努力理解,而且这些巨大的结构有无数,怎么办,以我看,不如回家种红薯。没有想到这段无心的话引起了两国战争,伊万大叔立刻就对我发动了战争,伊万大叔在俄国,我在加拿大,你想,加拿大全国警察加军队也不过才几万人,怎么能打的过伊万大叔,于是我只好狼狈逃跑,哈哈,由于我的当机立断,结果世界大战没有爆发,为了消除坏影响,伊万大叔卖通看雪老大,将这场争论给完全删了,可惜,哈哈。伊万大叔在随后的跟贴中可不要对我口诛笔伐。这些是玩笑话,当不得真,如果不写一些这样的垃圾,我的创作热情恐怕立刻就消失了,所以请看官们忍忍。 这里插一句题外话,大家在使用OD脱壳时,往往为了隐藏OD,会加载IsDebug V1.4.dll来隐藏OD,其实完全不需要加载这个东西,在OD中的数据窗口, 按 Ctrl-G ,在打开的小窗中输入 7FFDF000 然后在偏移量 02 的地方或者干脆输入 7FFDF002, 将鼠标放在这个地址上,Ctrl-E,将 1 改为零就 IsDebug了,如果已经是 0 就是你已经使用什么 dll 将 Debug 隐藏了。 对这两张表有了一定的认识后,我们再来看上面的那段代码的头部: 代码:--------------------------------------------------------------------------------77E80B1A           push  ebp77E80B1B           mov   ebp, esp77E80B1D           cmp   [ebp+lpModuleName], 077E80B21           jnz   short loc_77E80B31      ; lpModuleName 是有效的则转移77E80B23           mov   eax, large fs:18h       ; TEB.NT_TIB.Self 77E80B29           mov   eax, [eax+30h]          ; TEB.Peb77E80B2C           mov   eax, [eax+8]            ; PEB.ImageBaseAddress77E80B2F           jmp   short loc_77E80B45-------------------------------------------------------------------------------- 其中: 代码:--------------------------------------------------------------------------------77E80B23           mov   eax, large fs:18h       ; TEB.NT_TIB.Self-------------------------------------------------------------------------------- 就是将那个 7FFDE000 的地址送到 eax 中去,现在我们知道那是 TEB 表。 代码:--------------------------------------------------------------------------------77E80B29           mov   eax, [eax+30h]          ; TEB.Peb-------------------------------------------------------------------------------- 根据上面的 TEB 表,我们知道 7FFDE000 偏移 30h 的地方是一个指针,指向 PEB 表。 代码:--------------------------------------------------------------------------------77E80B2C           mov   eax, [eax+8]            ; PEB.ImageBaseAddress-------------------------------------------------------------------------------- 根据上面的 PEB 表,我们得到最后结果,原来这几句代码的目的是获得程序启动后被装载的基地址。就这么简单。 代码:--------------------------------------------------------------------------------77E80B1D           cmp   [ebp+lpModuleName], 0-------------------------------------------------------------------------------- 这句是检测用户给出的 lpModuleName 是否为零,如果为零,用户发出的请求是获取自己当前进程的 hModule,这就是为什么要获取程序启动后被装载的基地址的原因了。在下面的东西不用我解释了把,你可以亲眼看到他将那个Ansi字符串转换成Unicode后就调用 GetModuleHandleW 最后跑到一起去了。至于 GetModuleHandleW 干了一些什么事,且听下回分解,哈哈这样我可以多骗几个“加精”,qduwg 老师用三个 API 换一个“加精”,我准备用一个 API 调用换 3 到 5 个“加精”,哈哈,那当然还要看雪老大肯给我才行。 后话:过些日子我就要回中国了,回到我可爱的祖国,如果很忙,可能就不能连续的写了,这点请大家谅解,多给我一点鼓励,使我能多写几篇,否则热情一旦消失或瓶子不幸装满了,就没有这些垃圾文章了,哈哈。 另外伊万大叔花重金买了一条从专门出那种苗条型会飞的蟑螂的国家--德国(现在中国许多城市的蟑螂多为这个德国种,本土的蟑螂太笨,不会飞,所以蔓延比较慢,危害也比较小,有灭亡的危险)造的游艇,还说会开到珠江口,在船上请我喝那个“可爱的水”--vodka,根据伊万大叔提供的资料,vodka 没有任何的味道,最大的好处就是你喝过以后,不影响你和你那一半做某些亲热的动作,因为嘴中没有异味。不过我还是认为我们中国的酒好,你看那茅台、五粮液,酒瓶一开满屋香,多来劲,到时说不定又要爆发新的战争,为酒而战,不过这次是中俄之战了,哈哈,请看官们不用担心,我依然会毫不犹豫的跳海逃走,幸亏我的游泳技术不错,尤其擅长长距离游泳。
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部