针对文:http://bbs.pediy.com/showthread.php?t=110507
见到该帖后受不得诱惑,从仅有的28kx币中拿出2kx下载学习,的确很有意思,但也存在疑问,在此一并提出.
该帖中提及的代码叫Crack Me Hamishebahar,从GOOGLE中搜索应该来自伊朗的一个黑客和破解论坛,该作者已经给出了PASSWORD,但寻求求解它的方法.
下面我们对代码进行分析:
1)首先我们看到的程序是个loader,它要求您输入一个可多行的password,不如说是一个解码KEY,然后通过这个KEY解码程序目录下的一个加密DLL,并通过Assembly.Load()加载后执行.正确加载执行后会弹出一个伊朗文的提示框,否则英文提示PASSWORD错误.
2)下面我们看它的关键:如何验证KEY和解码加DLL:
/// <summary>
/// 这可是关键!
/// 1)先取头五个字节来验证 password(其实也就是解码KEY)
/// 2)文件最后五字节放着,正式文件开始五字节的解码结果,用来验证解码是否正确,不正确就不白费功夫了.
/// 3)接下来按照解码KEY长度,一段段取来解码并显示进度,最后把剩下部分解码但去掉最后的八个字节,(三个一字节计算因子,一个五字节验证用值)
/// 4)结果是个DLL,把它返回
/// </summary>
/// <param name="LoadFile"></param>
/// <param name="Password"></param>
/// <returns></returns>
public byte[] GetPasswordByte(string LoadFile, string Password)
{
List<byte> list = new List<byte>();
if (File.Exists(LoadFile))
{
byte[] buffer = new byte[Password.Length];
using (FileStream stream = new FileStream(LoadFile, FileMode.OpenOrCreate))
{
if (this.MainPrg != null)
{
this.MainPrg.Maximum = Convert.ToInt32(stream.Length);
this.MainPrg.Value = 0;
}
stream.Seek(stream.Length - 8L, SeekOrigin.Begin);
this.Rnd1 = Convert.ToByte(stream.ReadByte()); //保存那个DLL尾-8处的一个字节
stream.Seek(stream.Length - 7L, SeekOrigin.Begin);
this.Rnd2 = Convert.ToByte(stream.ReadByte()); //保存那个DLL尾-7处的一个字节
stream.Seek(stream.Length - 6L, SeekOrigin.Begin);
this.Rnd3 = Convert.ToByte(stream.ReadByte()); //保存那个DLL尾-6处的一个字节
byte[] buffer2 = new byte[5];
byte[] password = new byte[5];
stream.Seek(stream.Length - 5L, SeekOrigin.Begin);
stream.Read(buffer2, 0, 5); //取那个DLL尾-5处的五个字节
stream.Seek(0L, SeekOrigin.Begin);
stream.Read(password, 0, 5); //取DLL开头的五个字节
password = this.GetPassword(password.ToList<byte>(), Password); //取文件开头五字节解码
if (!this.Check(buffer2, password)) //和验证值比较看是否解码正确,以证明Password是对的
{
return null;
}
//下面开始真正的解码工作
stream.Seek(0L, SeekOrigin.Begin);
for (int i = 0; i < stream.Length; i++)
{
if ((i + Password.Length) >= (stream.Length - 8L))
{
int count = Convert.ToInt32(stream.Length) - i;
buffer = new byte[count];
stream.Seek((long) i, SeekOrigin.Begin);
stream.Read(buffer, 0, count);
buffer = this.GetPassword(buffer.ToList<byte>(), Password);
list.AddRange(this.SelectList(buffer.ToList<byte>(), 0, count - 8));
if (this.MainPrg != null)
{
this.MainPrg.Value = this.MainPrg.Maximum;
Application.DoEvents();
}
goto Label_024C;
}
stream.Seek((long) i, SeekOrigin.Begin);
stream.Read(buffer, 0, Password.Length);
buffer = this.GetPassword(buffer.ToList<byte>(), Password);
list.AddRange(this.SelectList(buffer.ToList<byte>(), 0, Password.Length));
if (this.MainPrg != null)
{
this.MainPrg.Value += Password.Length;
Application.DoEvents();
}
i += Password.Length - 1;
}
}
}
Label_024C:
return list.ToArray();
}
/// <summary>
/// 看看它在做什么?按Password的长度逐段调用解码方法解码,开始或最后不够Password的长度时按实际长度解码
/// </summary>
/// <param name="RET"></param>
/// <param name="Password"></param>
/// <returns></returns>
public byte[] GetPassword(List<byte> RET, string Password)
{
List<byte> list = new List<byte>();
for (int i = 0; i < RET.Count; i += Password.Length)//步长为password文本的长度,第一次时RET.Count为五,其后则取Password.Length为RET.Count,最后取文件剩下的长度
{
if ((i + Password.Length) > RET.Count)//如果当前完成位置再按步长增加超出要解码的字节数组长时
{
list.AddRange(this.GetPasswordByte(this.SelectList(RET, i, RET.Count - i), Password));//取剩下的部分解码
break;
}
list.AddRange(this.GetPasswordByte(this.SelectList(RET, i, Password.Length), Password).ToList<byte>());//按password文本的长度,一段段解码
}
return list.ToArray();
}
/// <summary>
/// 解码方法:
///
/// 对每个字节在0x00~0xff范围内进行轮式加减操作,如下:
/// (0~255轮操作)加214
/// (0~255轮操作)减199
/// 加第二个参数对应字节 (0~255轮操作)
/// 减第二人参数的长度 (0~255轮操作)
/// 加3 (0~255轮操作)
///
/// 其实是种按字节解码的方法,传入字节的多少没什么关系,只要字节数不大于Password长度,分多段解后合起来和一次解都一样
/// </summary>
/// <param name="MyByte"></param>
/// <param name="Password"></param>
/// <returns></returns>
private byte[] GetPasswordByte(List<byte> MyByte, string Password)
{
List<byte> list = MyByte;
for (int i = 0; i < list.Count; i++)
{
list[i] = this.GoPByte(this.Rnd3, list[i], true);
list[i] = this.GoPByte(this.Rnd2, list[i], false);
list[i] = this.GoPByte(Convert.ToByte(Password[i]), list[i], true);
list[i] = this.GoPByte(Password.Length, list[i], false);
list[i] = this.GoPByte(this.Rnd1, list[i], true);
}
return list.ToArray();
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)