某网站的分析,得到加密文件,并使用C#编写工具解密文件,重点讲解C#的编写
本帖最后由 cdj68765 于 2015-6-25 20:00 编辑说实话,我都不知道这篇帖子的内容该放在破解区还是编程语言区了,因为在我脑中的大纲里面,破解过程是一笔带过的,重点全是c#里面的事情。
先说下我要干什么吧
一个日本的模型网站,会提供上传者上传模型的展示,有些模型很精美,我想要下载到这些模型
为了防止被某些搜索网站搜索到,给自己带来不必要的麻烦,我在全程会规避网站的网址以及相关的关键词,能用图片替代的尽量用图片吧。
该网站的分析过程我就不再冗述,我就流水线带过
通过Fiddle这个工具分析网站加载了什么
从上往下看,第一个黑色框里面,加载了一个components.json,在这个json里面记载着
加密模型的下载地址,贴图的下载地址以及贴图的文件名,我在接下来写C#工具的时候,重点处理对象就是这个json
然后接下来在紫框里,加载了一个Unity3D的文件,模型解密过程,以及显示过程等等,我想都是通过这个U3D的webplayer打包文件实现的
然后青色框里面,以及红色框里面,就是加载了模型数据和贴图数据了,这没啥好讲的
然后我使用Procmon软件,查看模型数据被加载到了哪里
万幸,数据都被下载到了IE的缓存文件夹里面(我用IE浏览器打开的网站)
只不过比起在JSON里面显示的文件名,下载到本地的文件名都多了个,这个需要注意
我们先把要处理的重点文件,加密的模型文件拷贝出来吧
后缀的文件,嘛,对我们来说,后缀名是什么并没有意义,重点是里面的数据,尤其是头文件数据
很明显,都是没有意义的数据,我们先不管吧
我们把目光转向那个Unity3D文件,至少Unity3D后缀我们是知道明确意义的(Unity3D编写的Webplaye封包文件),
谈到Unity3D封包,自然就应该想到解包,用什么解包?谷歌一下的你们,估计很快就找到了disunity这个工具,233.我说的没错吧
但是,我这里要提供另外一个玩意,一个国外大神写的,网址是(http://aluigi.altervista.org/quickbms.htm),使用方法是先下载上面的主程序,然后根据你要处理的文件类型,到下面寻找对应的脚本文件,然后打开主程序,先加载脚本,然后加载代处理的文件后,选择保存目录就开始解包过程了,我使用这个工具加载Unity Web Player脚本,然后解包Unity3D文件,得到
这么些文件,然后猜测对模型的解密操作 等等都是在第一个dll,也就是Assembly-CSharp.dll里面执行的,标题很清楚,写着CSharp,也就是C#写的,我们用ILSpy对这个dll竟然反编译,得到了源码?
得到了很多cs的代码文件,那我们怎么知道,我想要找的方法在哪个代码文件里面呢
嘛,我使用的IDE是VS2013,于是我打开工程文件到VS2013,然后Ctrl+F,然后,方便吧,直接搜索我想处理对象的后缀,定位到了一个关键cs里面,
立马就看到了
叫做LoadFile方法名的代码段,我知道我找对地方了,然后就是分析代码,写工具了,你说简单吧
真是可喜可贺,楼主这么快就找到关键代码段了,但是很遗憾的是,楼主在上个星期六分析的时候,找到这个地方后,就没辙了,原因很简单,楼主并不会C#的编程,根本没学过
于是楼主我恶补了C#的编程知识后,然后才着手分析代码,使用C#编写工具
好了,经过周日一个晚上的恶补C#编程基础知识以后,我对C#有个大致的了解了,不就是和C差不多的语言么,套用套用就行了,我们先看这个加密代码段干了什么吧
public void LoadFile(byte[] file)///调用时候,载入的是byte数据
{
CGMLZMA cGMLZMA = new CGMLZMA(file.Length ^ 80128);///引用一个叫做CGMLZMA的子程序,对子程序输入的值是载入文件的数据大小按位与固定数值80128进行异或操作
using (MemoryStream memoryStream = new MemoryStream(file))///将文件数据存到名为memoryStream的内存流
{
using (MemoryStream memoryStream2 = new MemoryStream())///建立一个名为memoryStream2的空内存流用来存处理后的数据,也就是说,我最后只要把这个内存流里面的数据导出成文件就行了,后面的算法都可以不用管
{
while (true)///开始循环操作,看来除非遇到break,不然是不打算结束循环了
{
int num = (int)Math.Min(memoryStream.get_Length() - memoryStream.get_Position(), 4L);///定义一个整数,整数值的取值是调用数学方法里面的取最小值,取的是内存流大小减去当前内存流位置,与4进行取小操作
if (num <= 0)///当num取到的数值小于等于0就结束循环
{
break;
}
byte[] array = new byte;///建立一个大小为4的byte数组,
memoryStream.Read(array, 0, num);///从memoryStream的内存流读取4字节数据到array数组
if (num == 4)///这里没啥好讲的
{
uint num2 = BitConverter.ToUInt32(array, 0) ^ cGMLZMA.NextUInt32();\\将array数组里面的字节转换成整数值与应用的cGMLZMA类里面的NextUInt32方法进行异或操作,得到数据返回给新定义的整数变量num2
array = BitConverter.GetBytes(num2);///将整数变量num2里面的数值转换成字节传送给array
}
memoryStream2.Write(array, 0, num);///将array数组里面的数据写入到 memoryStream2内存流
}
memoryStream2.set_Position(0L);///将内存流memoryStream2的指针指向开头
}
return result;
}
public class CGMLZMA
{
private uint x;
private uint y;
private uint z;
private uint w;
public CGMLZMA(int seed)
{
this.x = (uint)seed;
this.y = 362436069u;
this.z = 521288629u;
this.w = 88675123u;
}
public uint NextUInt32()
{
uint num = this.x ^ this.x << 11;
this.x = this.y;
this.y = this.z;
this.z = this.w;
return this.w = (this.w ^ this.w >> 19 ^ (num ^ num >> 8));
}
});
至此我们已经知道代码对文件在数据层面上的操作了,我们需要做的,只是写一个读取方法,一个保存方法,然后调用这个方法就行了,期间甚至对源代码都可以不做任何修改
源代码里面,调用该方法,输入的参数是字节数组类型的,因此我们也应该使用字节数组读取文件,用什么方法呢,File.ReadAllBytes就行,C#实在是太人性化,打个file然后加个.,然后呢,你就随便挑方法就行了,不知道挑哪个就打个byte,然后蹦出这玩意给我看,简单易懂的英语,读取全部字节数据。
因此,开头我们就写个
static void Main(string[] args)
{
byte[] file = File.ReadAllBytes("C:\\XX.XX");
LoadFile(file);
}
}
完事,多少方便,然后结尾下一个方法用来导出内存流里面的数据到文件
FileStream fs = new FileStream("XX:\\XX.XX“, FileMode.OpenOrCreate);
BinaryWriter w = new BinaryWriter(fs);
w.Write(memoryStream2.ToArray());
fs.Close();
顺利导出,完毕,至此模型的解密工作全部完成,编译一下就可以食用了(误)
你们以为这样子就完了?如果仅仅只是这样的话就太没意思了,下面开始才是正题
上面的程序大家也看了,导入的时候,要手动填写文件的地址,导出文件地址也要手动填写,最重要的是,每次操作都要编译一次,烦不烦?于是,我着手开始编写完整的功能
在此,我会以一个刚接触C#一周不到的半吊子新手的角度,为大家讲解我写的C#里面,用到了哪些功能,是怎么实现这些功能的,(喂,你接触一个星期就来现学现卖了额,你确定你不是菜鸟一枚还在那班门弄斧?233)
文章的最后,我会贴出全部的代码,不过在此之前,我会一一慢慢讲解,反正估计没多少人看,我也就安心大胆的写了
凡事要有思路,编程也要有个流程,我写的这个工具,首先应该显示一个对话框用来让用户选择保存路径,然后获得文件方面,记不记得数据是保存在浏览器的缓存目录的?那我也去那里掏数据算了,然后还记不记得我上面那张json里面,数据的截图?
一个很明显是图片下载地址的东西,然后前面跟了个该图片文件所对应的文件名,我原本想的是,分析这个json,得到下载地址,然后使用下载的方法直接下载图片保存成对应的文件名,但是失败了,因为网站本身使用了自动秘钥,每15分钟生成一个自动秘钥导入到Cookie里面,如果秘钥不对将出现401,无权限访问的错误,本来我想写一个POST来突破这层验证的,但是想想,凭我才接触C#几天的功力,还是不要做这种吃力不讨好的事情了,还是分析json,然后从IE缓存得到文件比较方便。
综上所述,目前最重要的事情,就是先得到对应的json,然后分析json废话不多说,开始讲代码上面的事情吧
首先是显示一个对话框,让用户选择保存路径用的,用了以下代码
FolderBrowserDialog folderDlg = new FolderBrowserDialog();///引用一个FolderBrowserDialog方法,命名folderDlg
folderDlg.ShowDialog();///让命名为folderDlg的FolderBrowserDialog方法显示出来
好,文件保存地址的获得已经完毕了,以后想要调用该地址,只要folderDlg.SelectedPath就得到了
保存地址得到了,那么加载地址呢,我用了如下代码
string Cachefoler = Convert.ToString(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache)));
很长是吧,其实原来没这么长的,只不过我为了那啥,就把代码全部嵌套在一起了,其实看到这么长的代码也不用慌,从里面往外面读总没错,这里就是从一个叫做IE缓存的特殊目录得到地址路径,并把路径名转换成字符串给新定义的字符串变量Cachefoler,(C#)里面,充斥着各种类型的数据,就用 Convert.ToString()就是用来把括号内的数据转换成对应数据类型的,这里是ToString,所以就是把括号内的数据转换成字符串,当然这个方法并不是万能的,很多情况下并不适用。因此请多加注意。
两个地址得到了,我们想下,还差个什么,哦对,得到json文件,通过观察我发现,网站会从一个固定的网址获得该json文件
这个网址里面,唯一的变量就是红框里面的数值,
而,该变量就是
网址最后的页面标号,因此,我的思路是让用户输入要处理的网址,然后我用程序分析得到这个标号,然后通过这个标号获得json的下载地址,得到json后,再进行进一步操作
弹出窗口,让用户输入网址方面,我是弄了个单独的窗口项,设计成酱紫的
其中,我给确定按钮输入了以下代码private void button1_Click(object sender, EventArgs e)
{
try
{
this.Text = textBox1.Text;///从输入栏获得字符串
string[] downloadsArr = textBox1.Text.Split(Convert.ToChar("d"));///以字d为分割要素,分割获得的字符串
int.Parse(downloadsArr);///将分割得到的字符串数组最后的数据转变成整数(这里用到downloadsArr.Length来获得数组的大小,因为根据网址,我知道我想要得到的那个数字一定是在最后面的,但是.Length获得的数组大小是从1开始计数的,而数组本身却是从0开始计数的,因此这里downloadsArr.Length - 1就是指向数组最后一位,没有-1的话,不仅无法读取,而且还是还会出现越界错误。而这句话的意义就是与try配合着用,根据网址我知道d后面的一定会是一串数字,如果不是数字,那么转变成整数型就一定会出错,因此我用try用来判断这句话是否出错了,如果出错,那么必定是输入的网址有误。
this.td_num = downloadsArr;///将分割字符串得到的字符串数组最后一位的数据传给全局变量,方便主程序里用到该数据。
this.Close();///结束该窗体
}
catch(System.Exception)
{
MessageBox.Show("输入的网址有误","错误",MessageBoxButtons.OK,MessageBoxIcon.Error);
};
}
好,经过以上代码以后,我们就得到了页面编码了,存在td_num里
于是,接下来,我们怎么操作呢
首先回到主程序,然后在主程序的头,打一个using Json;我在这里打的这个Json是什么?其实是,我刚才编写的窗口项的命名空间
这个很重要,不在主函数里面using该命名空间的话,主函数根本找不到
Getjson fr = new Getjson();///引用引用窗体里面,类的名字给fr
fr.ShowDialog();///显示窗体
string num = fr.td_num;
从窗体的全局变量里面获得数值
然后我们才可以在主函数里面,写上面那段代码来调用刚才的窗体
紧接着,我调用了一个子程序,来从网站获得json到本地文件
private static string Getjson(string num)
{
string temp="";
try///用来判断接下来的操作有没有错误的,一旦出现错误,就可以判断是下载出问题了
{
WebClient client = new WebClient();引用WebClient
temp = Convert.ToString(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache)));///之前已经出现过了,我打算把获得的Json文件,存到IE的缓存目录里面去
client.DownloadFile("http://3d.nicovideo.jp/works/td" + num + "/components.json", temp + "\\components.json"); ///调用方法DownloadFile来下载文件到指定目录
}
catch(System.Exception)///要是上面出错就显示下面的对话框
{
MessageBox.Show("网络异常", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return temp + "\\components.json";///返回路径字符串
}
好,这样一来,我们从网站上面得到了需要的json了,接下来怎么操作呢首先吧,json是一种数据格式,有自己的数据格式规则,而我要做的,就是分析该文件里面的数据,得到自己想要的数据
怎么分析?自己根据json的规则写算法么?那太蠢了,在这里,我从网上找了个Newtonsoft.Json.dll,准备引用该dll里面的方法,帮我分析
解决方案里面添加引用没啥好讲的,其次需要在
引用该程序集
然后我在主函数里面,写了这么一段代码来调用json的分析方法
JsonReader reader = new JsonTextReader(new StringReader(new StreamReader(Getjson(urlnum), Encoding.GetEncoding("utf-8")).ReadToEnd()));
够长吧..其实原来没这么长的,纯属我无聊,把各种定义的变量取消,全部嵌套在一起了,还是从里往外读吧...首先是Getjson(urlnum),调用我上面写的子程序Getjson,输入的数值是从上面提到的窗口来的,然后经过该子程序后,返回的是json在本地的地址,也就是IE的缓存目录里面的地址,详情看上面的那个代码段,之后将该地址所指向的文件,以utf-8的格式写入数据流,然后使用 StringReader读取该数据流里面每一个字符数据,最后再调用JsonTextReader方法来分析所得到的json数据,至于怎么分析的,我并不知道,全部交给Newtonsoft.Json.dll完成了,分析得到的数据保存在reader里面,也就是说,之后我想调用json里面的数据,只要调用reader就行了
事不宜迟,让我们来看下,JsonTextReader方法将json数据分析成了什么样子了
while (reader.Read())///循环读取reader
{
Console.WriteLine(reader.Path + "\t" + reader.Value);///控制台显示reader地址和对应地址里面的值
}
Console.ReadLine();
我们可以很清晰的看到,json的数据被分门别类的处理好了,我们需要做的,只有从里面取自己需要的数据罢了,上面红框里面就是图片应该取的文件名,下面红框里面,就是对应的下载地址,但是我们并不需要那么长,我们只需要紫框里面那一段,因为保存到本地的文件名就是紫框里面的文件名,等会儿我们从本地搜索当然也需要文件名对应喽
取法也很简单,用Split方法把reader.Value按照字符“/”分成几段,然后分割后得到的数组最后一位里面,就是需要的字符串了,在此不再冗述
经过以上方法,我们从json里面,得到了保存到IE缓存里面的文件名,那么,接下来,该怎么使用这些文件名找到对应的文件呢
于是我想了一个方法,用了以下代码
string[] tree = Directory.GetDirectories(path, "*.*", SearchOption.AllDirectories);///获得指定目录path下面包括字目录下面所有的目录路径
string[] directories = new string;根据上面tree数组的大小再申请一个比tree数组大1的数组
for (int i = 0; i < tree.Length - 1; i++)循环遍历tree数组,由于length是从1开始计数的,所以这里-1,为了和数组的计数方式对应
{
directories = tree;将数组tree里面的数据,全部传给数组directories
}
directories = path;在数组directories的最后再把path添加进数组最后一位,做这一步操作是因为第一行代码,获得的所有目录是不包括自己本身的,所以要把目录本身给手动添加进去,本来这里应该用动态数组Array的,这样只要add(),就行了,根本不需要循环遍历什么的,但是没办法,学艺不精,对动态数组的操作,到现在还不熟练,以防万一还是用自己最熟悉的普通数组操作了 foreach (string dir in directories)///foreach循环遍历数组 directories里面的数据
{
string[] fileNames = Directory.GetFiles(dir, "*.png");///从指定目录下面搜索定义类型的文件,并将结果返回给字符串数组fileNames
}
之后只要将fileNames数组里面的数据和json里面获得数据进行对比就行了,很简单吧
写这个遍历目录算法的时候,我在网上查找了不少的资料,发现大家几乎都用的递归算法来实现遍历的,虽然并不是不可以,但是要自己写一个递归的子程序实在麻烦,而后,我在百无聊赖的时候,在那边一个个方法的寻找,于是就找到这个方法了,立马调过来使用,发现还是很方便的。不过,如果指定C盘的话,这个算法会把整个盘符都遍历一遍,然后逐个寻找对应的文件倒是真的,慎用啊(按照我的思路,是两个表格在对比,简单地说,是左边表格里面一行数据,拿右边表格的全部数据进行对比寻找,右边找完以后,左边表格再换一行继续跟右边表格全部数据进行对比,幸好我这边对比的数据量很小,要是一旦数据量大起来,程序崩溃是迟早的事情,毕竟效率太低)
System.IO.File.Copy(fileNames, path + @"\" + xx.jpg,true);找到文件以后就没啥好说的了,从找到的位置复制到指定位置就完毕了(注意要在后面加上true或者false,代表是否需要覆盖,不加的话遇到重复复制的情况,会崩溃)
好了,程序大功能上的实现就讲到这里,还有很多细节上面的东西我就不讲了,一切我都会贴在下面的代码里面,有细节上面的不懂直接回复我就行
namespace Netdownload
{
class Program
{
public bool firsttimecheckModel = true;
public bool checkModel = false;
static void Main(string[] args)
{
EmbeddedAssembly.Load("readcomponents.Newtonsoft.Json.dll", "Newtonsoft.Json.dll");
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Program Bool = new Program();
MessageBox.Show("请选择模型保存目录", "目录选择", MessageBoxButtons.OK, MessageBoxIcon.Information);
FolderBrowserDialog folderDlg = new FolderBrowserDialog();
folderDlg.ShowDialog();
string Cachefoler = Convert.ToString(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache)));
Getjson fr = new Getjson();
fr.ShowDialog();
string num = fr.td_num;
if (Dealwith(folderDlg.SelectedPath, Cachefoler, num) == false)
{
MessageBox.Show("请点击网页中间的播放按钮加载模型,当页面中的模型能正常显示后再进行下一步", "要求", MessageBoxButtons.OK, MessageBoxIcon.Question);
System.Diagnostics.Process.Start("iexplore.exe", @"3d.nicovideo.jp/externals/embedded?id=td" + num);
MessageBox.Show("是否已经加载完模型?", "确认", MessageBoxButtons.OK, MessageBoxIcon.Question);
if (Dealwith(folderDlg.SelectedPath, Cachefoler, num) == false)
{
if (MessageBox.Show("无法获得模型数据,是否手动指定缓存路径?", "错误", MessageBoxButtons.YesNo, MessageBoxIcon.Error) == DialogResult.Yes)
{
FolderBrowserDialog Cachepath = new FolderBrowserDialog();
Cachepath.ShowDialog();
if (Dealwith(folderDlg.SelectedPath, Cachepath.SelectedPath, num)==false)
{
MessageBox.Show("模型解析失败");
}
}else { MessageBox.Show("模型解析失败"); }
}
}
}
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return EmbeddedAssembly.Get(args.Name);
}
private static string Getjson(string num)
{
string temp="";
try
{
WebClient client = new WebClient();
temp = Convert.ToString(new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache)));//json保存目录
client.DownloadFile("http://3d.nicovideo.jp/works/td" + num + "/components.json", temp + "\\components.json");
}
catch(System.Exception)
{
MessageBox.Show("网络异常", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
return temp + "\\components.json";
}
private static bool Dealwith(string path, string getfoler, string urlnum)
{
string oldpath = path;
string[,] modelinfo = new string;
string[] MODEL = new string;
string[] Texname = new string;
string[] Urlname = new string;
string[] Urlest = new string;
int count = 0;
bool restex = false;
string dispicurl = "";
Getjson fr = new Getjson();
string tex = fr.Text;
if (urlnum == null)
{
System.Environment.Exit(0);
}
JsonReader reader = new JsonTextReader(new StringReader(new StreamReader(Getjson(urlnum), Encoding.GetEncoding("utf-8")).ReadToEnd()));
while (reader.Read())
{
Console.WriteLine(reader.Path + "\t" + reader.Value);
string stra = (string)reader.Path;
string getname = Convert.ToString(reader.Value);
string[] sArr = stra.Split(new char[] { '.' });
if (restex)
{
foreach (string s in sArr)
{
if (s.Equals("url"))
{
if (getname != "url")
{
string[] urlname = getname.Split(new char[] { '/' });
string[] texname = urlname.Split(new char[] { '.' });
Urlname = texname + "";
Urlest = texname;
count++;
restex = false;
}
}
}
}
foreach (string s in sArr)
{
if (s.Equals("name"))
{
if (getname != "name")
{
Texname = getname;
restex = true;
}
}
}
if (stra == "work.title")
{
if (getname != "title")
{
MODEL = getname;
DirectoryInfo dir = new DirectoryInfo(path);
dir.CreateSubdirectory(getname);
path = path + @"\" + getname;
WebClient client = new WebClient();
client.DownloadFile(dispicurl, path + @"\" + @"folder.jpg");
}
}
if (stra == "components.url")
{
if (getname != "url")
{
string[] urlname = getname.Split(new char[] { '/' });
string[] name = urlname.Split(new char[] { '.' });
MODEL = name + "";
MODEL = name;
}
}
if (stra == "components.display_url")
{
if (getname != "display_url")
{
dispicurl = getname;
}
}
}
Console.ReadLine();
for (int i = 0; i < count; i++)
{
modelinfo = Texname;
modelinfo = Urlname;
modelinfo = Urlest;
}
string[] tree = Directory.GetDirectories(getfoler, "*.*", SearchOption.AllDirectories);
string[] directories = new string;
for (int i = 0; i < tree.Length - 1; i++)
{
directories = tree;
}
directories = getfoler;
foreach (string dir in directories)
{
string[] fileNames = Directory.GetFiles(dir, "*.*");
for (int i = 0; i < fileNames.Length; i++)
{
string[] urlname = fileNames.Split(Convert.ToChar(@"\"));
for (int j = 0; j < count; j++)
{
if (urlname == modelinfo + "." + modelinfo)
{
bool check01 = true;
foreach (string s in Directory.GetFiles(path))
{
if (s.Equals(path + @"\" + modelinfo))
{
check01 = false;
}
}
if (check01)
{
System.IO.File.Copy(fileNames, path + @"\" + modelinfo,true);
}
}
}
}
}
foreach (string dir in directories)
{
string modelpath = "";
string[] ModelNames = Directory.GetFiles(dir, MODEL + ".*");
bool Check = false;
foreach (string a in ModelNames)
{
if (a != "")
{
modelpath = a;
Check = true;
}
}
if (Check)
{
string extsion = System.IO.Path.GetExtension(modelpath);
int timeout = 0;
string saveextsion = "dat";
switch (extsion)//这里我讲一下,不同的文件对应不同的种子(也就是相当于特定的秘钥),我找到了所有加密文件的种子数据,然后根据加载文件后缀获得对应的种子文件以给解密算法用,当然为了防止被搜索网站搜索到,这里的参数我都改了。
{
case ".p": timeout = 3; saveextsion = "m"; break;
case ".x": timeout = 8; saveextsion = "px"; break;
case ".m": timeout = 2; saveextsion = "o"; break;
case ".px": timeout = 1; saveextsion = "Pd"; break;
case ".nc": timeout = 2; saveextsion = "c"; break;
case ".pm": timeout = 5; saveextsion = "p"; break;
}
byte[] file = File.ReadAllBytes(modelpath);
CGMLZMA cGMLZMA = new CGMLZMA(file.Length ^ timeout);
using (MemoryStream memoryStream = new MemoryStream(file))
{
using (MemoryStream memoryStream2 = new MemoryStream())
{
while (true)
{
int num = (int)Math.Min(memoryStream.Length - memoryStream.Position, 4L);
if (num <= 0)
{
break;
}
byte[] array = new byte;
memoryStream.Read(array, 0, num);
if (num == 4)
{
uint num2 = BitConverter.ToUInt32(array, 0) ^ cGMLZMA.NextUInt32();
array = BitConverter.GetBytes(num2);
}
memoryStream2.Write(array, 0, num);
}
try
{
memoryStream2.Position = 0L;
string filename = path + @"\" + MODEL + "." + saveextsion;
FileStream fs = new FileStream(filename, FileMode.OpenOrCreate);
BinaryWriter w = new BinaryWriter(fs);
w.Write(memoryStream2.ToArray());
fs.Close();
}
catch(System.Exception)
{
memoryStream2.Position = 0L;
string filename = oldpath + @"\" + MODEL + "." + saveextsion;
FileStream fs = new FileStream(filename, FileMode.OpenOrCreate);
BinaryWriter w = new BinaryWriter(fs);
w.Write(memoryStream2.ToArray());
fs.Close();
}
MessageBox.Show("模型解析完毕");
return true;
}
}
}
}
return false;//只要返回值不是void的子程序的,在子程序的最后必须要有一个return,而且,必须要在末尾处有一个
}
}
}
于是我们编译一下吧
编译通过,得到了
一个可执行文件,还有个我应用的dll文件,额,我这么小的一个工具程序,为何还要带一个dll才能运行啊,难道就不能把这个dll封装到主程序里面去么
于是我在搜索的时候,搜索到了一个方法(http://www.codeproject.com/Articles/528178/Load-DLL-From-Embedded-Resource),按照他的方法来讲,我应该这么做
首先我应该在引用里面,把这货的属性,嵌入互操作类型改成true(虽然是嵌入到主程序里面,但还是要引入的)
然后把这个dll拖到解决里面,对,拖进去就行,也可以右键,添加现有项,不用指定放到什么目录里面,这不重要
记得把属性里面的生成操作改成嵌入到资源就行了
然后上面那个网址里面的作者写了个代码项目,不用管把这货也拖到解决方案里面去(记得改下后缀,改成cs后缀)
然后回到主程序里面,
在主程序键入以下代码
static void Main(string[] args)
{
EmbeddedAssembly.Load("XX.Newtonsoft.Json.dll", "Newtonsoft.Json.dll");
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
.............;//这里开始就可以写你自己的代码了
}
static System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
return EmbeddedAssembly.Get(args.Name);
}
上面没啥特别需要讲的,有个地方值得注意一下就行,EmbeddedAssembly.Load("XX.Newtonsoft.Json.dll", "Newtonsoft.Json.dll");,这里调用的是那个网址里面作者写的子程序,子程序需要输入两个参数,后面那个是你引入dll全名这没问题,但前面那个是你引入dll的地址,这个地址是,你这个工程所在文件夹的名字.你引用dll的名字,在我这里就是
”XX.Newtonsoft.Json.dll“,因为这个很重要所以我重点讲一下
编译一下,果然dll被嵌入到exe里面了,而且exe也能正常运行(别把所有的dll都嵌入到exe里面哦,不然打开你这个巨大的exe对电脑来讲会是一件很费事的事情)
好了,耗时一周的分析编写工作都结束了,C#果然是易学易用的语言啊(PS,很容易上瘾啊 有木有),我这种对编程一窍不通的人,也只要看下语法规则就能随便写小工具了
不过我写这篇文章的时候头脑里比较乱,不知道该写什么,不该写什么,所以先这样吧,以后我再慢慢改就是
最后希望大家能从一个接触C#一周不到的小白编写的代码当中,学到有用的玩意,233
{:301_1009:} 为啥不能给你评分..今天还有分呀..怪事.
推荐个C#学习视屏
http://www.52pojie.cn/thread-292090-1-1.html 本帖最后由 cdj68765 于 2015-7-6 17:07 编辑
realzoc 发表于 2015-7-5 10:31
LZMA是压缩算法的名称..
说实话 我看不出这段解密过程和
LZMA算法有何关系,而且解密算法使用了几个固定的秘钥,而且说实话,这个解密过程就是各种亦或,不涉及压缩过程,所以也就是文字里面出现了LZMA,实际不搭边
不明觉厉 nico都不放过#(滑稽) 取个屌点的名字就更好了。:lol Hmily 发表于 2015-6-22 15:15
取个屌点的名字就更好了。
不不,我写这篇文章 就是为了低调低调再低调,万一被日本那边发现就233了 {:1_915:} 去围观了下 nico,果然 233 跟二次元密不可分 向楼主学习。。。
mcevilrock 发表于 2015-6-22 23:29
为啥不能给你评分..今天还有分呀..怪事.
推荐个C#学习视屏
http://www.52pojie.cn/thread ...
谢谢分享,有空我一定去看 很详细的教程帖。学习了,谢谢楼主。