这里有些人还没忽悠过

上传的附件 anti.rar

昨天刚刚看到了shoooo发的anti-od1.1。出于个人喜好便下来学习学习。在此感谢shoooo又给我一个学习的机会。

现在就把我的学习成果与各位分享。前辈们也请多多指教,指出不足谢谢。

用OD载入anti文件后果然OD挂掉了。
来看看出错的地址

004A4EE5    3A5B 17                     cmp bl,byte ptr ds:[ebx+17]

大家会发现EBX的值是 0D0A0D0A.再看看EBP指向的栈空间是一堆■■■■的数据。
看来EBP是给被改掉了。

用二进制工具打开anti文件找到导入表的文件偏移。会发现ntdll后面跟了一串很长的0D0A0D0A最后以00结束。
ZwSetInformationProcess这个函数名也是如此。似乎他两就是罪魁祸首了。呵呵。

接下来就看看哪里把栈给溢出了。

从新加载OD。

0045F7E6    E8 89CBFFFF                 call <Ollydbg.sub_45C374>下断点。

OD加载文件后会断在0045F7E6处。F7跟进。
由于函数代码比较长就不全部贴出来了。

0045D120    6A 03                             push 3
0045D122    68 04020000                 push 204         ;指定读取的长度 
0045D127    8B4D CC                         mov ecx,dword ptr ss:[ebp-34]
0045D12A    038D 3CF9FFFF               add ecx,dword ptr ss:[ebp-6C4]
0045D130    51                                   push ecx
0045D131    8D85 78FDFFFF               lea eax,dword ptr ss:[ebp-288]
0045D137    50                                   push eax
0045D138    E8 CF410000                  call <Ollydbg._Readmemory>                          
0045D13D    83C4 10                          add esp,10
0045D140    85C0                               test eax,eax
0045D142    75 4E                              jnz short <Ollydbg.loc_45D192>  
这里读取Dll的名称。


0045D192 >  8D85 78FAFFFF               lea eax,dword ptr ss:[ebp-588]
0045D198    50                                    push eax
0045D199    8D95 78FCFFFF                lea edx,dword ptr ss:[ebp-388]
0045D19F    52                                    push edx
0045D1A0    6A 00                               push 0
0045D1A2    6A 00                               push 0
0045D1A4    8D8D 78FDFFFF               lea ecx,dword ptr ss:[ebp-288]
0045D1AA    51                                    push ecx
0045D1AB    E8 0C800400                  call <Ollydbg.j___fnsplit>
把后缀.dll 去掉。



0045D4F2 >  8B55 B8                     mov edx,dword ptr ss:[ebp-48]
0045D4F5    2955 C4                     sub dword ptr ss:[ebp-3C],edx
0045D4F8    8B4D C4                     mov ecx,dword ptr ss:[ebp-3C]
0045D4FB    3B4D A4                     cmp ecx,dword ptr ss:[ebp-5C]
0045D4FE    73 42                         jnb short <Ollydbg.loc_45D542>
0045D500    8B45 88                     mov eax,dword ptr ss:[ebp-78]
0045D503    0345 C4                     add eax,dword ptr ss:[ebp-3C]
0045D506    83C0 02                     add eax,2                                         ;得到函数名称
0045D509    50                              push eax
0045D50A    8D95 78FCFFFF          lea edx,dword ptr ss:[ebp-388]
0045D510    52                              push edx
0045D511    E8 BE610400             call <Ollydbg._strlen>
0045D516    59                              pop ecx
0045D517    8D95 78FDFFFF          lea edx,dword ptr ss:[ebp-288]
0045D51D    B9 00010000             mov ecx,100
0045D522    2BC8                          sub ecx,eax
0045D524    8D85 78FCFFFF          lea eax,dword ptr ss:[ebp-388]
0045D52A    83E9 02                     sub ecx,2
0045D52D    51                              push ecx
0045D52E    50                              push eax
0045D52F    68 92C04B00             push Ollydbg.004BC092
0045D534    52                              push edx
0045D535    E8 F2960400             call <Ollydbg._sprintf>                       ;这里格式化
0045D53A    83C4 14                     add esp,14
0045D53D    E9 AA000000             jmp <Ollydbg.loc_45D5EC>

上面的代码就是格式化字符串,把处理过的DLL名和函数名格式化成USER32.MessageBoxA的形式。
当格式化ntdll 和 ZwSetInformationProcess。注意看栈的变化。我是盯着EBP
的地址。果然当格式完后。EBP被覆盖了。 这里溢出的原因是DLL名做了处理,而函数名没有做处理或者说处理不好。导致了在调用sprintf函数格式化的时候把栈的溢出了。

