前言
最近闲暇之余在解CTf时,遇到了压缩包内crc32的问题(较短的明文可以通过爆破crc值推出明文内容)。大体浏览了一下网上常见的脚本,大多数是单线程穷举爆破,所以就在想着着手写一套多线程的分段爆破的代码出来。(其实在写这篇文章的时候的我已经想到了另外一种可以大范围缩短穷举值的方案下一篇再做说明,但是因为这个代码已经完成了,所以还是简单说一下 这个代码 中多线程分段处理的思路吧,也希望这个思路可以对别人有所帮助。)
新版基于crc加密方式逆向的分析的版本已经发布到 https://www.freebuf.com/sectool/319188.html了
ps:关于crc32的原理大家可以自行百度
一、概念确认
首先在开始讲之前 我们先确定几个需要用到的概念
1、明文字符集
也就是需要爆破的取值范围,假设取值范围为可见字符 1-9 那么对应10进制的就是49-57.
2、原始明文长度
指破解后的明文长度也就是原始明文的长度,例如原始明文为123456 那么明文长度为6.
4、穷举总数
其实就是值我们需要穷举的个数 例如范围0-9 原始明文为 6 那么总个数就是 10的6次方。
3、线程数(需要使用多少线程来跑,也就是需要)
也就是指定需要多少个新城来同时爆破,也就【穷举总数】需分成的段数。
确定好这些概念后,简单可以列出几个关系
每个线程需要处理的个数 = 穷举总数 / 线程个数 (有余数的情况下可以直接取天花板 或者在单独开个多余的线程处理)
二、分段核心思路设计
有了上面的概念,那么关键点来了,我的设计思路是设计一个基底,然后每一段线程都对基底进行加法运算从而在自己的范围内正常爆破。
为了更好理解,我们先假设取值范围为3-4(3和4l两个两个数) ,原始字符长度为3的情况,那么可能的范围是就是以下8种。
333,334,343,344,433,434,443,444;
我的思路是首先选择第一种可能性基,也就是333为基,后面的数全部由基通过加法获得。
ok接下来是更直观的表述:
首先 定义一个长度为3得到数组.
int tempStrO[3]=={‘3’,‘3’,‘3’};
其次就是比较关键的一点如何把步进值转换到每一个对应位,这里我使用了进制转换里面常见的短除法。具体说就是 3-4 的取值范围为 4-3+1=2, 也就是可以看作数据整体为一个2进制逢 2进一 的运算规则。
所以我又定义一个数组:
int tempAddStr[strSize]={0,0,0}
加 1 的情况
1短除2 将余数从尾部开始存入 tempAddStr;
tempAddStr 变为 {0,0,1};
然后循环 按角标相加(注意 一个是char 一个int)
{’3‘,’3‘‘,’3’}
+
{ 0, 0 , 1 }
=
{’3‘,’3‘‘,’4’}
加 2的情况
2短除2 将余数从尾部开始存入 tempAddStr;
然后循环 按角标相加(注意 一个是char 一个int)
{’3‘,’3‘‘,’3’}
+
{ 0, 1, 0 }
=
{’3‘,’4‘‘,’3’}
加 6的情况
6短除2 将余数从尾部开始存入 tempAddStr;
然后循环 按角标相加(注意 一个是char 一个int)
{’3‘,’3‘‘,’3’}
+
{ 1, 1 , 0 }
=
{’4‘,’4‘‘,’3’ }
如果取值范围更大的情况也是一样的 例如取值范围为32-128(10进制),明文长度为6的情况,
那么可以看作是128-32+1 进制也就是 逢97进1 ,如下简单看一下加100的情况。
加 100的情况(上下都为10进制)
100短除97 将余数从尾部开始存入 tempAddStr;
然后循环 按角标相加(注意 上下都为int)
{32,32,32,32,32,32}
+
{ 0 , 0 , 0,0 ,1 ,3 }
=
{32,32,32,32,33,35}
好的到这里基本上就可以确定核心的运算了 。下面先看一下短除的实现。
整套代码我最开始是使用c#写的 ,但是线程方面不是很理想又使用c写了一版
c#:
private static void calc_tempAddStr(ulong x, int index, int[] tempAddStr)
{
if (x < (ulong)stepInt)
{
tempAddStr[index] = (int)x;
return;
}
ulong div = x / (ulong)stepInt;
ulong rem = x % (ulong)stepInt;
tempAddStr[index] = (int)rem;
calc_tempAddStr(div, index - 1, tempAddStr);
}
改成c之后遇到了一个更麻烦的事情,就是基础类型长度不够,会出现后面的线程加的数字溢出后 变成处理前面线程 数据的情况,无奈引入了gmp库,因为要释放资源所以将递归算法改为了循环。
c:
`
void calc_tempAddStr(mpz_t _x, int index, int tempAddStr[]) {
mpz_t divStr;
mpz_init(divStr);
mpz_t rem;// = x % _stepInt;
mpz_init(rem);
mpz_pow_ui(divStr, _x, 1);
while (mpz_cmp(divStr, _stepInt) > 0){
index -= 1;
mpz_fdiv_r(rem, divStr, _stepInt);
mpz_fdiv_q(divStr, divStr, _stepInt);
tempAddStr[index] = mpz_get_ui(rem);
}
index -= 1;
tempAddStr[index] = mpz_get_ui(divStr);
mpz_clear(rem);
mpz_clear(divStr);
}
`
三、线程传参
上面确定以后,此处就变得很简单了
无非就是 计算 然后给每个线程分配开始结束就可以了。
C: 因为使用是GMP所以代码看着不是很简洁…
for (int t = 0; t < threadCount; t++) {
tid[t] = thread(Thread_crack, t);
}
void Thread_crack(unsigned long p) {
printf("线程 %ld 开始\n", p);
int tempStrO[strSize];
int tempAddStr[strSize];
for (int i = 0; i <= strSize; i++) {
tempStrO[i] = beginInt;
tempAddStr[i] = 0;
}
mpz_t _p;
mpz_init(_p);
mpz_set_ui(_p, p);
mpz_t _i;
mpz_init(_i);
mpz_set_ui(_i, 0);
mpz_mul(_i, _step, _p);
mpz_t _end;
mpz_init(_end);
mpz_add(_end, _step, _i);
while (mpz_cmp(_i, _end)<0){
//todo
mpz_add(_i, _i, _iStep);
}
mpz_clear(_p);
mpz_clear(_i);
mpz_clear(_end);
}
C# :
for (ulong t = 0; t < (ulong)threadCount; t++)
{
new Thread(new ParameterizedThreadStart(Thread_crack)).Start(t);
}
static void Thread_crack(object obj)
{
try
{
Console.WriteLine("线程===》" + ((ulong)obj + 1) + "已启动");
int[] tempStr = Enumerable.Repeat(beginInt, strSize).ToArray(); //记录数据
int[] tempAddStr;
for (ulong i = step * (ulong)obj; i < step + step * (ulong)obj; i++)
{
tempAddStr = Enumerable.Repeat(0, strSize).ToArray();
calc_tempAddStr(i, strSize - 1, tempAddStr);
if (checkSet.Contains(CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X")))
{
Console.WriteLine(addStr(tempStr, tempAddStr) + "---->" + CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X"));
using (StreamWriter fs = new StreamWriter(Application.StartupPath + "//result.txt", true))
{
fs.WriteLine(addStr(tempStr, tempAddStr) + "---->" + CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X"));
}
}
}
}
catch
{
}
}
四、c版源码
原本想只是用c实现的,最后引用gmp库的时候devc++实在是报错,最后不得已使用vs2013进行的配置,另外加入了GMP之后代码一点也不简洁了…
#include <math.h>
#include<thread>
#include <gmpxx.h>
#include <stdio.h>
#include <tchar.h>
using namespace std;
struct threadarg {
int x;
};
static long long checkSet[] = { 0xCC86365B, 0xBCEE7ED5, 0xCCCA7E74, 0x0972d361 }; //待爆破CRC
static int ckSize = sizeof(checkSet) / 8;
const static int strSize = 6;
const static int beginInt = 48;//32; //取值范围开始
const static int endInt = 90; //取值范围结束
const static int threadCount = 10;//线程数
static int stepInt = endInt - beginInt + 1;
static mpz_t _stepInt;
static mpz_t _strSize;// (strSize);
static mpz_t _threadCount;// (threadCount);
static mpz_t _step;//记录总变化数/线程数
static mpz_t _iStep;
void addStr(int tempStr1[], int tempAddStr1[], char * str) {
for (int i = 0; i < strSize; i++) {
str[i] = (char)(tempStr1[i] + tempAddStr1[i]);
}
}
void calc_tempAddStr(mpz_t _x, int index, int tempAddStr[]) {
mpz_t divStr;
mpz_init(divStr);
mpz_t rem;// = x % _stepInt;
mpz_init(rem);
mpz_pow_ui(divStr, _x, 1);
while (mpz_cmp(divStr, _stepInt) > 0){
index -= 1;
mpz_fdiv_r(rem, divStr, _stepInt);
mpz_fdiv_q(divStr, divStr, _stepInt);
tempAddStr[index] = mpz_get_ui(rem);
}
index -= 1;
tempAddStr[index] = mpz_get_ui(divStr);
mpz_clear(rem);
mpz_clear(divStr);
}
unsigned long GetCrc32Indirect(unsigned char * DataPtr, int DataLen, int CrcPoly) {
int dataIdx;
unsigned long crc32, byteTmp;
int bitLoop;
crc32 = 0xffffffff;
for (dataIdx = 0; dataIdx < DataLen; dataIdx++) {
byteTmp = (unsigned long)(DataPtr[dataIdx]) & 0x000000ff;
for (bitLoop = 0; bitLoop < 8; bitLoop++) {
if (((crc32 ^ byteTmp) & 1) != 0)
crc32 = ((crc32 >> 1) & 0x7fffffff) ^ CrcPoly;
else
crc32 = ((crc32 >> 1) & 0x7fffffff);
byteTmp = (byteTmp >> 1) & 0x7fffffff;
}
}
return crc32 ^ 0xffffffff;
}
bool checkContains(long long checkSet[], long key) {
for (int i = 0; i < ckSize; i++) {
if (checkSet[i] == key) {
return true;
}
}
return false;
}
void Thread_crack(unsigned long p) {
printf("线程 %ld 开始\n", p);
int tempStrO[strSize];
int tempAddStr[strSize];
for (int i = 0; i <= strSize; i++) {
tempStrO[i] = beginInt;
tempAddStr[i] = 0;
}
mpz_t _p;
mpz_init(_p);
mpz_set_ui(_p, p);
mpz_t _i;
mpz_init(_i);
mpz_set_ui(_i, 0);
mpz_mul(_i, _step, _p);
mpz_t _end;
mpz_init(_end);
mpz_add(_end, _step, _i);
while (mpz_cmp(_i, _end)<0){
calc_tempAddStr(_i, strSize, tempAddStr);
char strTemp[strSize + 1];
strTemp[strSize] = '\0';
addStr(tempStrO, tempAddStr, strTemp);
if (checkContains(checkSet, GetCrc32Indirect((unsigned char *)(strTemp), strSize, 0xedb88320))) {
printf("%d-%s---->%0x \n", (unsigned int)p, strTemp, GetCrc32Indirect((unsigned char *)(strTemp), strSize, 0xedb88320));
}
mpz_add(_i, _i, _iStep);
}
mpz_clear(_p);
mpz_clear(_i);
mpz_clear(_end);
}
int main() {
mpz_init(_stepInt);
mpz_set_ui(_stepInt, stepInt);
mpz_init(_strSize);
mpz_set_ui(_strSize,strSize);
mpz_init(_threadCount);
mpz_set_ui(_threadCount,threadCount);
mpz_init(_iStep);
mpz_set_ui(_iStep, 1);
mpz_init(_step);
mpz_pow_ui(_step, _stepInt, strSize);
mpz_cdiv_q(_step, _step, _threadCount);
thread tid[threadCount];
struct threadarg arg[threadCount];
for (int t = 0; t < threadCount; t++) {
tid[t] = thread(Thread_crack, t);
}
for (int t = 0; t < threadCount; t++) {
tid[t].join();
}
int data;
printf("结束!\n");
scanf("%d", &data);
mpz_clear(_stepInt);
mpz_clear(_strSize);
mpz_clear(_threadCount);
mpz_clear(_iStep);
mpz_clear(_step);
return 0;
}
五、c#版源码
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace crc爆破
{
class Program
{
static string[] checkSet = { "CC86365B", "BCEE7ED5", "CCCA7E74" }; //待爆破CRC
static int strSize = 6; //原始明文长度6
static int beginInt = 48; //取值范围开始 10进制
static int endInt = 90; //取值范围结束 10进制
static int stepInt = endInt - beginInt + 1; //计算长度
static int threadCount = 10; //线程个数
static ulong step = (ulong)Math.Pow((double)(endInt - beginInt + 1), (double)(strSize)) / (ulong)threadCount;//记录总变化数/线程数
static void Main(string[] args)
{
try
{
if (File.Exists(Application.StartupPath + "//config.ini"))
{
string[] Filelines = File.ReadAllLines(Application.StartupPath + "//config.ini");
checkSet = Filelines[0].Split('=')[1].Split(',');
strSize = int.Parse(Filelines[1].Split('=')[1].Trim());
beginInt = int.Parse(Filelines[2].Split('=')[1].Trim());
endInt = int.Parse(Filelines[3].Split('=')[1].Trim());
threadCount = int.Parse(Filelines[4].Split('=')[1].Trim());
for (ulong t = 0; t < (ulong)threadCount; t++)
{
new Thread(new ParameterizedThreadStart(Thread_crack)).Start(t);
}
}
}
catch
{
Console.WriteLine("配置文件有误!");
Console.ReadLine();
}
}
//核心处理函数。
static void Thread_crack(object obj)
{
try
{
Console.WriteLine("线程===》" + ((ulong)obj + 1) + "已启动");
int[] tempStr = Enumerable.Repeat(beginInt, strSize).ToArray(); //记录数据初始化第一种可能性。
int[] tempAddStr;
for (ulong i = step * (ulong)obj; i < step + step * (ulong)obj; i++)
{
tempAddStr = Enumerable.Repeat(0, strSize).ToArray();//tempAddStr初始化为0
calc_tempAddStr(i, strSize - 1, tempAddStr);
if (checkSet.Contains(CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X")))
{
Console.WriteLine(addStr(tempStr, tempAddStr) + "---->" + CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X"));
using (StreamWriter fs = new StreamWriter(Application.StartupPath + "//result.txt", true))
{
fs.WriteLine(addStr(tempStr, tempAddStr) + "---->" + CRC32.GetCRC32(addStr(tempStr, tempAddStr)).ToString("X"));
}
}
}
}
catch
{
}
}
//tempAddStr+tempStr 得到变化后的 str也就是待 crc计算的明文串
private static string addStr(int[] tempStr, int[] tempAddStr)
{
string str = "";
for (int i = 0; i < strSize; i++)
str += (char)(tempStr[i] + tempAddStr[i]);
return str;
}
//短除 取余递归填充 temAddStr
private static void calc_tempAddStr(ulong x, int index, int[] tempAddStr)
{
if (x < (ulong)stepInt)
{
tempAddStr[index] = (int)x;
return;
}
ulong div = x / (ulong)stepInt;
ulong rem = x % (ulong)stepInt;
tempAddStr[index] = (int)rem;
calc_tempAddStr(div, index - 1, tempAddStr);
}
}
}
五、…
其实在写这版代码的时候,我简单查看了一下crc的加密方式,目前已经有一个新的破解思路了,应该可以大范围缩短需要穷举的个数。
另外这版的运算也完全没有达到我的预期,仅仅作为记录吧,希望可以给他人带来一些灵感,如果大家有好的思路也欢迎讨论。
后面基于crc加密方式逆向实现的已经发布到https://blog.csdn.net/qq_35780686/article/details/122383823这里了