目录
01.前言
02.异或加密算法
03.汉字在计算机内的存储
04.算法思想
05.控制台下调试
06.Winform下的CrackMe
07.写在后头
这个加密算法是昨天在回家的船上突发奇想想到的,主要其实是因为想到了那一句话“不如我哋从头嚟过”
于是就回家屁颠屁颠的给家里的电脑下载了一个Visual Studio开始了实验
在实验的过程中发现其实并没有想的那么简单
说到底只是一个普通的异或加密算法
但是有那么一点点的改变吧
说不完美是因为,在实验的过程中发现如果按照正常输出可能会输出乱码
所以对输出位做了一点控制
好了话不多说,先简单说一下异或加密算法
异或加密是一种很简单的加密算法
异或的运算:
数学运算符为XOR(exclusive OR),在计算机中通常用"^"的符号表示
在二进制中:
1 XOR 0=1
0 XOR 1=1
1 XOR 1=0
0 XOR 0=0
不解释过多了,线性代数都学过
也总不能直接把别人的介绍直接复制过来发吧哈哈哈
解密就反过来求异或的结果就好了
异或运算是一个双向运算
关于这方面的介绍,在CSDN以及各大论坛搜索引擎等地方都有很多,大家可以自行查阅
在这里简单说一下个人通过查阅资料得到的理解,如有错误还请指正
其实不用理会那么多汉字输入码、汉字机内码、汉字交流码、汉字字形码
因为在计算机内都是以二进制的形式存储的
ASCII码以7位在计算机内表示(存储)英文字符,一个字符占一位
最高位置为0
而汉字在计算机内存储占两个字节,即十六位
最高位都置为1
不过重点在于,如果编码标准不同,那么汉字转换出来的二进制数据也会不同
就好比网络传输中客户端与服务器端用的编码不同,可能会出现数据传输丢失、错误等现象(没记错的话)
看完汉字章节大家应该都想到了
一般的异或加密以英文或者数字的转码作为秘钥
而今天要写的是要以中文的转码作为秘钥
但是中文一个占两位
那么就会出现一些比较奇怪的情况
比如,要么输入两个英文才能跟一个中文的二进制码做异或运算
要么只跟一个中文的二进制码的前半段做异或运算
这里为了避免麻烦,这次算法就用了第一种方案:两个英文字符和一个中文字符做异或运算
(这也是说觉得不完美的原因之一)
另外一点不完美的原因在于,有时候英文字符转换的二进制码与中文转换的二进制码做相与运算后,再进行解码,可能会得出乱码字符
所以我对运算后的结果做了控制
通过查表得知,小写英文字符二进制码均以010开头,大写英文字符二进制码011开头,所以在输出转码结果的时候,我将前两位控制输出为0和1(在接下来展示的代码可以看到)
主要过程是这样的:
设置一个秘钥-->用户输入用户名-->将用户名转为二进制-->将秘钥转换为二进制-->两个二进制数据做相与运算得到密码-->与用户输入的密码做相比,如果正确提示正确,如果错误则提示错误
先选择在控制台下调试了一番,代码如下,为了不影响美观,注解都写在代码的注释中了
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
//自己设置的秘钥
string key = "不如我们从头来过";
//一个存储秘钥二进制数据的字符串数组
//数组中一个数据代表一个8位的二进制数
//如: _key[1] = 01011010
string[] _key = new string[16];
//按照gb2313国标码编码转码
System.Text.Encoding str = System.Text.Encoding.GetEncoding("gb2312");
//转码
byte[] data = str.GetBytes(key);
StringBuilder result = new StringBuilder(8);
int i = 0;
//遍历byte数组,将其中的数据转为二进制数据
foreach (byte b in data)
{
result.Append(Convert.ToString(b, 2).PadLeft(8, ' '));
_key[i] = result.ToString();
i++;
result.Clear();
}
//输出查看结果
for (i = 0; i < 16; i++)
{
Console.WriteLine(_key[i]);
}
//这里偷懒了,没有写子函数,全部写在Main里了,等等的Winform代码是分开子函数了的
//所以输出一行分隔符
Console.WriteLine("-----------------------");
//用户名转二进制
string UserName = "testUser";
string[] _UserName = new string[16];
data = str.GetBytes(UserName);
i = 0;
foreach (byte b in data)
{
result.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
_UserName[i] = result.ToString();
i++;
result.Clear();
}
for (i = 0;i < 16; i++)
{
Console.WriteLine(_UserName[i]);
}
Console.WriteLine("-----------------------");
//根据用户名成密码
string[] pwd = new string[16];
for (i = 0; _UserName[i] != null; i++)
{
for (int j = 0; j < 8; j++)
{
if (j == 0)
{
result.Append('0');//控制最高位为0
}
else if (j == 1)
{
result.Append('1');//控制第二位为1
}
else
{
result.Append(_UserName[i][j] ^ _key[i][j]);//异或运算得到答案
}
}
pwd[i] = result.ToString();
result.Clear();
}
for (i = 0; pwd[i] != null; i++)
{
Console.WriteLine(pwd[i]);
}
Console.WriteLine("-----------------------");
//将生成的答案转回英文字符
string password = "";
for (i = 0; pwd[i] != null; i++)
{
System.Text.RegularExpressions.CaptureCollection cs = System.Text.RegularExpressions.Regex.Match(pwd[i], @"([01]{8})+").Groups[1].Captures;
byte[] data1 = new byte[cs.Count];
for (int j = 0; j < cs.Count; j++)
{
data1[j] = Convert.ToByte(cs[j].Value, 2);
}
password += Encoding.ASCII.GetString(data1, 0, data1.Length);
}
Console.WriteLine(password);
Console.ReadKey();
}
}
}
控制台结果:
先上界面吧
两个Label,一个Button,两个TextBox,没什么特别的
代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
string crackMeKey = "不如我们从头来过";
string[] BinaryKey = new string[16];
string[] BinaryUserName = new string[16];
string[] BinaryPassword = new string[16];
public Form1()
{
InitializeComponent();
}
//秘钥转二进制
private string[] KeyToBinary(string key)
{
string[] result = new string[16];
System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312");
byte[] data = chs.GetBytes(key);
StringBuilder stringBuilder = new StringBuilder(8);
int i = 0;
foreach (byte b in data)
{
stringBuilder.Append(Convert.ToString(b, 2).PadLeft(8, ' '));
result[i] = stringBuilder.ToString();
i++;
stringBuilder.Clear();
}
return result;
}
//用户名转二进制
private string[] UserNameToBinary(string UserName)
{
string[] result = new string[16];
System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312");
byte[] data = chs.GetBytes(UserName);
StringBuilder stringBuilder = new StringBuilder(8);
int i = 0;
foreach (byte b in data)
{
stringBuilder.Append(Convert.ToString(b, 2).PadLeft(8, '0'));
result[i] = stringBuilder.ToString();
i++;
stringBuilder.Clear();
}
return result;
}
//根据用户名与秘钥做异或运算生成二进制密码
private string[] GetBinaryPassword(string[] BinaryUserName,string[] BinaryKey)
{
string[] result = new string[16];
StringBuilder stringBuilder = new StringBuilder(8);
for (int i = 0; BinaryUserName[i] != null; i++)
{
for (int j = 0; j < 8; j++)
{
if (j == 0)
{
stringBuilder.Append('0');
}
else if (j == 1)
{
stringBuilder.Append('1');
}
else
{
stringBuilder.Append(BinaryUserName[i][j] ^ BinaryKey[i][j]);
}
}
result[i] = stringBuilder.ToString();
stringBuilder.Clear();
}
return result;
}
//生成最终字符串密码
private string GetFinalPassword(string[] BinaryPassword)
{
string result = "";
for (int i = 0; BinaryPassword[i] != null; i++)
{
System.Text.RegularExpressions.CaptureCollection cs = System.Text.RegularExpressions.Regex.Match(BinaryPassword[i], @"([01]{8})+").Groups[1].Captures;
byte[] data1 = new byte[cs.Count];
for (int j = 0; j < cs.Count; j++)
{
data1[j] = Convert.ToByte(cs[j].Value, 2);
}
result += Encoding.ASCII.GetString(data1, 0, data1.Length);
}
return result;
}
//确认按钮响应函数
private void btn_Confirm_Click(object sender, EventArgs e)
{
string _UserName = textBoxUsrName.Text.ToString();
string _UserPwd = textBoxPwd.Text.ToString();
BinaryKey = KeyToBinary(crackMeKey);
BinaryUserName = UserNameToBinary(_UserName);
BinaryPassword = GetBinaryPassword(BinaryUserName, BinaryKey);
string FinalPassword = GetFinalPassword(BinaryPassword);
//正确提示
if (_UserPwd.Equals(FinalPassword))
{
MessageBox.Show("好");
}
//错误提示
else
{
MessageBox.Show("不如我们从头来过");
}
}
}
}
效果:
好了,先说不足,算法是一个简单的算法,没什么特别的
在可执行程序的输入框我也没有做输入的限制
我的源码声明的数组很小,如果输入过长会引起数组越界的错误
不过当然是可以避免的,可以用正则表达式限制一下用户的输入
不过应该也没人会无聊到输十几位的用户名吧
没有做特别的完善,毕竟其实是自己写着玩的,也没有打算当作业交上去
而且实在是懒得给家里的电脑装什么IDA来做解析
实在是懒得给家里的电脑装什么加壳脱壳的软件来做拓展了
就先写到这里
不过其实“不如我们从头来过”这个梗挺有意思的,输入错了,也要从头来过
如果把这句话放在文章最后还可以“从头再看一次”
跟特别的人想说一句话可真难啊
还要查那么多资料,打这么久的代码,写一篇blog
“不如我哋从头嚟过”