首页
社区
课程
招聘
[原创]我也来个shoooo的anti-od1.1的分析
发表于: 2009-1-10 03:46 9025

[原创]我也来个shoooo的anti-od1.1的分析

2009-1-10 03:46
9025

我先说说我对它的认识过程(我是个大菜鸟,高手莫笑话偶啊):
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


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 7
支持
分享
最新回复 (13)
雪    币: 2110
活跃值: (21)
能力值: (RANK:260 )
在线值:
发帖
回帖
粉丝
2
早上上来看一眼,一眼就瞅见此贴。
嗯,看了你的分析,有些明白了,原来只道是输入表的问题,没具体看看是怎么回事。
2009-1-10 08:30
0
雪    币: 2368
活跃值: (81)
能力值: (RANK:300 )
在线值:
发帖
回帖
粉丝
3
一眼就看见书呆子的脚印了....特来膜拜...
2009-1-10 08:45
0
雪    币: 398
活跃值: (343)
能力值: (RANK:650 )
在线值:
发帖
回帖
粉丝
4
这个达阵了,比那个精华
2009-1-10 08:58
0
雪    币: 442
活跃值: (43)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
我太菜了

不懂
2009-1-10 09:03
0
雪    币: 4541
活跃值: (2490)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
6
如果要是自己做一个很长的,能够运行的文件名的函数,不也是会溢出?
2009-1-10 17:19
0
雪    币: 271
活跃值: (21)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
OD在那地方溢出主要是dll的文件名的问题,函数名无所谓,OD读函数名读到一定长度就不读了,顶多读个错名字不影响,而dll名它好象是读完的,没仔细看。我记得windows文件名有长度限制吧?好象是255个字节。
2009-1-10 19:22
0
雪    币: 271
活跃值: (21)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
8
改下shoooo的antiod,把ntdll后面写00 ZwSetInformationProcess后面再加很多0D0A,用正常OD在0045D535下断能看到,跟下能看到EBP变化, 里面函数也够长的,我没仔细跟.
2009-1-10 19:41
0
雪    币: 4541
活跃值: (2490)
能力值: ( LV4,RANK:55 )
在线值:
发帖
回帖
粉丝
9
DLL名OD做了处理,最长只读255个字节,函数名没有做处理,所以如果函数名够长的话,也会溢出...
2009-1-10 21:06
0
雪    币: 1349
活跃值: (485)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
膜拜学习~~~
2009-1-10 21:14
0
雪    币: 1632
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
那个精华是在“破坏”之后去应对,这个精华是从根上阻止了“破坏”。
即四个圈说的 治标和治本。

膜拜了,反正我只管用strongod就行了,这些是大牛们的事。
2009-1-11 02:14
0
雪    币: 208
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
顶一下,呵呵,分析的很好啊
2009-1-11 10:55
0
雪    币: 423
活跃值: (11)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
13
学习、学习、再学习。

大牛不鸣则已,一鸣惊人。。。
2009-1-11 18:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
收藏,辛苦了
2009-1-12 23:09
0
游客
登录 | 注册 方可回帖
返回
//