能力值:
( LV2,RANK:10 )
2 楼
P222页,书中提到 MetaData Root 元数据头的官方定义,下文所示。
但我查了google, 搜索了MSDN并未找到“官方定义”,后来在找到一个CLI规范,查到.Net PE实现依据的标准文档,读者可自行搜索关键字 《CLI_Ecma-335-part-i-iv》
但由于标准并未严格定义所有结构体字段,部分.Net 实现的标准是有差异的,更权威的资料参考书中提到的《Expert .NET 2.0 IL Assembler - Serge Lidin》
使用上面的资料,你可以比较容易配合《加密与解密》.Net 扩展PE中提到的结构体,Metadata table、IL指令等进行深入学习。
struct STORAGESIGNATURE
{
ULONG Signature; // Magic signature for physical metadata : 0x424A5342.
USHORT MajorVersion; // Major version, 1 (ignore on read)
USHORT MinorVersion; // Minor version, 0 (ignore on read)
ULONG ExtraData; // offset to next structure of information
ULONG Length ; // Length of version string in bytes
};
typedef STORAGESIGNATURE UNALIGNED * PSTROAGESIGNATURE;
struct STORAGEHEADER
{
BYTE Pad; // Padding to next 4 byte boundary
BYTE Flags; // Reserved, always 0
USHORT Streams; // Number of streams, say n.
};
能力值:
( LV2,RANK:10 )
3 楼
P228 9.2.3 节上面的内容,提到使用SDK Peverfy.exe 来验证IL代码的正确性,辅助IL编程。
本人电脑安装了 Visual Studio 2015, 未发现此文件。
能力值:
( LV2,RANK:10 )
4 楼
P232 经 Win10 x64,win7 x64测试PEBrowse Professional Interactive 无法调试《加密与解密》9.3.1 中的32位 craceme.exe程序,光盘提供的工具也无法调试,替代方案,使用dnSpy进行IL级代码调试,汇编级调试只能使用 XP环境
windows XP + .Net2.0 环境 可调试
具体情况参考文章:http://bbs.pediy.com/showthread.php?t=205578&page=3
能力值:
( LV2,RANK:10 )
5 楼
dnSpy 可正常反编译书上示例的混淆后的IL代码(Reflector同样可正常反编译,书上说的异常已经不存在)
dnSpy v3.0.1, Reflector v8.4
具体内容:http://bbs.pediy.com/showthread.php?t=205578&page=3
建议:说明试验环境,或删除该节内容
能力值:
( LV2,RANK:10 )
6 楼
光盘文件:9.4 代码保护技术及其逆向\9.4.4 压缩\bsp加壳\加壳的sample1044.exe
以上加壳文件,在win10环境下无法正常运行,使用bsp 手动对sample1044.exe 加壳,依然无法运行(原始sample1044.exe文件正常运行)
建议:更新示例,或删除该节内容
以下是本节dump学习笔记
9.4.4 脱压缩壳
压缩壳:本节讨论加壳软件中不含元数据加密功能的壳。
背景:本节程序运行 .Net 1.1 下,而其它版本的壳基本原理是相同的。
示例程序代码
.assembly extern mscorlib{}
.assembly sample1044{}
.module sample1044.exe
.imagebase 0x00400000 //可省略
.subsystem 0x0003 //可省略
.namespace tankaiha.sample1044
{
.class private auto ansi beforefieldinit class1 extends [mscorlib]System.Object
{
.method public hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 1
.locals init (class [mscorlib]System.Reflection.Assembly V_0)
IL_0000: nop
IL_0001: call class [mscorlib]System.Reflection.Assembly [mscorlib]
System.Reflection.Assembly::GetEntryAssembly()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance string [mscorlib]System.Object::ToString()
IL_000d: call void [mscorlib]System.Console::WriteLine(string)
IL_0012: nop
IL_0013: ret
}
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
}
}
示例程序功能
通过 System.Reflection空间 Assembly.GetEntryAssembly()方法,取得正运行的 Assembly 全名并输出
程序输出信息
sample1044, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
两类压缩壳
.Net平台压缩壳
工具
Sixxpack 工具(更名为AdeptCompressor)
bsp 工具(.NETZ)
Sixxpack 原理
将原程序压缩后,存储于新文件;
运行时动态解压到 bytes[] 数组;
核心代码
Assembly assembly1 = Assembly.Load(buffer);
assembly1.EntryPoint.Invoke(null, null);
.Net 支持内存中 Assembly 载入,然后 Invoke调用该Assembly 的方法
bsp 原理
原理与Sixxpack基本相同,压缩算法不同
解压方法
方法一:手动 dump(下文以 bsp 为例)
方法二:工具 dump
方法三:根据解密算法直接还原程序
Win平台压缩壳(略)
bsp压缩壳手动dump示例
说明:Sixxpack,bsp压缩后的程序都失效,无法正常运行,此处仅展示其脱壳原理
目标程序:9.4.4 压缩\bsp加壳\加壳的sample1044.exe
dnSpy v3.0.1 反编译程序,程序入口C#代码如下
using System;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Resources;
namespace sHell
{
// Token: 0x02000002 RID: 2
public sealed class bsp
{
// Token: 0x06000001 RID: 1 RVA: 0x0000207C File Offset: 0x0000047C
public static void \u0020()
{
object obj = null;
Assembly assembly = Assembly.GetExecutingAssembly();
byte[] array = null;
byte[] array2 = null;
ResourceReader resourceReader = new ResourceReader(assembly.GetManifestResourceStream("*"));
IDictionaryEnumerator enumerator = resourceReader.GetEnumerator();
while (enumerator.MoveNext())
{
if ("B".Equals((string)enumerator.Key))
{
array = (byte[])enumerator.Value;
}
if ("BC".Equals((string)enumerator.Key))
{
array2 = (byte[])enumerator.Value;
}
}
int num = 0;
byte b = 132;
byte[] publicKey = assembly.GetName().GetPublicKey();
int num2 = array.Length;
for (int i = 0; i < num2; i++)
{
b += publicKey[num];
byte[] array3;
IntPtr intPtr;
(array3 = array)[(int)(intPtr = (IntPtr)i)] = (array3[(int)intPtr] ^ b);
if (++num >= 160)
{
num = 0;
}
}
MemoryStream memoryStream = new MemoryStream(array);
MemoryStream memoryStream2 = new MemoryStream();
int[] array4 = new int[33];
int num3;
while ((num3 = memoryStream.ReadByte()) != -1 && num3 != 0)
{
int num4 = num3;
int num5 = 0;
for (int j = 0; j < 4; j++)
{
num5 = (num5 << 8 | memoryStream.ReadByte());
}
array4[num4] = num5;
}
int num6 = 32;
uint num7 = 0u;
ArrayList arrayList = new ArrayList();
do
{
int num8 = array4[num6];
for (int k = 0; k < num8; k++)
{
arrayList.Add(new Bsp(num6, num7));
num7 += 1u;
}
num6--;
num7 >>= 1;
}
while (num6 > 0);
int count = arrayList.Count;
byte[] array5 = new byte[count];
memoryStream.Read(array5, 0, count);
int num9 = 0;
for (int l = 0; l < 4; l++)
{
num9 = (num9 << 8 | memoryStream.ReadByte());
}
arrayList.Reverse();
int num10 = 0;
byte b2 = 0;
int num11 = 0;
uint num12 = 0u;
int m = 0;
while (m < num9)
{
if (num10 == 0)
{
int num13 = memoryStream.ReadByte();
if (num13 == -1)
{
break;
}
b2 = (byte)num13;
num10 = 8;
}
num10--;
num11++;
if (num11 > 32)
{
break;
}
num12 <<= 1;
num12 |= (uint)b2 >> 7;
b2 = (byte)(b2 << 1);
for (int n = 0; n < count; n++)
{
Bsp bsp = (Bsp)arrayList[n];
if (num11 < bsp.%)
{
break;
}
if (num11 <= bsp.% && bsp.% == num12 << 32 - num11)
{
memoryStream2.WriteByte(array5[n]);
m++;
num11 = 0;
break;
}
}
}
byte[] array6 = memoryStream2.ToArray();
memoryStream2.Close();
memoryStream.Close();
Assembly arg_2D1_0 = Assembly.Load(array6);
array6[0] = 1;
array6[1] = 2;
GC.Collect();
assembly = arg_2D1_0;
assembly.GetType("sHell. ").GetMethod(" ").Invoke(null, new object[]
{
obj,
array2
});
}
}
}
《加密与解密3rd》P244 断点处代码定位
书中IL代码如下,使用dnSpy IL代码模式定位到 IL_02BB 处,注意到上下文的(Load,Invoke) 方法
/* 0x00000743 1123 */ IL_02BB: ldloc.s 35
/* 0x00000745 281700000A */ IL_02BD: call class [mscorlib]System.Reflection.Assembly [mscorlib]System.Reflection.Assembly::Load(uint8[])
/* 0x0000074A 1123 */ IL_02C2: ldloc.s 35
/* 0x0000074C 16 */ IL_02C4: ldc.i4.0
/* 0x0000074D 17 */ IL_02C5: ldc.i4.1
/* 0x0000074E 9C */ IL_02C6: stelem.i1
/* 0x0000074F 1123 */ IL_02C7: ldloc.s 35
/* 0x00000751 17 */ IL_02C9: ldc.i4.1
/* 0x00000752 18 */ IL_02CA: ldc.i4.2
/* 0x00000753 9C */ IL_02CB: stelem.i1
/* 0x00000754 281800000A */ IL_02CC: call void [mscorlib]System.GC::Collect()
/* 0x00000759 0B */ IL_02D1: stloc.1
...
/* 0x00000784 6F1B00000A */ IL_02FC: callvirt instance object [mscorlib]System.Reflection.MethodInfo::Invoke(object, object[])
/* 0x00000789 26 */ IL_0301: pop
/* 0x0000078A DE03 */ IL_0302: leave.s IL_0307
/* 0x0000078C 26 */ IL_0304: pop
/* 0x0000078D DE00 */ IL_0305: leave.s IL_0307
/* 0x0000078F 2A */ IL_0307: ret
WinHex dump 内存数据
◊ dnspy 断点
▪ IL_02BB 代码
IL_02BB: ldloc.s 35
▪ 对应C#代码
Assembly arg_2D1_0 = Assembly.Load(array6);
▪ 然后 dump 变量 arg_2D1_0 所在地址的内存,此处 dump 出的数据是 bsp的一个 loader, 程序通过该 loader 再调用原程序,是否dump该loader与目标无关,下面介绍dump方法
◊ dump 方法
▪ dnSpy 对 arg_2D1_0 所在代码行下断点,查询内存值 x
▪ Menu/Tools/Open Memory(Alt+F9)
▪ Alt + G 跳转到内存 x 处
▪ 内存 x + 0x08 处为 "MZ", 而 x + 0x04 为 Image Size,自行计算dump 的内存区段
◊ 跟踪Invoke 方法,运行到 System.Reflection.Assembly::GetEntryAssembly() 方法处,使用WinHex定位相应内存,向上查询PE签名字符"MZ"
◊ 由于内存成片分配,dump 大小可据此反复尝试,直到dump成功
压缩壳原理总结
其它保护方案
◊ 商业软件如 {smartassembly},其壳将多个文件加密保存,并使用强名称作为解密密钥,并且加入强大混淆
◊ 使用支持.Net PE的传统壳,特点是生成 win32 而非.Net;
共同特点
◊ 压缩壳非.Net保护技术主流;
◊ 此类壳都会在运行时,将原文件在内存中完整展开,此种加壳方式叫"Whole Assembly Protection",这种方式保护强度很弱,可轻易被 NET Unpacker 之类的脱壳软件 dump;
能力值:
( LV2,RANK:10 )
7 楼
书中提到“用OllydDbg任意跟踪一个.Net PE加载过程”,实际上 win7 x64, win10 x64环境下,OD无法打开 .Net PE文件
OD2.01 提示以下信息:
Error: Unable to start file "xxx.exe"
通过Google 发现以下信息:
1) 部分系统将的PE Loader 并不将.Net PE 识别为本地程序调试
On Windows 2000, the Windows PE loader treated .NET exes just like normal ones regarding this import - you could put a BP there and it would stop.
With XP though, the Windows PE loader handles .NET assemblies internally, and don't call/use that entry in import table.
https://forum.exetools.com/showthread.php?t=8459
2) OD2.01 官方文档说明,不支持 .Net PE调试
OllyDbg2 does mention that it can parse CIL (I suppose Oleh meant MSIL...) in the 2.01 alpha 2 notes:
https://www.reddit.com/r/ReverseEngineering/comments/2coysr/net_crackmes_ollydbg/
尝试了 x32 dbg ,x32 MDebug 均无法调试
尝试WinDbg x64,可调试,注意到 .net pe 本身是32位的,可能是IL代码解释为本地代码时,解释为64位程序
继续尝试x64 dbg 也成功调试, 但调试的地址空间不正确,因此只能静态分析!!
XP 系统 + .Net2.0 环境,OD2.01可正常调试不加壳.Net 32位PE,但尝试过程中,仍然存在调试异常。
放弃动态分析,尝试IDA静态分析,也无法在Win10下分析。。。
能力值:
( LV2,RANK:10 )
8 楼
[P249 .Net PE方法Hex数据定位] P248-249 学习笔记
说明
▪ IL代码放置在.text PE section,对内存下硬件断点,可直接定位到 PE IL代码解密后的源代码。
▪ JIT执行前会将加密IL代码和Metadata解密,执行后会加密。
▪ 此处断点后分析 .Net 方法头、方法体,因无法运行加壳程序,此时仅分析未加壳程序。
目标程序:9.4.5 加密\raw_Crackme.exe
dnSpy 定位 Crackme/Form1/Dispose 方法
dnSpy 定位方法 Hex 数据:Dispose方法上鼠标右击,选中“在十六进制编译器中显示方法体”
方法 Hex 数据如下
00001050h: 13 30 02 00 2B 00 00 00 01 00 00 11 00 03 2C 0B ; .0..+.........,.
00001060h: 02 7B 01 00 00 04 14 FE 01 2B 01 17 0A 06 2D 0E ; .{.....?+....-.
00001070h: 00 02 7B 01 00 00 04 6F 10 00 00 0A 00 00 02 03 ; ..{....o........
00001080h: 28 11 00 00 0A 00 ; (.....
方法头分析
00001050h: 13 30 02 00 2B 00 00 00 01 00 00 11
此方法头为 fat header 具体含义如下(表格见P249)
注意:看到内存数据 [13 30] 首先想到可能是 fat 方法头 [Task Explorer更新]
Task Explorer 在Explorer Suite 中提供,URL如下:
http://www.ntcore.com/exsuite.php
Explorer Suite 提供以下程序组件:
CFF Explorer.exe
PE Detective.exe
Signature Explorer.exe
Task Explorer-x64.exe
Task Explorer.exe
能力值:
(RANK:350 )
9 楼
这章tankaiha没时间更新了,这章节出版社正在校对,若有人愿意在1个月内更新一下,应该还来得及。
能力值:
( LV2,RANK:10 )
10 楼
还没看完,进度比较慢,如果可以,很乐意帮忙
能力值:
( LV2,RANK:10 )
11 楼
国外壳加密壳 dump 实例
目标程序:9.4.5 加密\国外壳\例子.msi(需要安装,分析安装后的文件)
rscoree.dll [_RSEEStartup] 反汇编分析
* IDA Pro/View/OpenSubviews/Export/_RSEEStartup 定位代码
* dll 名称混淆代码段A 逻辑分析
---------------------------------------------------------------------------------------
.text:10002CC5 push offset aVvn8StYmW ; "vvn8~st|ym}w"
.text:10002CCA call sub_10001680
IDA 分析函数“sub_10001680”伪代码,并使用 python 表示如下
# 'mscorjit.dll'
s = "76 76 6E 38 7E 73 74 7C 79 6D 7D 77"
def deobfuscate_dll(s):
# s = "76 76 6E 38 7D 75 81 7C 79 6D 7D 77"
s = reversed([int(i, 16) - 10 for i in s.split()])
return ''.join([chr(i) for i in s])
--------------------------------------------------------------------------------------- x64dbg 动态分析
WinXP下x64dbg F3 加载程序, F9运行,让所有模块成功载,以方便下载断点
对 mscorjit.dll[compileMethod]下断点
[compileMethod] 逆向分析
-----------------------------------------
7906E7F5 | mov ebp,esp |
7906E7F7 | sub esp,10 |
7906E7FA | push dword ptr ss:[ebp+14] |
7906E7FD | mov eax,dword ptr ss:[ebp+10] |
7906E800 | mov ecx,dword ptr ds:[eax] | [eax]:const CILJit::`vftable'
7906E802 | and dword ptr ss:[ebp-4],0 |
7906E806 | and dword ptr ss:[ebp-C],0 | [ebp-C]:__except_handler4
7906E80A | and dword ptr ss:[ebp-10],0 |
7906E80E | and dword ptr ss:[ebp-8],0 |
7906E812 | lea edx,dword ptr ss:[ebp-8] | edx:_KiFastSystemCallRet@0
7906E815 | push edx |
7906E816 | lea edx,dword ptr ss:[ebp-C] | edx:_KiFastSystemCallRet@0, [ebp-C]:__except_handler4
7906E819 | push edx |
7906E81A | lea edx,dword ptr ss:[ebp-10] | edx:_KiFastSystemCallRet@0
7906E81D | push edx |
7906E81E | push dword ptr ss:[ebp+1C] |
7906E821 | lea edx,dword ptr ss:[ebp-4] | edx:_KiFastSystemCallRet@0
7906E824 | push edx | ICorJitInfo
7906E825 | mov edx,dword ptr ds:[eax+4] |
7906E828 | push eax |
7906E829 | push dword ptr ss:[ebp+C] |
7906E82C | call <mscorjit.int __fastcall jitNativeCode(struct CORINFO_METHOD_STRUCT_ *,struct CORINFO_MODULE_STRUCT_ *,class ICorJitInfo *,struct CORINFO_METHOD_INFO *,void * *,unsigned long *,void * *,void * *,void * *,unsigned int)> |
7906E831 | test eax,eax |
7906E833 | jne mscorjit.7906E83D |
7906E835 | mov ecx,dword ptr ss:[ebp+18] | ecx:const CILJit::`vftable'
7906E838 | mov edx,dword ptr ss:[ebp-4] | edx:_KiFastSystemCallRet@0
7906E83B | mov dword ptr ds:[ecx],edx | [ecx]:private: virtual enum CorJitResult __stdcall CILJit::compileMethod(class ICorJitInfo *,struct CORINFO_METHOD_INFO *,unsigned int,unsigned char * *,unsigned long *)
7906E83D | leave |
7906E83E | ret 18 |
------------------------------------------------
[jitNativeCode] 参数分析
-------------------------
x64dbg 对以下代码下断点,并分析堆栈参数
-----------
7906E82C | call <mscorjit.int __fastcall jitNativeCode(
struct CORINFO_METHOD_STRUCT_ *,
struct CORINFO_MODULE_STRUCT_ *,
class ICorJitInfo *,
struct CORINFO_METHOD_INFO *,
void * *,unsigned long *,void * *,void * *,void * *,unsigned int)> |
----------------
注意此处使用 fastcall 函数调用约定,前两个参数从左向右依次使用寄存器存放,其余参数从右向左压栈
;struct CORINFO_METHOD_STRUCT_ * -> EAX
;struct CORINFO_MODULE_STRUCT_ * -> ECX
0012EC34 0012EDAC ;class ICorJitInfo *
0012EC38 0012EE38 ;struct CORINFO_METHOD_INFO *
0012EC3C 0012EC60 ;void * *
0012EC40 0012EEC4 ;unsigned long *
0012EC44 0012EC54 ;void * *
0012EC48 0012EC58 ;void * *
0012EC4C 0012EC5C ;void * *
0012EC50 00107210 ;unsigned int
0012EDAC E4 7B E9 79 14 7C E9 79 08 80 19 00 C0 44 26 01 ä{éy.|éy....ÀD&.
0x79E97BE4 ; x64dbg 内存布局指向 .text section
P253 总结
1) CodeView及第二种壳的 Hook方法,最多只能称为 Wrapper
◊ CodeView 采用对所有元数据一次性加密的方法,在JIT运行时解密,运行后加密;
◊ 国外壳将.Net PE所有方法都加密
• 调用方法时,通过recore.dll动态解密方法并调用;
• recore.dll混淆 dll、func 名称,来达到隐藏 corjit.dll [compileMethod] 的目的,即Hook compileMethod 来解密代码,并将解密代码传递给 compileMethod;
• corjit.dll [compileMethod]作为万能断点使用,可进一步分析具体 .Net PE 类的方法逻辑;
2) 强加密壳:Hook 内核 dll,改变其代码流程,从而在 .Net 内核 JIT的过程中进行加密与解密
3) 脱壳
◊ 方法一:进程注入,而后反射的方法取得源代码和元数据,再重构数据;
◊ 方法二:Hook JIT层得到代码,此法更通用;
4) 软件保护建议:混淆 + 加密结合使用
能力值:
( LV2,RANK:10 )
12 楼
P256 所使用的工具 injectReflector,在XP 及win10环境下无法使用,不清楚此反射机制对于当前的.Net环境是否适用
能力值:
( LV2,RANK:10 )
13 楼
在P259 图9.40 mscordbc.dll 下方第一行描述“下面利用OllydDbg调试带Profiler启动的程序”
问题:
1) 这个程序是哪个程序?随书光盘是否已经提供?(本人未找到)
2) 如何下的断点?并定位到mscorwks.dll 中MakeJitWorker API代码处? 《加密与解密》第三版 第9章 .NET平台加解密 算是自学完成了,以下链接是onenote笔记,需要的同学可以下载:
http://pan.baidu.com/s/1nv57ppB
为何需要?
1) 内容及资料进行扩展补充
2)部分语言重新组织,整体逻辑更清晰
3) 帮助绕过各种坑
最后,感谢本章作者,虽然存在一些小问题,但瑕不掩瑜,授之以渔,不仅给了大方向,也写的很深入。
能力值:
( LV2,RANK:10 )
14 楼
在 SDK 的路径下找,比如,VS2008 这个文件的路径为:
C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\PEVerify.exe
能力值:
( LV2,RANK:10 )
15 楼
谢谢,我是VS2015,使用Search Everything 全盘搜索的,应该是VS版本问题