xiaohang99 发表于 2017-4-11 09:43

Safengine Shielden 2.3.8.0 脱壳 之 为了能下断

本帖最后由 xiaohang99 于 2017-4-12 22:36 编辑

最近又在论坛上看到不少人求Safengine Shielden (以下称SE)的脱壳方法,其实之前论坛里的几位大牛都已经放出过不少牛文,虽然这些牛文是对应老版本的SE的,文中附带的代码可能已经失效,但是里面的方法确实在新版本中依然有效的,可能大神们对自己思路的阐述过于跳跃了,所以难免让新人们有些跟不上。在这里,我把我对大牛们的思路理解放上来,以供参考吧。SE 2.3.8.0目前已知的最大反调试手段就是anti断点,主要是anti了硬件断点和int3软断点,加壳后无法对被加壳的程序下断,客观上提高了脱壳的难度,所以第一步是要想办法把anti断点去掉。
我的思路
int3软断点的anti必然是基于内存效验的方法(int3软断点需要修改内存代码),对壳代码进行hash效验保护,如若发现内存有被修改,则结束进程。如果要patch掉内存hash效验,必须知道hash代码的位置,由于SE的垃圾混淆代码很多,更本不可能通过搜索的方式获取,最好的办法就是用硬件断点下一个读取断,所以问题就是要先修复硬件断点。
对于硬件断点,SE采用的是先通过SetThreadContext设置四个DRx寄存器为1byte读取特定地址的断点,然后通过在虚拟机中构造读取特定地址以产生异常,再用SEH handler处理异常,并检测DRx寄存器的值。如果该产生异常的时候没有产生异常,又或者DRx的值不为先前SetThreadContext设定的值,那么就退出进程。
         对于内存断点反anti,暂时没有思路,还需要继续研究。
处理过程
先获取SE对DRx设置的值。
1、先在KiUserExceptionDispatcher下断
2、断下两次后记录
= 0012F7FC   00E8A7CAunpackme.00E8A7CA//异常产生的位置,就是后面的vm:ds位置

3、CPU窗口中显示调试寄存器
         DR0 =00E57016
         DR1 =00E57000
         DR2 =00E57004
         DR3 = 00E57008
         DR7 = 33330555
PatchShadow_CreateThread
由于SE会产生数个线程来进行反调试检测,内存完整性检测等,为了减少干扰项,把SE创建的检测线程先patch掉,不让他启动,可能会对后面的程序运行有副作用,但是便于我一开始的脱壳调试。
         PS: SE会把NTDLL.dll 和krenel32.dll中的一些代码Shadow到一段自己分配的空间中,所以需要利用内存搜索的方法,搜索特定代码找到这些被Shadow的API。

[*]先在KiUserExceptionDispatcher下断
[*]第一次断下后,搜索特征代码

#8BFF558BECFF751CFF7518FF7514FF7510FF750CFF75086AFFE8????????5DC21800#
搜索到的代码:
                         00D1FB55   8BFF         mov   edi, edi      ;改成 ret 18
00D1FB57   55         pushebp
00D1FB58   8BEC         mov   ebp, esp
00D1FB5A   FF75 1C      pushdword ptr
00D1FB5D   FF75 18      pushdword ptr
00D1FB60   FF75 14      pushdword ptr
00D1FB63   FF75 10      pushdword ptr
00D1FB66   FF75 0C      pushdword ptr
00D1FB69   FF75 08      pushdword ptr
00D1FB6C   6A FF      push-1
00D1FB6E   E8 D9FDFFFFcall00D1F94C
00D1FB73   5D         pop   ebp
00D1FB74   C2 1800      ret   18


[*]修改函数开头位置为ret1800D1FB55   8BFF      mov   edi, edi      ;改成 ret 18


Patch Shadow_GetThreadContext
         SE会调用Shadow_GetThreadContext来检测是否存在调试软件下的硬件断点,如果有就会退出进程。

[*]在刚才搜索到Shadow_CreateThread的内存段内搜索特征代码:

#8BFF558BECFF750CFF7508FF15????????85C00F8C??????0033C0405DC208009090909090#
搜索到的代码:
01888ABF    8BFF            mov   edi, edi
01888AC1    55            push    ebp
01888AC2    8BEC            mov   ebp, esp
01888AC4    FF75 0C         push    dword ptr
01888AC7    FF75 08         push    dword ptr
01888ACA    FF15 9E088501   call    dword ptr //获取这里地址
01888AD0    85C0            test    eax, eax
01888AD2    0F8C 89B20000   jl      01893D61
01888AD8    33C0            xor   eax, eax
01888ADA    40            inc   eax
01888ADB    5D            pop   ebp
01888ADC    C2 0800         retn    8



[*]如果直接修改以上代码会被SE检测到,我的方法是inline hook 函数调用KiFastSystemCall之前的地址(壳另外加载了一块内存)      


01888ACA    FF15 9E088501   call    dword ptr //获取这里地址
//获取到的地址标准的ntdll.ZwGetContextThread调用
0169C597    B8 55000000   mov   eax, 55 //服务号
0169C59C    BA 00005901   mov   edx, 00390000 //这里改成我自己分配的空间
0169C5A1    FFE2            jmp   edx//跳到我自己分配的空间执行
0169C5A3    C2 0800         retn    8



//我构造的代码

00390000    B8 55000000   mov   eax, 55
00390005    BA 0003FE7F   mov   edx, 7FFE0300
0039000A    FF12            call    dword ptr
0039000C    50            push    eax
0039000D    8B4424 0C       mov   eax, dword ptr
00390011    8038 10         cmp   byte ptr , 10
00390014    75 16         jnz   short 0039002C
00390016    33D2            xor   edx, edx
00390018    8950 04         mov   dword ptr , edx
0039001B    8950 08         mov   dword ptr , edx
0039001E    8950 0C         mov   dword ptr , edx
00390021    8950 10         mov   dword ptr , edx
00390024    52            push    edx
00390025    6A 06         push    6
00390027    E8 299C477C   call    kernel32.TlsSetValue
0039002C    58            pop   eax
0039002D    C2 0800         retn    8


  壳总是会判断这 TlsValue 是否等于 Dr0+Dr1+Dr2+Dr3 之 Total 值
  我们在壳欲取得 Drx 的值时,将之清为 0,并设 TlsValue 为 0

  至于 SetTlsValue 的 Index 应为多少才对, 很多方法可以得知.
例如断 Shadow 的 SetTlsValue ,XP SP2 用的 Index 是 4 , XP SP3 则是 6

Hook ExecuteHandler2
         这里是重头戏,反anti 硬件断点必须从这里开始,由于壳会检测异常信息中的DRx寄存器,所以我们必须在SE 的SEH handler被调用前,给_CONTEXT结构中的DRx赋予之前获得的特定值。
_CONTEXT结构如下:
typedef struct _CONTEXT
{
    DWORD ContextFlags;
    DWORD   Dr0;
    DWORD   Dr1;
    DWORD   Dr2;
    DWORD   Dr3;
    DWORD   Dr6;
    DWORD   Dr7;
    FLOATING_SAVE_AREA FloatSave;
    DWORD   SegGs;
    DWORD   SegFs;
    DWORD   SegEs;
    DWORD   SegDs;
    DWORD   Edi;
    DWORD   Esi;
    DWORD   Ebx;
    DWORD   Edx;
    DWORD   Ecx;
    DWORD   Eax;
    DWORD   Ebp;
    DWORD   Eip;
    DWORD   SegCs;
    DWORD   EFlags;
    DWORD   Esp;
    DWORD   SegSs;
}CONTEXT;

typedef struct _FLOATING_SAVE_AREA
{
    ULONG ControlWord;
    ULONG StatusWord;
    ULONG TagWord;
    ULONG ErrorOffset;
    ULONG ErrorSelector;
    ULONG DataOffset;
    ULONG DataSelector;
    UCHAR RegisterArea;
    ULONG Cr0NpxState;
} FLOATING_SAVE_AREA, *PFLOATING_SAVE_AREA;