问题找到了,花了不少时间。心里挺高兴的,就试着想把这个漏洞补补看。


心想既然栈溢出了,无非是栈空间给那两个超长的字符串给弄的。那把缓存弄得足够大,能装的下应该就可以了吧。
来到函数入口
0045C374 >  55                          push ebp
0045C375    8BEC                       mov ebp,esp
0045C377    81C4 30F9FFFF       add esp,-6D0
把ESP-6D0 改大吧。喜欢算准确点也行。我没算 直接改成 esp - 1500 够了吧。
空间是够大了。还要改缓存的地址。
原来是 [ebp-288]
以下是我修改的几个地方

0045D517    8D95 78FDFFFF               lea edx,dword ptr ss:[ebp-1200] 

0045D131    8D85 00EEFFFF               lea eax,dword ptr ss:[ebp-1200]

0045D1A4    8D8D 00EEFFFF               lea ecx,dword ptr ss:[ebp-1200]

0045D5EC    8D85 00EEFFFF               lea eax,dword ptr ss:[ebp-1200]

修改完后再运行试试。哈哈。可以运行了吧。

这个解决方法还有明显缺陷 如果把函数名改更长,还是可以溢出的。

经过和同学讨论觉比较好的解决方法是,把函数名和DLL名称
的总长度求出来,然后动态分配内存。这样就能比较好解决内存溢出的问题。
而在调试过程中确实发现了比较好的时机获取到DLL名和函数名。
0045D511    E8 BE610400                 call <Ollydbg._strlen>
当运行到这里的时候,栈顶的2个值正好是 DLL名称和函数名称的地址。这里我们就可以改到自己流程求出他们的长度,分配足够
的内存。

到此,整个分析和修改完成。欢迎各位朋友批评指正,再次感谢shoooo给一次了锻炼的机会。也希望能多向前辈们学习,共同进步。

有表述不清楚的地方请多多见谅。分析不足的地方和错误的地方也请各位朋友指正,谢谢。



                    武汉科锐学员 :dachongGG
                      

                      2009.1.09
          

