首页
社区
课程
招聘
[原创]MyGetProcAddress 详细分析 + 还原C代码 + 流程图
发表于: 2010-9-1 16:55 16024

[原创]MyGetProcAddress 详细分析 + 还原C代码 + 流程图

2010-9-1 16:55
16024

【谢谢forgot的提醒,特此更正,这个GetProcAddress是一个病毒的自实现过程,不是sdk中真实的GetProcAddress。】
模拟GetProcAddress很早以前就有了,我发出来是觉得那个不细,毕竟这样的东西是给像我一样的菜鸟学习的,在我学习的过程中总是遇到很多笼统的教学帖或者普及帖,所以详细的分析了一下,也当做自己的练习。

分析的肯定有不妥之处,还请各位多多包含~

GetProcAddress:

00401931  /$  55            push    ebp
00401932  |.  8BEC          mov     ebp, esp
00401934  |.  81EC 0C010000 sub     esp, 10C            ;  分配10C大小的栈空间
0040193A  |.  53            push    ebx
0040193B  |.  56            push    esi
0040193C  |.  57            push    edi
0040193D  |.  8B7D 08       mov     edi, dword ptr [ebp+8]     ;  edi = arg1
00401940  |.  33DB          xor     ebx, ebx                   ;  ebx = 0
00401942  |.  3BFB          cmp     edi, ebx                   ;  判断输入参数是否为0
00401944  |.  0F84 CA000000 je      00401A14                   ;  如果参数为0,则跳转
0040194A  |.  8B47 3C       mov     eax, dword ptr [edi+3C]    ;  获取输入模块的IMAGE_NT_HEADERS结构偏移,并传入eax
0040194D  |.  03C7          add     eax, edi                   ;  用模块基址+IMAGE_NT_HEADERS的偏移 = IMAGE_NT_HEADERS的RVA