[*]在NTDLL.dll的CODE段中搜索特征码

#558BECFF750C5264FF350000000064892500000000FF7514FF7510FF750CFF75#
搜索到的代码:
.text:7C923282               push    ebp
.text:7C923283               mov   ebp, esp
.text:7C923285               push   
.text:7C923288               push    edx             ; 如果定义的异常处理handle中出错,这个handle是最终处理者
.text:7C923289               push    large dword ptr fs:0
.text:7C923290               mov   large fs:0, esp
.text:7C923297               push   
.text:7C92329A               push   
.text:7C92329D               push   
.text:7C9232A0               push   
.text:7C9232A3               mov   ecx,
.text:7C9232A6               call    ecx             ; _except_handler(
.text:7C9232A6                                       ;   struct _EXCEPTION_RECORD *ExceptionRecord,
.text:7C9232A6                                       ;   void * EstablisherFrame,
.text:7C9232A6                                       ;   struct _CONTEXT *ContextRecord,
.text:7C9232A6                                       ;   void * DispatcherContext )
.text:7C9232A8               mov   esp, large fs:0
.text:7C9232AF               pop   large dword ptr fs:0
.text:7C9232B6               mov   esp, ebp
.text:7C9232B8               pop   ebp
.text:7C9232B9               retn    14h



[*]构造inline hook 代码


// hook executehandler2
7C99AFC0    E8 00000000   call    7C99AFC5
7C99AFC5    5B            pop   ebx
7C99AFC6    83EB 05         sub   ebx, 5
7C99AFC9    81EB 00060000   sub   ebx, 600   //减出myexceptionhandle的地址
7C99AFCF    3B8B FC0F0000   cmp   ecx, dword ptr //比较是否是SE的SEHhandler
7C99AFD5    75 02         jnz   short 7C99AFD9
7C99AFD7    8BCB            mov   ecx, ebx //是的话就HOOK,否则放过
7C99AFD9    33DB            xor   ebx, ebx
7C99AFDB    FFD1            call    ecx
7C99AFDD    64:8B25 0000000>mov   esp, dword ptr fs:
7C99AFE4    64:8F05 0000000>pop   dword ptr fs:
7C99AFEB    8BE5            mov   esp, ebp
7C99AFED    5D            pop   ebp
7C99AFEE    C2 1400         retn    14


E8 00 00 00 00 5B 83 EB 05 81 EB 00 0600 00 3B 8B FC 0F 00 00 75 02 8B CB 33 DB FF D1 64 8B 2500 00 00 00 64 8F 05 00 00 00 00 8B E55D C2 14

// myexceptionhandle
00401000    60            pushad
00401001    E8 00000000   call    00401006
00401006    5B            pop   ebx
00401007    83EB 06         sub   ebx, 6
0040100A    81C3 00080000   add   ebx, 800
00401010    8BFB            mov   edi, ebx
00401012    8B7424 24       mov   esi, dword ptr //pExceptionRecord
00401016    B9 06000000   mov   ecx, 6
0040101B    F3:A5         rep   movs dword ptr es:, dword ptr
0040101D    8BFB            mov   edi, ebx
0040101F    83C7 30         add   edi, 30
00401022    8B7424 2C       mov   esi, dword ptr //pContextRecord
00401026    B9 18000000   mov   ecx, 18
0040102B    F3:A5         rep   movs dword ptr es:, dword ptr
0040102D    8B83 90000000   mov   eax, dword ptr
00401033    8D8B A0000000   lea   ecx, dword ptr
00401039    C1E0 02         shl   eax, 2
0040103C    03C8            add   ecx, eax
0040103E    8B7424 24       mov   esi, dword ptr
00401042    8B46 0C         mov   eax, dword ptr
00401045    8901            mov   dword ptr , eax
00401047    FF83 90000000   inc   dword ptr
0040104D    8D7B 30         lea   edi, dword ptr
00401050    8B83 E8070000   mov   eax, dword ptr //这里考虑可以在hook setthreadcontext中赋值