我先说说我对它的认识过程(我是个大菜鸟,高手莫笑话偶啊):
OD载入anti-od1.1,结果还没到入口点OD就被FUCK了,猜想是输入表的问题。
拿loadpe打开,输入表查看,点L看到ntdll后面很多换行(兴奋)。
Name:                0x000040A8  ("ntdll

点H打开40A8处看一下,后面一大堆0D0A,0D0A是回车换行的意思,过一大堆0D0A后有00结束。到这里我就简单的以为是系统认识0D0A,OD不认识造成的错误。然后就放一边了。今天看到有人发个分析的帖子,给出了解决方法结果shoooo说只治标,到这里我想到我以前的想法错了。
看来不动手光靠猜想是不行的。

loadpe再次打开,我把0D0A全改成3333就是ntdl333333333333。。。结果程序一样运行,看来这个输入表项系统填充IAT的时候没用上,明明在输入表里,系统怎么没去填充呢?只好点ntdll这个输入表项仔细看了,点H从OriginalFirstThunk,FirstThunk依次看。最后发现问题在FirstThunk,只要FirstThunk指向的IAT结构为空,这个表项就不填充。我把409C处的值取值随便改成非0,饭岛爱就运行不起来了(除非ntdll,ZwSetInformationProcess改成00结束)。我又试了Kernel32.dllFirstThunk指向的IAT结构后面的值,只要第一个不为0,整个IAT都能正常填充。
到这里问题就明朗了,shoooo利用IAT结构构造了一个没用的输入表项,只要IAT结构为空,这个输入表项的名字和函数可以随便取,反正运行的时候系统不管它。但是我们的OD就不行了,它会读这个乱起的名字,如果恶意构造个名字,后果是很严重的。

OK我们动手补丁下我们的OD让它先判断IAT结构,为空就不管这项读下个表项。

罗嗦了半天,呵呵。复制一份OD,用复制的OD打开目标OD程序,F9,目标程序载入antiod,
004A4EE5    3A5B 17                     cmp bl,byte ptr ds:[ebx+17]处出错
在4A4ED8处下断重新载入程序,再次载入antiod记下异常时的最后一次断下的返回值,45DAED
看到这个函数太长了,我贴下主要代码
0045D0E6   > /6A 03         push    3
0045D0E8   . |6A 14         push    14
0045D0EA   . |8B45 CC       mov     eax, dword ptr [ebp-34]
0045D0ED   . |0345 BC       add     eax, dword ptr [ebp-44]
0045D0F0   . |0345 FC       add     eax, dword ptr [ebp-4]
0045D0F3   . |50            push    eax
0045D0F4   . |8D95 30F9FFFF lea     edx, dword ptr [ebp-6D0]
0045D0FA   . |52            push    edx                                          
0045D0FB   . |E8 0C420000   call    _Readmemory              ////此处为读一项IMAGE_IMPORT_DESCRIPTOR
0045D100   . |83C4 10       add     esp, 10
0045D103   . |85C0          test    eax, eax
0045D105   . |75 0C         jnz     short 0045D113
0045D107   . |C745 F4 02000>mov     dword ptr [ebp-C], 2
0045D10E   . |E9 1C050000   jmp     0045D62F
0045D113   > |83BD 3CF9FFFF>cmp     dword ptr [ebp-6C4], 0
0045D11A   . |0F84 0F050000 je      0045D62F
0045D120   . |6A 03         push    3
0045D122   . |68 04020000   push    204
0045D127   . |8B4D CC       mov     ecx, dword ptr [ebp-34]
0045D12A   . |038D 3CF9FFFF add     ecx, dword ptr [ebp-6C4]
0045D130   . |51            push    ecx                                             ///NameRVA
0045D131   . |8D85 78FDFFFF lea     eax, dword ptr [ebp-288]
0045D137   . |50            push    eax
0045D138   . |E8 CF410000   call    _Readmemory           //读Name项
0045D13D   . |83C4 10       add     esp, 10
0045D140   . |85C0          test    eax, eax
0045D142   . |75 4E         jnz     short 0045D192

我们需要在读完IMAGE_IMPORT_DESCRIPTOR还没有读Name的时候patch判断FirstThunk
就在0045D11A 处补丁。我开始在代码段找了一块0串,结果改了不让保存,郁闷!我就自己加了一个区段VA:1AA000
45D11A处改为
0045D11A   .- E9 E1CE1400   jmp     005AA000
0045D11F      90                     nop
5AA000处代码写:
005AA000  - 0F84 2936EBFF   je      0045D62F
005AA006    60                  pushad
005AA007    6A 00            push    0
005AA009    6A 04            push    4
005AA00B    8B4D CC         mov     ecx, dword ptr [ebp-34]
005AA00E    038D 40F9FFFF   add     ecx, dword ptr [ebp-6C0] 
005AA014    68 50A05A00     push    005AA050
005AA019    51              push    ecx                             /////FirstThunk的值409C
005AA01A    A1 685A4D00     mov     eax, dword ptr [4D5A68]
005AA01F    50              push    eax
005AA020    E8 4951F0FF     call    <jmp.&KERNEL32.ReadProcessMemory>
005AA025    A1 50A05A00     mov     eax, dword ptr [5AA050]
005AA02A    85C0            test    eax, eax
005AA02C    61              popad
005AA02D  - 0F85 ED30EBFF   jnz     0045D120        ///不为0继续执行
005AA033  - E9 DC35EBFF     jmp     0045D614          ///为0已读大小加上,再读下一条IMAGE_IMPORT_DESCRIPTOR
005AA038    90              nop

然后保存。用这个修改的OllyICE.exe去打开antiod试试没问题,加载别的程序也没出错。


加点OD对这个anti溢出的说明:

有个朋友发帖分析了,我只稍微提下

问题在这个格式化函数处,它把目标格式化成USER32.MessageBoxA的形式。用格式化成USER32.MessageBoxA作例子说明。
0045D51D   .  B9 00010000   mov     ecx, 100                            //////作者的期望是格式化的结果在256个字节以内。
0045D522   .  2BC8          sub     ecx, eax                            ///先减去USER32的长度,6个大小
0045D524   .  8D85 78FCFFFF lea     eax, dword ptr [ebp-388]
0045D52A   .  83E9 02       sub     ecx, 2                              //再减去'.'+结束00 2个大小
0045D52D   .  51            push    ecx                                 ///ecx就是剩余空间大小
0045D52E   .  50            push    eax
0045D52F   .  68 92C04B00   push    004BC092                                      ;  ASCII "%s.%.*s"
0045D534   .  52            push    edx                                            /////USER32    这里实际大小只读了255个字节,既DLL名如果大于255个字节,256位写00  
0045D535   .  E8 F2960400   call    004A6C2C
问题就在这里了,如果USER32名字大于等于255,ecx值将为-1,我们看看它的内部处理

004A6C2C  /$  55            push    ebp
004A6C2D  |.  8BEC          mov     ebp, esp
004A6C2F  |.  8B45 08       mov     eax, dword ptr [ebp+8]
004A6C32  |.  8D4D 08       lea     ecx, dword ptr [ebp+8]
004A6C35  |.  C600 00       mov     byte ptr [eax], 0
004A6C38  |.  8D45 10       lea     eax, dword ptr [ebp+10]
004A6C3B  |.  50            push    eax
004A6C3C  |.  6A 00         push    0
004A6C3E  |.  6A 00         push    0
004A6C40  |.  8B55 0C       mov     edx, dword ptr [ebp+C]
004A6C43  |.  52            push    edx
004A6C44  |.  51            push    ecx
004A6C45  |.  68 046C4A00   push    004A6C04
004A6C4A  |.  E8 6D020000   call    004A6EBC
004A6C4F  |.  83C4 18       add     esp, 18
004A6C52  |.  5D            pop     ebp


格式化都在4A6EBC,这个函数太长了,只贴关键点说明下。
       

用一个临时变量,我们用mark标记,用IDA的标记功能比较爽,这里还贴OD的吧。


根据参数;  ASCII "%s.%.*s"给mark赋值
走USER32时mark = -1;
004A6F5B  |.  33D2          |xor     edx, edx
004A6F5D  |.  8955 F0       |mov     dword ptr [ebp-10], edx
004A6F60  |.  83CA FF       |or      edx, FFFFFFFF
004A6F63  |.  C645 F7 00    |mov     byte ptr [ebp-9], 0
004A6F67  |.  8955 F8       |mov     dword ptr [ebp-8], edx                  ///mark

走MessageBoxA时mark值为传入的参数ecx剩余空间大小, 中间有个处理参数为0  null出去的;

关键在这里了:
004A74A8  |> \837D F8 00    |cmp     dword ptr [ebp-8], 0           
004A74AC  |.  7C 05         |jl      short 004A74B3
004A74AE  |.  8B45 F8       |mov     eax, dword ptr [ebp-8]
004A74B1  |.  EB 05         |jmp     short 004A74B8
004A74B3  |>  B8 FFFFFF7F   |mov     eax, 7FFFFFFF
004A74B8  |>  8B55 E8       |mov     edx, dword ptr [ebp-18]
004A74BB  |.  33C9          |xor     ecx, ecx
004A74BD  |.  894D C0       |mov     dword ptr [ebp-40], ecx
004A74C0  |.  EB 05         |jmp     short 004A74C7
004A74C2  |>  48            |/dec     eax
004A74C3  |.  FF45 C0       ||inc     dword ptr [ebp-40]
004A74C6  |.  42            ||inc     edx
004A74C7  |>  85C0          | test    eax, eax
004A74C9  |.  0F84 CF000000 ||je      004A759E
004A74CF  |.  803A 00       ||cmp     byte ptr [edx], 0
004A74D2  |.^ 75 EE         |\jnz     short 004A74C2
004A74D4  |.  E9 C5000000   |jmp     004A759E

///mark < 0 就记下dword ptr [ebp-18]字符串的长度保存在dword ptr [ebp-40](size)中,后面用这个长度格式化,不小于0取mark值和dword ptr [ebp-18]字符串的长度的最小值。
如果DLL名字大于等于255字符,分配的栈空间是按格式化256字符长分配的, 函数名超长的再格式化自然溢出了。如果DLL名字小于255字节,.后的函数名取前面字节填充256字节剩余空间。 这样顶多出现函数名不全的情况,不会溢出。


window DLL文件名好象只能取到240多位(我随便重命名了一个文件,数了下)。 保证程序能运行的情况下用我上面的patch方法不会出现溢出的情况

实际上这样patch以后也只能保证加载的时候不溢出. 如果程序在运行的时候把IMAGE_IMPORT_DESCRIPTOR改的面目全非,程序运行的时候IAT填充完了,不影响程序运行.OD这时附加即使不崩溃也读的内容乱七八糟.  读输入表0045D0FB   . |E8 0C420000   call    _Readmemory              ////此处为读一项IMAGE_IMPORT_DESCRIPTOR的这句所在函数过程需要说明下,它的作用如下
在OD的CPU窗口里call    dword ptr [402198] ////显示成00401770  call    dword ptr [<&MSVCRT.__p__commode>;  针对它的解决方法是NOP掉读原始输入表的过程,但是这样似乎不太好. 我的方法就是用海风的插件,哈哈!!!  海风的插件真是好东西,支持海风!!!!!!


附上我修改的OllyICE.exe

上传的附件 OllyICE.rar [附件下载:http://bbs.pediy.com/showthread.php?t=80224 ]