0040194F  |.  8B70 78       mov     esi, dword ptr [eax+78]    ;  IMAGE_NT_HEADERS偏移0x78处是IMAGE_DATA_DIRECTORY结构数组首地址,在这里取第一个目录项,也就是输出表的RVA
00401952  |.  8B40 7C       mov     eax, dword ptr [eax+7C]    ;  eax = 输出表的大小
00401955  |.  8945 F8       mov     dword ptr [ebp-8], eax     ;  把输出表大小传入局部变量
00401958  |.  8B45 0C       mov     eax, dword ptr [ebp+C]     ;  eax = 第二个参数内容,第二个参数是一个函数名字符串
0040195B  |.  03F7          add     esi, edi                   ;  esi = 输出表在内存空间中的地址,定位输出表
0040195D  |.  3D 00000100   cmp     eax, 10000                 ;  如果参数地址大于0x10000 ,则说明是按名称输出,否则这里要按序号输出
00401962  |.  73 1C         jnb     short 00401980
00401964  |.  8B4E 10       mov     ecx, dword ptr [esi+10]    ;  ecx = IMAGE_EXPORT_DIRECTORY.Base 函数序号基数
00401967  |.  8B56 14       mov     edx, dword ptr [esi+14]    ;  edx = IMAGE_EXPORT_DIRECTORY.NumberOfFunctions 输出表中函数数量
0040196A  |.  03D1          add     edx, ecx                   ;  edx = 最大的函数序号
0040196C  |.  3BC2          cmp     eax, edx                  ;  对比参数中的序号,和本dll库中的最大的函数序号
0040196E  |.  0F83 A0000000 jnb     00401A14          ;  如果大于本dll中的最大的函数序号,则返回错误
00401974  |.  3BC1          cmp     eax, ecx                   ;  对比参数中的序号,和本dll库中的最小的函数序号
00401976  |.  0F82 98000000 jb      00401A14           ;  如果小于本dll中的最小的函数序号,则返回错误
0040197C  |.  2BC1          sub     eax, ecx                   ;  eax = 函数的位序,表明参数序号代表的函数所在的位置
0040197E  |.  EB 47         jmp     short 004019C7
00401980  |>  8B4E 20       mov     ecx, dword ptr [esi+20]    ;  esi + 0x20 是IMAGE_EXPORT_DIRECTORY.AddressOfNames,即ecx = 输出名称表
00401983  |.  895D 08       mov     dword ptr [ebp+8], ebx     ;  参数清零
00401986  |.  03CF          add     ecx, edi                   ;  ecx = 输出名称表在内存中的位置
00401988  |.  395E 18       cmp     dword ptr [esi+18], ebx    ;  判断IMAGE_EXPORT_DIRECTORY.AddressOfFunctions是否为0
0040198B  |.  76 45         jbe     short 004019D2             ;  <= 0 则跳转
0040198D  |.  894D FC       mov     dword ptr [ebp-4], ecx     ;  把输出地址表的VA传给第一个局部变量
00401990  |.  EB 03         jmp     short 00401995
00401992  |>  8B45 0C       /mov     eax, dword ptr [ebp+C]    ;  接下来是一个循环,在动态库输出表中查找指定的函数
00401995  |>  8B55 FC        mov     edx, dword ptr [ebp-4]      ;  edx = 输出名称表va
00401998  |.  8BCF          |mov     ecx, edi                                 ;  ecx = 输入的参数(HMODULE)
0040199A  |.  030A          |add     ecx, dword ptr [edx]      ;  ecx = 输出名称表中的第一个函数名称的地址
0040199C  |.  51            |push    ecx
0040199D  |.  50            |push    eax
0040199E  |.  E8 2FFDFFFF   |call    004016D2                  ;  判断两字符串是否相等
004019A3  |.  59            |pop     ecx
004019A4  |.  84C0          |test    al, al
004019A6  |.  59            |pop     ecx
004019A7  |.  74 11         |je      short 004019BA            ;  判断比较结果
004019A9  |.  FF45 08       |inc     dword ptr [ebp+8]
004019AC  |.  8345 FC 04    |add     dword ptr [ebp-4], 4      ;  跳到下一个函数
004019B0  |.  8B45 08       |mov     eax, dword ptr [ebp+8]    ;  eax = 比较次数
004019B3  |.  3B46 18       |cmp     eax, dword ptr [esi+18]   ;  判断是否到达了输出表尾
004019B6  |.^ 72 DA         \jb      short 00401992            ;  如果没到尾,则继续循环查找函数
004019B8  |.  EB 18         jmp     short 004019D2
004019BA  |>  8B46 24       mov     eax, dword ptr [esi+24]    ;  eax = IMAGE_EXPORT_DIRECTORY.AddressOfNameOrdinals,即导出序号表地址rva
004019BD  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]     ;  ecx = 函数位置
004019C0  |.  8D0448        lea     eax, dword ptr [eax+ecx*2] ;  eax = 刚刚找到的函数在序号表中的序号word RVA
004019C3  |.  0FB70438      movzx   eax, word ptr [eax+edi]    ;  eax = 函数序号
004019C7  |>  8B4E 1C       mov     ecx, dword ptr [esi+1C]    ;  ecx = IMAGE_EXPORT_DIRECTORY.AddressOfFunctions 函数地址表
004019CA  |.  8D0481        lea     eax, dword ptr [ecx+eax*4] ;  eax = 函数的真正地址的RVA保存的地址
004019CD  |.  8B1C38        mov     ebx, dword ptr [eax+edi]   ;  ebx = 函数在内存中的真正地址的RVA
004019D0  |.  03DF          add     ebx, edi                   ;  ebx = 函数在内存中的va
004019D2  |>  8B45 F8       mov     eax, dword ptr [ebp-8]     ;  eax = 输出表大小
004019D5  |.  03C6          add     eax, esi                   ;  eax = 输出表尾
004019D7  |.  3BD8          cmp     ebx, eax                   ;  判断函数地址和输出表尾
004019D9  |.  73 6F         jnb     short 00401A4A             ;  if (ebx > 输出表尾va || ebx < 输出表头va || ebx == 0) 则跳转
004019DB  |.  3BDE          cmp     ebx, esi
004019DD  |.  72 6B         jb      short 00401A4A
004019DF  |.  85DB          test    ebx, ebx
004019E1  |.  74 67         je      short 00401A4A
004019E3  |.  8A03          mov     al, byte ptr [ebx]          ;  函数的第一个byte放入al
004019E5  |.  8BF3          mov     esi, ebx                        ;  esi = 函数地址
004019E7  |>  84C0          /test    al, al
004019E9  |.  74 29         |je      short 00401A14            ;  如果内容为0则函数返回
004019EB  |.  3C 2E         |cmp     al, 2E                          ;  如果是'.'
004019ED  |.  74 06         |je      short 004019F5            ;  如果其中有‘.’ ,则说明此函数来自于其他库
004019EF  |.  8A46 01       |mov     al, byte ptr [esi+1]
004019F2  |.  46            |inc     esi
004019F3  |.^ EB F2         \jmp     short 004019E7
004019F5  |>  80A5 F4FEFFFF>and     byte ptr [ebp-10C], 0      ;  ebp-10c位清零
004019FC  |.  6A 40         push    40
004019FE  |.  59            pop     ecx                            ;  ecx = 0x40
004019FF  |.  33C0          xor     eax, eax                   ;  eax = 0
00401A01  |.  8DBD F5FEFFFF lea     edi, dword ptr [ebp-10B]   ;  edi = buffer(ebp-10B)
00401A07  |.  F3:AB         rep     stos dword ptr es:[edi]            ;  ebp-10b 缓冲区清0  共0x100个0
00401A09  |.  66:AB         stos    word ptr es:[edi]
00401A0B  |.  AA            stos    byte ptr es:[edi]          ;  再补充3位的0
00401A0C  |.  8BFE          mov     edi, esi                   ;  edi = '.'的位置
00401A0E  |.  2BFB          sub     edi, ebx                   ;  用‘.’的位置 - 函数首位置 则得到了动态库的名字长度
00401A10  |.  85FF          test    edi, edi                   ;  判断长度
00401A12  |.  7F 04         jg      short 00401A18             ;  如果大于0,说明正确
00401A14  |>  33C0          xor     eax, eax                   ;  如果<=0 则本函数返回0
00401A16  |.  EB 34         jmp     short 00401A4C             ;  跳到结尾
00401A18  |>  57            push    edi                        ; /dll名长度
00401A19  |.  8D85 F4FEFFFF lea     eax, dword ptr [ebp-10C]   ; |
00401A1F  |.  53            push    ebx                        ; |dll名地址
00401A20  |.  50            push    eax                        ; |dest
00401A21  |.  E8 DC380000   call    <jmp.&MSVCRT.memcpy>       ; \memcpy
00401A26  |.  80A43D F4FEFF>and     byte ptr [ebp+edi-10C], 0  ;  清0
00401A2E  |.  83C4 0C       add     esp, 0C
00401A31  |.  8D85 F4FEFFFF lea     eax, dword ptr [ebp-10C]   ;  eax = dll名
00401A37  |.  50            push    eax                        ; /加载动态库
00401A38  |.  FF15 04604000 call    dword ptr [<&kernel32.GetM>; \GetModuleHandleA
00401A3E  |.  46            inc     esi                        ;  跳过'.' 因为本库中如果有其他库的函数,保存形式为“Advapi32.RegQueryValueEx”,即跳过.后就是函数名的地址
00401A3F  |.  56            push    esi                        ;  压入函数名
00401A40  |.  50            push    eax                        ;  压入模块名
00401A41  |.  E8 EBFEFFFF   call    00401931                   ;  递归调用
00401A46  |.  59            pop     ecx
00401A47  |.  8BD8          mov     ebx, eax                   ;  ebx = 函数地址
00401A49  |.  59            pop     ecx
00401A4A  |>  8BC3          mov     eax, ebx                  ;  eax = 函数地址,用于返回
00401A4C  |>  5F            pop     edi
00401A4D  |.  5E            pop     esi
00401A4E  |.  5B            pop     ebx
00401A4F  |.  C9            leave
00401A50  \.  C3            retn

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 7
支持
分享
最新回复 (32)
雪    币: 7135
活跃值: (4075)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
赞一个...
2010-9-1 17:22
0
雪    币: 434
活跃值: (72)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
牛人!不知WIN7下如何。。。。。。。。。。。。。。。。
2010-9-1 20:44
0
雪    币: 168
活跃值: (152)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
4
学下PE结构后就可以自己写一个了……
2010-9-1 23:03
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
5
我发这个是希望新手把这个当做一个逆向和PE结构的小练习。并不是想具体提供给别人一个函数。所以牛牛们直接飘过啦。嘿嘿。
2010-9-2 04:52
0
雪    币: 284
活跃值: (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
mark学习
2010-9-2 07:55
0
雪    币: 73
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
不能看懂,感觉很强大!
2010-9-2 10:31
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
8
对着PE结构表一点一点看。
2010-9-2 10:40
0
雪    币: 397
活跃值: (397)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
9
学习了,
问一下你用的是啥工具画的流程图 ?
2010-9-2 11:02
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
10
用的EDGE Diagrammer,不支持中文~不知道是我自己没弄明白还是为什么,其实我最开始图弄好的时候都是中文的,但这个软件无法正确导出中文,全是乱码,没办法才都写成英文~~~
2010-9-2 11:52
0
雪    币: 697
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
这个很好,很劲哈
2010-9-2 11:59
0
雪    币: 293
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
很详细!!!!!!!
2010-9-5 23:10
0
雪    币: 141
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
Mark and study.
2010-9-5 23:33
0
雪    币: 30
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
茅塞顿开~~~  
2010-9-5 23:38
0
雪    币: 6075
活跃值: (2236)
能力值: (RANK:1060 )
在线值:
发帖
回帖
粉丝
15
标题改成MyGetProcAddress了,容易导致误解,因为GetProcAddress不是这么实现的。
2010-9-6 01:01
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
16
谢谢版主提醒
2010-9-6 06:44
0
雪    币: 478
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
mark and read it.
2010-9-6 09:08
0
雪    币: 40
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
楼主EDGE用的很熟练啊。  帮你顶 。。。。。
2010-9-6 09:41
0
雪    币: 563
活跃值: (101)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
收下了
2010-9-6 10:36
0
雪    币: 1163
活跃值: (137)
能力值: ( LV12,RANK:230 )
在线值:
发帖
回帖
粉丝
20
谢谢,第一次用,我是对照着他自带的模板来画的,这个工具比较傻瓜化,模板非常实用
2010-9-6 10:50
0
雪    币: 218
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
Mark and study.
2010-9-7 15:14
0
雪    币: 274
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
mark mark mark
2010-9-7 15:51
0
雪    币: 2362
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
发现自己写这个函数总是被误报
还是想办法调系统里这个函数好
2010-9-7 16:17
0
雪    币: 266
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
mark一下吧
2010-9-7 16:45
0
雪    币: 350
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
学习。  
就是有点还没看明白,在使用GetProcAddress的时候,什么情况下会出现递归调用呢?
2010-9-10 02:09
0
游客
登录 | 注册 方可回帖
返回
//