015A0056    8947 04         mov   dword ptr , eax
015A0059    8B83 EC070000   mov   eax, dword ptr
015A005F    8947 08         mov   dword ptr , eax
015A0062    8B83 F0070000   mov   eax, dword ptr
015A0068    8947 0C         mov   dword ptr , eax
015A006B    8B83 F4070000   mov   eax, dword ptr
015A0071    8947 10         mov   dword ptr , eax
015A0074    8B83 F8070000   mov   eax, dword ptr
015A007A    8947 18         mov   dword ptr , eax
015A007D    8B4424 30       mov   eax, dword ptr
015A0081    8B4C24 28       mov   ecx, dword ptr
015A0085    8D7B 30         lea   edi, dword ptr
015A0088    50            push    eax
015A0089    57            push    edi
015A008A    51            push    ecx
015A008B    53            push    ebx
015A008C    8B8B FC070000   mov   ecx, dword ptr
015A0092    33C0            xor   eax, eax
015A0094    33DB            xor   ebx, ebx
015A0096    33FF            xor   edi, edi
015A0098    33F6            xor   esi, esi
015A009A    FFD1            call    ecx
015A009C    61            popad
015A009D    C3            retn

60 E8 00 00 00 00 5B 83 EB 06 81 C3 0008 00 00 8B FB 8B 74 24 24 B9 06 00 00 00 F3 A5 8B FB 83 C7 30 8B 74 24 2C B9 18 00 00 00 F3 A58B 83 90 00 00 00 8D 8B A0 00 00 00 C1 E0 02 03 C8 8B 7424 24 8B 46 0C 89 01 FF 83 90 00 00 008D 7B 30 8B 83 E8 07 00 00 89 47 04 8B 83 EC 07 00 00 8947 08 8B 83 F0 07 00 00 89 47 0C 8B 83F4 07 00 00 89 47 10 8B 83 F8 07 00 00 89 47 18 8B 44 2430 8B 4C 24 28 8D 7B 30 50 57 51 53 8B8B FC 07 00 00 33 C0 33 DB 33 FF 33 F6 FF D1 61 C3 00 00
[*]Inline Hook ExecuteHandler2Hook在这里.text:7C9232A6               call    ecx   改成Jmp to // hook executehandler2


Patch vm:ds
         这个地址是前面已经找到的,由于暂时还没找到HASH检测的代码位置,所以不能直接改代码,我的方法是,用脚本在这个位置上设置一个硬件执行断点在,当脚本断下后,脚本自动修改代码跳转到我们自己分配的空间内执行一段代码,在代码的开头立即修复之前被脚本改掉的代码,然后产生一个single step异常给系统,再执行之前由于改成跳转没有执行到的程序源码后跳转到原程序下一行继续。
         这个方法是确定是要被占用一个硬件断点。
以下是我构造的代码:
//跳转来自0E8A7C8
00390100    60            pushad
00390101    BE 00023900   mov   esi, 390200
00390106    BF C8A7E800   mov   edi, 0E8A7C8
0039010B    B9 05000000   mov   ecx, 5
00390110    F3:A4         rep   movs byte ptr es:, byte ptr //还原ds被我修改用来跳转的代码
00390112    61            popad
00390113    9C            pushfd                     \
00390114    66:810C24 0001or      word ptr , 100    3条指令用来产生一个80000004 的 EXCEPTION_SINGLE_STEP
0039011A    9D            popfd                      /    就是通过符号位中的单步标记来产生一个异常
0039011B    8B10            mov   edx, dword ptr --读取数值,由于硬件执行断点的那行没有执行
0039011D- E9 A8A6AF00   jmp   乔巴.00E8A7CA      --返回执行下一行代码


