能力值:
( LV3,RANK:20 )
|
-
-
2 楼
不懂,我的理解是,假设你一个软件,登陆密码是123456,不论你如何加密,但是你下次输入123456的时候都必须能够登陆上去。只要有了这点,人家一直穷举,只要穷举到了123456还是可以登陆进去,你的加密就算在复杂,也失去了意义。至于你说的硬盘id和随机数之类的,这些还是可以伪造的,硬盘id可以hook,随机数可以int3 hook单字节指令以后模拟运行,也可以hook住random函数。
|
能力值:
( LV4,RANK:40 )
|
-
-
3 楼
密码字符多了,穷举需要近乎无穷大的时间,所以穷举攻击失效,本帖的方法就是讨论如何输入有限的一点点密码,而实际使用大量的数组做用户密码。仔细看看道理很简单的。也不需要伪造什么东西。
|
能力值:
( LV2,RANK:10 )
|
-
-
4 楼
现有的多数都比你的扯淡话完善的多。 继续围观装疯卖傻。
|
能力值:
( LV4,RANK:40 )
|
-
-
5 楼
我说的方法不能用吗?什么叫装疯卖傻?你要是理解不了我可以说得更详细些。 例如得到几个用户输入的密码字符,当然这些都是数据了,用这几个数算出几个参数不难吧,然后算出随机函数的种子值,以此做种子取例如1000个随机函数的值放到字节数组里,这也不难吧,我们就用1000个数据为基础做出用户密码,但实际上我们用不了那么多的数据作为用户密码,刚才不是算出了几个参数吗?随便取一个算出个大于1000的数,用此数除以1000得到一个余数N,原来的数组是
f[0]
,
f[1],...f[999],现在取用f[N]及其后面的几个数据作为加密用的用户密码即可。 为什么这样做,主要为了克制穷举攻击而已,诸位有什么好办法请不吝赐教。
|
能力值:
( LV10,RANK:163 )
|
-
-
6 楼
按网上大多数人做法是 MD5/SHA 加几次密码再发服务器, 服务器判断对错。 防穷举可以像银行一样,错误几次后禁止登陆。
|
能力值:
( LV4,RANK:40 )
|
-
-
7 楼
错误几次后禁止登陆,确实是个办法。但必须考虑最坏的情况,即窃密方能够使用穷举攻击,也能有相应的软件,在此条件下能抵抗的住才能实现安全加密。
|
能力值:
( LV4,RANK:40 )
|
-
-
8 楼
使用长密码对分组密码加密用处不大,因为分组密码密钥就那几位,找到关键部位穷举攻击就不错。 但对于流密码加密使用长密码就很厉害了,因为密码长度没有约束。而穷举长密码就是个梦。 可以这样做:设定一个长密码作为默认密码,得到用户密码后,对默认密码进行改造,得到加密使用的密码。如果加密无需那么多密码,可以拣选其中部分使用。
|
能力值:
( LV3,RANK:30 )
|
-
-
9 楼
额,楼主我想问一下,你这个长密码也是依据用户提供的短密码生成的吧
|
能力值:
( LV3,RANK:30 )
|
-
-
10 楼
楼主,还记得你写的功能强大的自动密码文件加密软件吗?那个帖子不能回复就在此回复一下吧。提前说一下,我是个初学者。我粗略的分析了一下,你的算法加密强度非常低,所有自动生成的密钥都是基于GetTickCount()返回得32位值,无论自动密码和用户密码长度多少,破解最多需要尝试 pow(2,32)*1000*20 次就能得到结果。(其实根本不需要这么多次,因为GetTickCount()返回开机到现在经过的毫秒数,不会太大) 上式的1000是最大自动密钥长度(字节为单位),20是最长用户密码长度(字节单位)。 所谓不可破解就是假定破解者不知道算法和被加密文件格式,现实中需要严格保护加密算法的情况,使用的加密算法都是特供的,你我都没有能力开发。 下面提供我逆出来的加解密算法,并提供解密pe格式文件的简单例子。 ps:我在伪代码里发现了很多类似 (a ^ (a ^ b)) 的异或代码,不知道是反编译器的原因还是楼主真的不知道
(a ^ (a ^ b)) == b ps:一切避开数论讨论加密原理的行为都是耍流氓 #include <cstdio>
#include <ctime>
#include <Windows.h>
#include <algorithm>
#pragma warning(disable:28159)
#pragma warning(disable:4996)
DWORD pKeySink[623];
DWORD dwKeyLengthInDword = 623;
DWORD dwGenerateKeyCount;
BYTE cbDataSink[1000];
bool cbKeyGenerated;
void GenerateKeySinkWithRandom(_In_ DWORD Random) {
DWORD dwCurrentValue;
dwGenerateKeyCount = 0;
cbKeyGenerated = true;
pKeySink[0] = Random;
for (DWORD index = 0; index < 622; ++index) {
dwCurrentValue = pKeySink[index];
pKeySink[index + 1] = index + 0x6C078965 * (dwCurrentValue ^ (dwCurrentValue >> 30));
}
}
void updateKeySink() {
bool v4; // zf
for (auto i = 0; i < dwKeyLengthInDword; ++i) {
v4 = (LOBYTE(pKeySink[i + 1] % 624) & 1) == 0;
pKeySink[i] = ((pKeySink[(i + 1) % 624] & 0x7fffffffu) >> 1) ^ pKeySink[i % 624];
if (!v4)
pKeySink[i] ^= 0x9908B0DF;
}
return;
}
DWORD GenerateRandomInKeySink() {
int v1; // eax
DWORD v2; // ecx
DWORD v3; // edx
DWORD result; // eax
if (!cbKeyGenerated) GenerateKeySinkWithRandom(time(nullptr));
v1 = dwGenerateKeyCount;
if (!dwGenerateKeyCount)
{
updateKeySink();
v1 = dwGenerateKeyCount;
}
v2 = ((((pKeySink[v1] >> 11) ^ pKeySink[v1]) & 0xFF3A58AD) << 7) ^ (pKeySink[v1] >> 11) ^ pKeySink[v1];
v3 = (v1 + 1) % 0x270u;
result = ((v2 & 0xFFFFDF8C) << 15) ^ v2 ^ ((((v2 & 0xFFFFDF8C) << 15) ^ v2) >> 18);
dwGenerateKeyCount = v3;
return result;
}
__forceinline BOOLEAN NTAPI RtlIsValidImageHeader(_In_ LPCVOID lpBufferToImageHeader) {
auto dos = PIMAGE_DOS_HEADER(lpBufferToImageHeader);
return (dos->e_magic == IMAGE_DOS_SIGNATURE) &&
(dos->e_lfanew >= sizeof(IMAGE_DOS_HEADER)) &&
(dos->e_lfanew < 0x400) &&
(PIMAGE_NT_HEADERS(SIZE_T(lpBufferToImageHeader) + dos->e_lfanew)->Signature == IMAGE_NT_SIGNATURE);
}
VOID NTAPI RtlUpdateBuffer(
_In_ DWORD dwKey,
_In_ BOOLEAN Encrypt,
_In_ DWORD dwUserKeyLength,
_In_ DWORD dwAutoKeyLength,
_In_reads_bytes_(dwCiphertextLength) LPCVOID lpCiphertext,
_In_ DWORD dwCiphertextLength,
_Out_writes_bytes_all_(dwCiphertextLength) LPVOID lpResult,
_Out_writes_opt_(dwUserKeyLength) LPDWORD lpKey) {
LPDWORD pAutoKey = new DWORD[1ull + dwAutoKeyLength];
DWORD dwTemp = 0;
RtlMoveMemory(lpResult, lpCiphertext, dwCiphertextLength);
GenerateKeySinkWithRandom(dwKey);
for (auto i = 0; i < dwUserKeyLength; ++i)
cbDataSink[i] = GenerateRandomInKeySink();
for (auto i = 10; i; --i)
for (auto j = 0; j < dwUserKeyLength; ++j)
std::swap(cbDataSink[j], cbDataSink[(j + GenerateRandomInKeySink()) % dwUserKeyLength]);
cbDataSink[dwUserKeyLength] = 0;
if (dwUserKeyLength > 0)
for (auto i = 0; i < dwUserKeyLength; ++i)
dwTemp += cbDataSink[i];
GenerateKeySinkWithRandom(dwTemp);
if (dwAutoKeyLength > 0) {
for (auto i = 0; i < dwAutoKeyLength; ++i)
pAutoKey[i] = GenerateRandomInKeySink();
for (auto i = 0; i < dwAutoKeyLength; ++i)
std::swap(pAutoKey[i], pAutoKey[(i + GenerateRandomInKeySink()) % dwAutoKeyLength]);
}
GenerateKeySinkWithRandom(dwTemp * dwTemp);
if (dwCiphertextLength > 0) {
dwTemp = dwCiphertextLength % dwAutoKeyLength;
for (auto i = 0; i < dwCiphertextLength; ++i) {
if (i == dwTemp) GenerateKeySinkWithRandom(pAutoKey[1]);
LPBYTE(lpResult)[i] += (Encrypt ? 1 : -1) * GenerateRandomInKeySink();
}
}
if (lpKey) {
for (auto i = 0; i < dwUserKeyLength; ++i)
lpKey[i] = cbDataSink[i];
}
delete[]pAutoKey;
}
#define RtlEncryptBuffer(_dwKey_, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)\
RtlUpdateBuffer(_dwKey_, TRUE, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)
#define RtlDecryptBuffer(_dwKey_, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)\
RtlUpdateBuffer(_dwKey_, FALSE, _dwUserKeyLength_, _dwAutoKeyLength, _lpCiphertext_, _dwCiphertextLength_, _lpResult_, _lpKey_)
BOOLEAN NTAPI RtlCrackPeFileEncryption(
_Out_writes_bytes_(21*2) LPSTR lpUserPassword,
_In_opt_ DWORD dwBeginRandom,
_In_ LPCVOID lpBufferToCrack,
_In_ DWORD dwBufferSize) {
lpUserPassword[0] = '\0';
BOOLEAN result = FALSE;
LPVOID lpData = nullptr;
const auto Length = sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS32);
auto BeginRandom = dwBeginRandom;
DWORD dwUserKey[2];
if (dwBufferSize < Length)
return FALSE;
lpData = new char[dwBufferSize];
while (BeginRandom & 0xffffffff) {
RtlDecryptBuffer(BeginRandom, 2, 567, lpBufferToCrack, dwBufferSize, lpData, LPDWORD(&dwUserKey));
if (RtlIsValidImageHeader(lpData)) {
//found
result = TRUE;
sprintf(lpUserPassword, "%02X%02X", BYTE(dwUserKey[0]), BYTE(dwUserKey[1]));
break;
}
--BeginRandom;
}
return result;
}
int main() {
//BYTE test[4] = { 0x11,0x22,0x33,0x44 };
//PDWORD key = nullptr;
//calc(test, 4, key, &key, 2, 567);
//calc(test, 4, key, nullptr, 2, 567);
typedef struct _PE_HEADERS{
IMAGE_DOS_HEADER dos;
IMAGE_NT_HEADERS32 nt;
_PE_HEADERS() {
dos.e_magic = IMAGE_DOS_SIGNATURE;
dos.e_lfanew = sizeof(dos);
nt.Signature = IMAGE_NT_SIGNATURE;
}
}PE_HEADERS,*PPE_HEADERS;
auto pImage = new PE_HEADERS;
DWORD key[2];
char szKey[42];
RtlEncryptBuffer(GetTickCount(), 2, 567, pImage, sizeof(PE_HEADERS), pImage, LPDWORD(&key));
if (!RtlCrackPeFileEncryption(szKey, GetTickCount(), pImage, sizeof(PE_HEADERS))) {
//failed
abort();
}
//check key[] and szKey
//success
delete pImage;
return 0;
}
|
能力值:
( LV4,RANK:45 )
|
-
-
11 楼
楼上说的对,如果要证明你的密码强度大一定要给出严谨的数学证明
|
能力值:
( LV6,RANK:90 )
|
-
-
12 楼
有种民科既视感
|
能力值:
( LV4,RANK:40 )
|
-
-
13 楼
@Boring勇哥
自动密码没有位数的限制,你那个20位是怎么来的?想让程序生成多少位字节密码是使用者自己设定的。
由于密码是程序随机生成的,所以加密强度极高,我是自夸了。这些密码信息不是保存在密文里(像一般加盐技术那样)而是由用户自行保存,所以相当安全,破解者想要得到比登天还难。 @Lightal 业余爱好,数学功底很差。 重申长密码的使用: 程序内部有一个较长的固定数组F,而
用户输入的密码
到达以后,程序将首先使用
用户输入的密码进行计算并用数据调整前面那个数组F,使其发生彻底改变生成F1,程序将使用这些改变后的数组F1作为用户密码来加密明文等。
|
能力值:
( LV3,RANK:30 )
|
-
-
14 楼
截图为证。即使没有这些限制,复杂度也只是线性增长,并非指数增长,因此安全性不会提高太多。
|
能力值:
( LV3,RANK:30 )
|
-
-
15 楼
只要程序第一次调用的GetTickCount()或者time()返回值确定了,后面所有的随机数数组的内容都确定了。
|
能力值:
( LV4,RANK:40 )
|
-
-
16 楼
确定了又如何,能得到数据才是真章,否则还是0?
|
能力值:
( LV3,RANK:30 )
|
-
-
17 楼
?????那不是你加密用的自动生成的密钥?有了密钥不就能解出明文了?看来是我水平太低,无法理解你的高级算法,楼主加油吧
|
能力值:
( LV4,RANK:40 )
|
-
-
18 楼
您需要将自动密码的那个程序了解清楚,那个程序是这样工作的,你用它加密文件时: 给它你要加密的文件,它将自动生成用户密码(密码位数是事先设置的),它用这些用户密码完成加密,并给你显示用户密码让你保存,以便解密时用。特殊之处是不用您自己提供密码,程序代劳了。
|
能力值:
( LV3,RANK:30 )
|
-
-
19 楼
我知道,程序算密码用的第一个随机数是一个32位的整数,只要确定了这个数,你后面所有的密钥都可以推算出来,加密还有什么意义?我又没说用户提供密码吧?不要偷换概念
|
能力值:
( LV3,RANK:30 )
|
-
-
20 楼
你一直在回避算法的安全性,回答的无关紧要,不要回复我了,节约大家的时间,谢谢。
|
能力值:
( LV2,RANK:10 )
|
-
-
21 楼
输入少量的用户密码 =》 我爆破少量密码就好了,省心!感谢楼主让我们吃上饭
|
能力值:
( LV3,RANK:30 )
|
-
-
22 楼
wx_ggMM
输入少量的用户密码 =》 我爆破少量密码就好了,省心!感谢楼主让我们吃上饭
哈哈哈
|
能力值:
( LV4,RANK:40 )
|
-
-
23 楼
别做梦了,程序设计就是使用大量密码的,请注意其默认的密码数组是预先设定好的,用户输入的少量密码,将对那个固定数组的数值进行全面改造,改造后的数组才是真正要使用的用户密码数组。
|
能力值:
( LV1,RANK:0 )
|
-
-
24 楼
看着很有意思,专门注册了账号... 楼主能不能给举个例子,假如短密码只有1位,按照你的方法生成的长密码有多少种可能?然后再依次类推,短密码有2位、3位...时,长密码分别是多少种情况?
|
能力值:
( LV4,RANK:40 )
|
-
-
25 楼
楼上知道程序可以有默认密码吧,这个可以随便定义长度并无限制,设这个字符串为A串,这个东西是表面上的,如果读懂了程序你可以知道A串是什么,现在程序接收到用户的短密码,例如一两个字节,用这两个字节算出一个数值作为随机函数的种子,然后用随机函数控制对 A串进行随机排序,这时您了解的 A串将发生天翻地覆的变化,然后您就可以用这个变化后的 A串,当作用户密码来用了,这就是少量输入使用大量密码的秘密。
|