转帖一篇sixxpack破解的文章!
http://www.cnblogs.com/dczsf/archive/2009/11/02/1594256.html
星期天闲着没事玩游戏,玩游戏不能无外挂。于是百度了半天,找到了一个,看介绍貌似不错,就下载了下来。一看,竟然是用.net写的,下意识地Reflector了一下。发现竟是一个叫actmp的程序集。如图:
随便点开看了看,没发现什么和外挂有关的东西。奇怪,于是又百度了一下"actmp.dll",发现了这篇文章
http://blog.csdn.net/yizhiduxiu11/archive/2008/12/23/3589396.aspx,其中说"
经Sixxpack加壳后,用Reflector打开exe文件,哈哈,看到的永远都只是一个actmp.dll的信息,其中包含4个7zip命名空间和1个Sixxpack命名空间,反编出来这些东西对也没啥处......"。 基本明白咋回事了。我又用“Sixxpack”做关键字搜了一下,果然找到了一堆这个软件的信息,其中说"
Sixxpack 是一个 .net EXE 文件压缩工具。经 Sixxpack 压缩过的文件,运行时与压缩前相比没有任何区别,压缩比最大可达80%。压缩后的文件更难被反编译,保护您的程序不被破解......"
感慨搜索引擎强大的同时,我们可以确定,这个程序100%是用Sixxpack处理过了。目的可能是为了减少体积,更可能是为了防止俺这样随便Reflector的人。如果用其他工具压缩也还罢了,但是Sixxpack貌似也是.net写的,而且没有混淆过,不看一看他的代码多对不起自己.net程序员的光荣称号啊。为了查看方便,我用File Disassembler插件把源码导了出来,找到了Main方法所在的类。核心的东西在Main方法里,很简单,我加上了注释。代码如下:
namespace
Sixxpack
{
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
internal class stub
{
internal static int orig = 0x8000 ; // 加密程序集的偏移量
[STAThread]
private static void Main( string [] args)
{
try
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(stub.myResolveEventHandler); // 程序集解析失败的后路
}
catch (Exception)
{
}
object [] parameters = new object [] { args }; // 参数,如果有,就传给解压出来的程序集
MemoryStream inStream = new MemoryStream(); // 构造一个内存流
Stream stream2 = new FileStream(Application.ExecutablePath.Substring(Application.ExecutablePath.LastIndexOf( ' // ' ) + 1 ), FileMode.Open, FileAccess.Read); // 打开一个文件流,指向正在运行的这个文件
stream2.Position = orig; // 当前流的位置设置为加密程序集的偏移量
byte [] buffer = new byte [stream2.Length - orig]; // 一个缓冲字节数组,长度为加密程序集的长度,用来存储加密程序集
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length)); // 把加密程序集读取到缓冲数组里
inStream.Write(buffer, 0 , buffer.Length); // 把加密程序集从缓冲数组里写到内存流里
inStream.Seek( 0L , SeekOrigin.Begin); // 把当前流的位置放到开始处
Compressor compressor = new Compressor();
Assembly assembly = Assembly.Load(compressor.Decompress(inStream)); // 把内存流中的数据解压出来,并加载到一个程序集
try
{
assembly.EntryPoint.Invoke( null , parameters); // 执行这个程序集的入口方法,并传给参数
}
catch
{
assembly.EntryPoint.Invoke( null , null ); // 没有参数的话就不传
}
}
private static Assembly myResolveEventHandler( object sender, ResolveEventArgs args)
{
string path = args.Name.ToString().Trim().Split( new char [] { ' , ' })[ 0 ].ToString().Trim();
if (path.IndexOf( " . " ) >= 0 )
{
path = path.Replace( " . " , " _ " ) + " .dll " ;
}
else
{
path = " _ " + path + " .dll " ;
}
MemoryStream inStream = new MemoryStream();
Stream stream2 = new FileStream(path, FileMode.Open, FileAccess.Read);
byte [] buffer = new byte [stream2.Length];
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length));
inStream.Write(buffer, 0 , buffer.Length);
inStream.Seek( 0L , SeekOrigin.Begin);
Compressor compressor = new Compressor();
return Assembly.Load(compressor.Decompress(inStream));
}
}
}
{
using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
internal class stub
{
internal static int orig = 0x8000 ; // 加密程序集的偏移量
[STAThread]
private static void Main( string [] args)
{
try
{
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(stub.myResolveEventHandler); // 程序集解析失败的后路
}
catch (Exception)
{
}
object [] parameters = new object [] { args }; // 参数,如果有,就传给解压出来的程序集
MemoryStream inStream = new MemoryStream(); // 构造一个内存流
Stream stream2 = new FileStream(Application.ExecutablePath.Substring(Application.ExecutablePath.LastIndexOf( ' // ' ) + 1 ), FileMode.Open, FileAccess.Read); // 打开一个文件流,指向正在运行的这个文件
stream2.Position = orig; // 当前流的位置设置为加密程序集的偏移量
byte [] buffer = new byte [stream2.Length - orig]; // 一个缓冲字节数组,长度为加密程序集的长度,用来存储加密程序集
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length)); // 把加密程序集读取到缓冲数组里
inStream.Write(buffer, 0 , buffer.Length); // 把加密程序集从缓冲数组里写到内存流里
inStream.Seek( 0L , SeekOrigin.Begin); // 把当前流的位置放到开始处
Compressor compressor = new Compressor();
Assembly assembly = Assembly.Load(compressor.Decompress(inStream)); // 把内存流中的数据解压出来,并加载到一个程序集
try
{
assembly.EntryPoint.Invoke( null , parameters); // 执行这个程序集的入口方法,并传给参数
}
catch
{
assembly.EntryPoint.Invoke( null , null ); // 没有参数的话就不传
}
}
private static Assembly myResolveEventHandler( object sender, ResolveEventArgs args)
{
string path = args.Name.ToString().Trim().Split( new char [] { ' , ' })[ 0 ].ToString().Trim();
if (path.IndexOf( " . " ) >= 0 )
{
path = path.Replace( " . " , " _ " ) + " .dll " ;
}
else
{
path = " _ " + path + " .dll " ;
}
MemoryStream inStream = new MemoryStream();
Stream stream2 = new FileStream(path, FileMode.Open, FileAccess.Read);
byte [] buffer = new byte [stream2.Length];
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length));
inStream.Write(buffer, 0 , buffer.Length);
inStream.Seek( 0L , SeekOrigin.Begin);
Compressor compressor = new Compressor();
return Assembly.Load(compressor.Decompress(inStream));
}
}
}
从以上代码,我们可以猜测,Sixxpack把压缩过的程序集和Sixxpack的解压程序(也就是壳)放在了一个文件里。运行程序时,Sixxpack的解压程序先运行,然后执行以下步骤:
1.构造一个内存流。
2.取出压缩后的程序集二进制数据,放进内存流。
所谓压缩后的程序集二进制数据,也就是这个文件的第0x8000(即程序里定义的orig字段)到最后一个字节的数据。至于为啥是从0x8000开始,我想0x8000应该是壳的长度吧,壳的数据占据了从0到0x8000字节的空间,剩下的应该就是压缩后的程序集的数据了。
3.用Compressor类的Decompress方法解压内存流数据,得到原始程序集。
4.用反射加载原始程序集,找到程序集的入口方法,并执行这个入口方法(这时Sixxpack已完成任务,控制权已转交给解压出来的程序集),如果入口方法需要参数的话,会把Main方法的参数传给入口方法。
既然明白了解压的方法,那么想拿到原始的程序集也不是什么难事了。只要知道压缩后的程序集在文件中的偏移量(我猜测应该都是0x8000,不过没看过其他例子,不敢确定),就可以把它取出来,然后用Compressor类的Decompress方法解压即可得到原始程序集,最后输出到一个文件就行了。
代码很好写,大部分照抄Main方法就行了,至于Decompress方法,不用管它具体怎么实现的,反射过来直接使用即可。下面是主要代码,我直接写在界面里了,懒得再独立写一个方法:
private void btnDecompress_Click(object sender, EventArgs e)
{
try
{
Assembly assembly = Assembly.LoadFile(txtAssemblyPath.Text); // 加载指定程序集
if (assembly != null )
{
if (assembly.FullName.Contains( " actmp, " )) // 看看是不是Sixxpack压缩过的程序集
{
Type compressorClass = assembly.GetType( " Sixxpack.Compressor " ); // 得到Compressor方法的类型
MethodInfo DecompressInfo = compressorClass.GetMethod( " Decompress " ); // Decompress方法,供后面使用
int orig = Convert.ToInt32(assembly.GetType( " Sixxpack.stub " ).GetField( " orig " , BindingFlags.NonPublic | BindingFlags.Static).GetValue( null )); // 压缩后的程序集的偏移量,我虽然强烈怀疑就是0x8000,为了保险起见,还是反射出来吧。
#region 这段照抄Main方法的内容
MemoryStream inStream = new MemoryStream();
Stream stream2 = assembly.GetFiles()[ 0 ]; // 这么写就可以了
stream2.Position = orig;
byte [] buffer = new byte [stream2.Length - orig];
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length));
inStream.Write(buffer, 0 , buffer.Length);
inStream.Seek( 0L , SeekOrigin.Begin);
#endregion
byte [] data = ( byte [])DecompressInfo.Invoke(Activator.CreateInstance(compressorClass), new object [] { inStream }); // 调用Decompress方法,获得原始程序集的数据
if (sfdAssembly.ShowDialog() == DialogResult.OK)
{
sfdAssembly.OpenFile().Write(data, 0 , data.Length); // 把数据保存到指定文件,OK,完工!
}
MessageBox.Show( " 文件解压成功! " , " 恭喜 " , MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show( " 未找到正确的程序集! " , " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
else
{
MessageBox.Show( " 该文件没有程序集! " , " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
try
{
Assembly assembly = Assembly.LoadFile(txtAssemblyPath.Text); // 加载指定程序集
if (assembly != null )
{
if (assembly.FullName.Contains( " actmp, " )) // 看看是不是Sixxpack压缩过的程序集
{
Type compressorClass = assembly.GetType( " Sixxpack.Compressor " ); // 得到Compressor方法的类型
MethodInfo DecompressInfo = compressorClass.GetMethod( " Decompress " ); // Decompress方法,供后面使用
int orig = Convert.ToInt32(assembly.GetType( " Sixxpack.stub " ).GetField( " orig " , BindingFlags.NonPublic | BindingFlags.Static).GetValue( null )); // 压缩后的程序集的偏移量,我虽然强烈怀疑就是0x8000,为了保险起见,还是反射出来吧。
#region 这段照抄Main方法的内容
MemoryStream inStream = new MemoryStream();
Stream stream2 = assembly.GetFiles()[ 0 ]; // 这么写就可以了
stream2.Position = orig;
byte [] buffer = new byte [stream2.Length - orig];
stream2.Read(buffer, 0 , Convert.ToInt32(buffer.Length));
inStream.Write(buffer, 0 , buffer.Length);
inStream.Seek( 0L , SeekOrigin.Begin);
#endregion
byte [] data = ( byte [])DecompressInfo.Invoke(Activator.CreateInstance(compressorClass), new object [] { inStream }); // 调用Decompress方法,获得原始程序集的数据
if (sfdAssembly.ShowDialog() == DialogResult.OK)
{
sfdAssembly.OpenFile().Write(data, 0 , data.Length); // 把数据保存到指定文件,OK,完工!
}
MessageBox.Show( " 文件解压成功! " , " 恭喜 " , MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
MessageBox.Show( " 未找到正确的程序集! " , " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
else
{
MessageBox.Show( " 该文件没有程序集! " , " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, " 错误 " , MessageBoxButtons.OK, MessageBoxIcon.Hand);
}
}
下面是这个小程序的完整的代码,需要的童鞋可以下载参考一下。
点击下载
由此可见,如果仅仅想减小一下你的程序的体积,Sixxpack应该是个不错的工具,但是指望用它保护自己的代码,显然只能失望了。稍微懂点.net的,只要愿意花点时间琢磨,都能拿到你的原始程序。所以想保护自己代码,还是使用那些专业的加密混淆工具吧。不过感觉Sixxpack的思路确实很不错,有时间一定要好好看一下它具体的代码。
我最后用这个小程序得到了这个外挂的真正程序集。看了一下,忍不住破口大骂,原来是个骗局。百度搜了一下,果然骂声一片,竟然还真有不少童鞋上当了。不过这和本篇内容无关了,而且现在时间太晚了,等有时间一定要揭露他一下,也免得再有童鞋上当。