Patch Shadow_SetThreadContext
为了防止硬件断点被覆盖,这个Shadow函数必须Patch掉,当然,如果前面的部分没有Patch 而直接来改这个函数,SE就不会获得需要检测的异常,造成程序的退出。
1、搜索代码
通过之前patch Shadow_GetThreadContext获得的调用KiFastSystemCall前代码区域,查找调用服务号为0D5 的调用,


[*]修改代码只需要把跳入KiFastSystemCall的位置直接nop掉就行

FFE2            jmp   edx// 这里是跳入KiFastSystemCall,直接nop

Patch掉HASH 计算
         通过上面几步,现在我可以下硬件断点了,能断下来的第一件事,当然是找HASH效验代码。

[*]在前面vm:ds的位置后面几行下个硬件读取断点断下之后:


.sedata:00E6323F               push    esi
.sedata:00E63240
.sedata:00E63240 loc_E63240:                           ; CODE XREF: .sedata:00E6324Ej
.sedata:00E63240               movzx   esi, byte ptr
.sedata:00E63244               rol   eax, 7 //断在这里
.sedata:00E63247               xor   eax, esi
.sedata:00E63249               inc   ecx
.sedata:00E6324A               cmp   ecx,
.sedata:00E6324E               jb      short loc_E63240
.sedata:00E63250               pop   esi
.sedata:00E63251               jmp   short locret_E6325C



[*]Run Trace 后获得整个循环体,直接找到跳出循环的位置来到这里:


00E63247 Main   xor   eax, esi                        ; FL=PZ, EAX=F4E889ED, ECX=00093F9F, ESI=000000D0
00E63250 Main   pop   esi                              ; ESP=0012FEC8, ESI=F4E889ED
00E63251 Main   jmp   short 00E6325C
00E6325C Main   retn    4                                  ; ESP=0012FED0            //hash 值保存在eax



[*]重新加载几次之后,发现HASH计算的参数和结果是不变的,我只能说,呵呵

检测开始位置:0E7C91D
检测大小:       0093F9F
返回EAX:    F4E889ED
         

[*]偷懒直接patch 掉
//------ Fix HASH------

00E63240    B8 ED89E8F4   mov   eax, F4E889ED
00E63245    90            nop
00E63246    90            nop
00E63247    90            nop
00E63248    90            nop
00E63249    90            nop
00E6324A    90            nop
00E6324B    90            nop
00E6324C    90            nop
00E6324D    90            nop
00E6324E    90            nop
00E6324F    90            nop

weiwj520 发表于 2018-3-16 20:13

Sound 发表于 2017-4-14 15:39
不错不错 自从手动解决比较麻烦后 现在一般都是跑脚本 。

大佬,请指教哪里有破se反断点脚本?
最近一段时间,一直在研究这个帖子,看了不下二十遍,无奈基础还是欠缺,还是没法完全读懂。
请求指点迷津!!!

Some 发表于 2017-4-13 09:27

xiaohang99 发表于 2017-4-12 22:34
样本我也想上传,可惜太大,压缩好有50mb,没办法,个人等级太低

可以使用网盘,如 微云,百度云

mayl8822 发表于 2017-4-11 10:16

66666666666666谢谢分享, 学习了

imtom123 发表于 2017-4-11 11:57

太神啦 谢谢楼主

Some 发表于 2017-4-11 12:36

可以提供样本么?提高针对性,

无痕软件 发表于 2017-4-11 12:47

好文!!{:1_921:}

huzhongyang 发表于 2017-4-11 14:24

好文!!好文!!好文!!

xiaodou 发表于 2017-4-11 16:25

好文~这必须支持下~~mark~

gunxsword 发表于 2017-4-11 16:36

这都是牛人啊!

he5888 发表于 2017-4-11 19:27

这都是牛人啊!

心平气和123 发表于 2017-4-11 19:37

都是牛人啊!我看这些太多看不懂的。
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Safengine Shielden 2.3.8.0 脱壳 之 为了能下断