全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!

pptp,ms,chap,v2,rfc2759 · 浏览次数 : 6

小编点评

```python def GenerateAuthenticatorResponse(password_unicode,NTResponse,PeerChalleng,AuthenticatorChallenge,UserName): # 创建 Magic1 和 Magic2 字符串 Magic1 = b'\\x4D\\x61\\x67\\x69\\x63\\x20\\x73\\x65\\x72\\x76\\x65\\x72\\x20\\x74\\x6F\\x20\\x63\\x6C\\x69\\x65\\x6E\\x74\\x20\\x73\\x69\\x67\\x6E\\x69\\x6E\\x67\\x20\\x63\\x6F\\x6E\\x73\\x74\\x61\\x6E\\x74' Magic2 = bytes([0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,0x6E]) # 将 password_unicode、NTResponse、PeerChalleng 和 AuthenticatorChallenge 字符串编码到 bytes 字符串 Passwordhash = Nt_password_hash(password_unicode) PasswordHashhash = Nt_password_hashhash(Passwordhash) # 创建 SHA-256 对象 Context = SHA.new() # 将密码hash、NTResponse 和 Magic1 字符串添加到 Context 中 Context.update(PasswordHashhash) Context.update(NTResponse) Context.update(Magic1) # 获取 Context 的摘要 aa = Context.digest() # 将 Challenge 和 Magic2 字符串添加到 Context 中 Context.update(Challenge) Context.update(Magic2) # 获取 Context 的摘要 aa = Context.digest() # 返回 AuthenticatorResponse 字符串 return b'S=' + binascii.hexlify(aa)[:40] # 以下代码用于示例,生成一个 Challenge 和 AuthenticatorResponse 字符串 UserName = 'WA_042'.encode("utf8") password = '123456'.encode("utf8") PeerChalleng = b'\\x11\\xD8\\x2A\\x82\\x0F\\xBE\\xB0\\x30\\x73\\xB1\\xF7\\x03\\x73\\x22\\x26\\xBF' AuthenticatorChallenge = b'\\x11\\xD4\\x43\\x9D\\xBD\\x65\\xA2\\x6D\\x75' password_unicode = b'' for ch in password: password_unicode += bytes([ch]) password_unicode += b'\\x00' NTResponse = GenerateNTResponse(AuthenticatorChallenge, PeerChalleng, UserName, password_unicode) # 打印 Challenge 和 AuthenticatorResponse 字符串 print("Challenge: ", NTResponse) print("AuthenticatorResponse: ", AuthenticatorResponse) ``` **注意:** * `Nt_password_hash` 函数用于计算密码的哈希值。 * `GenerateNTResponse` 函数用于根据AuthenticatorChallenge、PeerChalleng 和用户名生成一个 Challenge 和 AuthenticatorResponse 字符串。 * `binascii.hexlify(aa)[:40]` 用于将 byte 字符串转换为十六进制字符串,并仅保留前 40 个字符。

正文

本文基于网络密码课上的实验

本来想水一水就过去,代码就网上找找,不行就GPT写,但是!一份都找不到,找到的代码都是跑不了的,总会是就是乱七八糟。所以准备认真的写一份。

代码编译成功的前提是要预先装好openssl库!

本随笔主要有三个内容:

  1. 编写程序,模拟计算NTResponse、AuthenticatorResponse,
  2. 根据前期PPTP实验中捕获的数据包中CHAP协议的挑战响应认证数据,在未知用户口令情况下编程实现CHAP认证口令的破解

在单向数据条件下(仅能截获用户数据)实现CHAP认证口令的破解

首先放一个我自己抓的包,可以看到,这是chap协议挑战响应的三次握手,

 那么我们继续进行,编程模拟,就要先搞清楚每个字段代表的什么,文档中第一个包的描述,给的是Authenticator challenge

也就是我住的第一个包里的value

这是第二个包,16字节peer-challenge,8位的0,24位的NT-Response

value内的值,对应看

 

 第三个包,内容是s=authticator-response

接下来我们开始编程实现,每一个字段都是由对应的函数计算得出

一、编写程序,模拟计算NTResponse、AuthenticatorResponse

1.查阅RFC2759文档,找到描述的计算NTResponse的函数

NT-Response的值是由GenerateNTResponse()计算得出,看到该函数有四个输入,分别是AuthenticatorChallenge(16)、PeerChallenge(16)、UserName和Password,一个输出Response(24)
 

 

此外,有三个函数对输入进行处理:ChallengeHash:对两个挑战值hash,结果放到challenge中。NtPasswordHash:对password做hash,结果放到password中。ChallengeResponse:对challenge和passwordhash做运算,结果得到NT-Response。

2.根据文档描述编写代码

GenerateNTResponse()函数

1 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response)
2 {
3     BYTE challenge[8];
4     BYTE password_hash[16];
5 
6     _ChallengeHash(hProv, peer_challenge, auth_challenge, user_name, challenge);
7     _NtPasswordHash(hProv, password, password_hash);
8     _ChallengeResponse(hProv, challenge, password_hash, response);
9 }

_ChallengeHash()函数,对peer challenge、auth challenge、user name连接然后进行sha1哈希,结果放到challenge中返回

 1 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge)
 2 {
 3     HCRYPTHASH hHash = 0;
 4     if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
 5         throw "CryptCreateHash failed (SHA1)";
 6     if (!CryptHashData(hHash, peer_challenge, 16, 0))
 7         throw "CryptHashData failed (peer challenge)";
 8     if (!CryptHashData(hHash, auth_challenge, 16, 0))
 9         throw "CryptHashData failed (auth challenge)";
10     if (!CryptHashData(hHash, (const BYTE*)user_name,
11         (DWORD)strlen(user_name), 0))
12         throw "CryptHashData failed (user name)";
13     DWORD hash_len = SHA1LEN;
14     BYTE hash_buffer[SHA1LEN];
15     if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer,
16         &hash_len, 0))
17         throw "CryptGetHashParam failed (challenge hash)";
18     memcpy(challenge, hash_buffer, 8);
19 
20 }

_NtPasswordHash函数():将password做MD4哈希,然后返回password_hash中

 

 1 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash)
 2 {
 3     HCRYPTHASH hHash = 0;
 4     if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHash))
 5         throw "CryptCreateHash failed (MD4)";
 6     if (!CryptHashData(hHash, (const BYTE*)password, lstrlenW(password) << 1, 0))
 7         throw "CryptHashData failed (user password)";
 8     DWORD hash_len = MD4LEN;
 9     BYTE hash_buffer[MD4LEN];
10     if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, &hash_len, 0))
11         throw "CryptGetHashParam failed (NT password hash)";
12     memcpy(password_hash, hash_buffer, 16);
13 }

ChallengeResponse()函数:首先将16位passwordhash后面填充五个0变成21位,接着分成三个长度为7的密钥,分别对challenge做三次DES加密,三次加密结果放到response中

 

 1 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response)
 2 {
 3     BYTE z_password_hash[21];
 4     memset(z_password_hash, 0, 21);
 5     memcpy(z_password_hash, password_hash, 16);
 6 
 7     _DesEncrypt(hProv, challenge, z_password_hash, response);
 8     _DesEncrypt(hProv, challenge, z_password_hash + 7, response + 8);
 9     _DesEncrypt(hProv, challenge, z_password_hash + 14, response + 16);
10 }

接下来我们来看DES加密函数,由于每次给的密钥都是7位长度,所以加密前首先调用EXPAND()函数对密钥进行扩展,扩展到8位,这里的加密模式是ECB(电子密码本)加密,加密的结果放到result中

 1 typedef struct {
 2     BLOBHEADER key_header;
 3     DWORD key_length;
 4     BYTE key_data[8];
 5 } DESKey;
 6 
 7 void EXPAND(BYTE* key);
 8 
 9 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result)
10 {
11     // Fill CryptoAPI-required key structure
12     DESKey des_key;
13     des_key.key_header.bType = PLAINTEXTKEYBLOB;
14     des_key.key_header.bVersion = CUR_BLOB_VERSION;
15     des_key.key_header.reserved = 0;
16     des_key.key_header.aiKeyAlg = CALG_DES;
17     des_key.key_length = 8;
18     memcpy(des_key.key_data, key, 7);
19     EXPAND(des_key.key_data);
20 
21     HCRYPTKEY hKey;
22     // import key BLOB
23     if (!CryptImportKey(hProv, (BYTE*)&des_key,
24         sizeof(des_key), 0, 0, &hKey))
25         throw "CryptImportKey failed";
26     // set ECB mode required by RFC
27     DWORD des_mode = CRYPT_MODE_ECB;
28     if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&des_mode, 0))
29         throw "CryptSetKeyParam failed (ECB mode)";
30     // set initialization vector
31     BYTE IV[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
32     if (!CryptSetKeyParam(hKey, KP_IV, &IV[0], 0))
33         throw "CryptSetKeyParam failed (init vector)";
34     // encrypt
35     DWORD data_len = 8;
36     memcpy(result, data, 8);    // encrypt in-place
37     if (!CryptEncrypt(hKey, 0, FALSE, 0, (BYTE*)&result[0], &data_len, 8))
38         throw "CryptEncrypt failed (DES)";
39 }
40 

下面来看一下密钥扩展函数,该函数的具体流程如下:

  1. 创建了一个名为new_key的字节数组,用于存储扩展后的密钥。
  2. 接下来,使用一个循环来处理每个7位的数据包。循环从0到7迭代,共8次。
  3. 在每次迭代中,根据当前的索引值i,获取原始密钥中的两个相邻的八位字节。如果i大于0,则取索引为i-1的字节作为左字节;否则,取索引为0的字节作为左字节。右字节的获取方式类似,如果i小于7,则取索引为i的字节作为右字节;否则,取索引为6的字节作为右字节。
  4. 接着,通过位运算将左字节和右字节中的位进行清除,只保留当前7位的数据。左字节通过与操作符&和右移操作符>>实现,右字节通过与操作符&和左移操作符<<实现。
  5. 然后,将左字节和右字节进行位移操作,将它们移动到最终的位置。左字节通过左移操作符<<实现,右字节通过右移操作符>>实现。
  6. 最后,将左字节和右字节进行按位或操作,得到最终的八位字节,并将其存储在new_key数组中对应的位置。
     1 void EXPAND(BYTE* key)
     2 {
     3     BYTE left_octet, right_octet;
     4     BYTE new_key[8];
     5     // split original key into eight 7-bit packs:
     6     // 00000001 11111122 22222333 3333444 44455555 55666666 67777777
     7     for (int i = 0; i < 8; i++)
     8     {
     9         // fetch two adjacent octets of key containing current 7 bits
    10         left_octet = key[i > 0 ? i - 1 : 0];
    11         right_octet = key[i < 7 ? i : 6];
    12         // clear all bits except current 7 ones
    13         left_octet &= 0xFF >> (8 - i);
    14         right_octet &= 0xFF << (i + 1);
    15         // shift bits to their final position
    16         left_octet = left_octet << (8 - i);
    17         right_octet = right_octet >> i;
    18         // combine into resulting octet
    19         new_key[i] = left_octet | right_octet;
    20     }
    21     memcpy(key, new_key, 8);
    22 }

    至此,我们就算出来了NtResponse

    2.计算AunthenticatorResponse

    首先查看文档,得知输入有Password、NT-Response、PeerChallenge、AuthenticatorChallenge、UserName
    输出AuthenticatorResponse。该函数处理流程如下:
    1. 该函数里面定义了长度16的PasswordHash和PasswordHashHash,长度为8的Challenge。首先,使用MD4算法对密码进行哈希处理,得到PasswordHash。
    2. 然后,对PasswordHash进行再次哈希处理,得到PasswordHashHash。
    3. 接着,使用SHA算法对PasswordHashHash、NT-Response和Magic1进行哈希处理,得到Digest。
    4. 使用ChallengeHash函数对PeerChallenge、AuthenticatorChallenge和UserName进行哈希处理,得到Challenge。
    5. 再次使用SHA算法对Digest、Challenge和Magic2进行哈希处理,得到最终的Digest

     

     

    下面是具体实现代码

  

 1 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName)
 2 {
 3     BYTE Magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
 4     0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
 5     0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
 6     0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
 7     BYTE Magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
 8     0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
 9     0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
10     0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
11     0x6E };
12     BYTE Passwordhash[16];
13     _NtPasswordHash(hProv, password_unicode, Passwordhash);
14     BYTE PasswordHashhash[16];
15     _NtPasswordHashHash(hProv, Passwordhash, PasswordHashhash);
16     SHA_CTX Context;
17     SHA1_Init(&Context);
18     SHA1_Update(&Context, PasswordHashhash, 16);
19     SHA1_Update(&Context, NTResponse, 24);
20     SHA1_Update(&Context, Magic1, 39);
21     unsigned char aa[SHA_DIGEST_LENGTH];
22     SHA1_Final(aa, &Context);
23     BYTE Challenge[8];
24     _ChallengeHash(hProv, PeerChalleng, AuthenticatorChallenge, UserName, Challenge);
25     SHA1_Init(&Context);
26     SHA1_Update(&Context, aa, sizeof(aa));
27     SHA1_Update(&Context, Challenge, 8);
28     SHA1_Update(&Context, Magic2, 41);
29     unsigned char bb[SHA_DIGEST_LENGTH];
30     SHA1_Final(bb, &Context);
31     cout << endl << "AuthenticatorResponse: s=";
32     for (int i = 0; i < 20; i++)
33     {
34         printf("%02X", (unsigned char)bb[i]);
35     }
36 }

3.运行程序,将结果和wireshark抓包内容进行比对

 NT-Response:DA2191E86678231E62B5D628CBA859031B1E6082533B32B5

 

AuthenticatorResponse: s=2AA71CBBDD95F43ABA628329A0271A8DD1114310

 全部代码如下:注意展开

  1 #pragma comment(lib, "libssl.lib")
  2 #pragma comment(lib, "libcrypto.lib")
  3 #include <stdio.h>
  4 #include <fstream>
  5 #include <tchar.h>
  6 #include <windows.h>
  7 #include <WinCrypt.h>
  8 #include <openssl/sha.h>
  9 #include<string>
 10 #include<iostream>
 11 #include <openssl/des.h>
 12 using namespace std;
 13 
 14 void print_array(const BYTE* array, DWORD length);
 15 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response);
 16 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName);
 17 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response);
 18 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash);
 19 BYTE nt_response[24];
 20 int main()
 21 {
 22     const char* user_name = "WA_042";
 23     const wchar_t* password = L"123456";
 24     const BYTE auth_challenge[] = { 0x11,0xD8,0x2A,0x82,0x0F,
 25                                    0xBE,0xB0,0x30,0x73,0xB1,0xF7,
 26                                    0x03,0x73,0x22,0x26,0xBF };
 27     const BYTE peer_challenge[] = { 0xC1,0xD4,0x43,0x9D,0xBD,0x65,
 28                                    0xA2,0x74,0xB9,0x7A,0xF0,0x3F,
 29                                    0x98,0x00,0x6D,0x75 };
 30     HCRYPTPROV hProv = 0;
 31 
 32     CryptAcquireContext(&hProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
 33     //wcout << password;
 34     printf("User name: %s\n", user_name);
 35     wprintf(L"User password: %s\n", password);
 36     printf("Authenticator challenge: ");
 37     print_array(auth_challenge, 16);
 38     printf("\n");
 39     printf("Peer challenge: "); print_array(peer_challenge, 16);
 40     printf("\n");
 41     GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, password, nt_response);
 42     printf("NT Response: "); print_array(nt_response, 24);
 43     GenerateAuthenticatorResponse(hProv, password, nt_response, peer_challenge, auth_challenge, user_name);
 44     cout << endl;
 45     Challenge1(hProv, auth_challenge, peer_challenge, user_name, nt_response);
 46     cout << endl << "挑战一结束";
 47     return 0;
 48 }
 49 
 50 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response)
 51 {
 52     std::ifstream file("passlib.txt");
 53     if (!file) {
 54         std::cerr << "无法打开文件" << std::endl;
 55     }
 56 
 57     string line;
 58     while (std::getline(file, line)) {
 59         BYTE Nt_response[24];
 60         wchar_t* wc = new wchar_t[line.size()];
 61         swprintf(wc, 100, L"%S", line.c_str());
 62         // wcout << wc << endl;
 63         GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, wc, Nt_response);
 64         //cout << Nt_response << endl;
 65         for (int i = 0; i < 24; i++)
 66         {
 67 
 68             if (Nt_response[i] != nt_response[i])
 69             {
 70                 cout << "破解失败" << endl;
 71                 break;
 72             }
 73             else
 74             {
 75                 cout << "破解成功!密码为:";
 76                 wcout << wc << endl;
 77                 break;
 78             }
 79             // 处理读取到的数据
 80         }
 81     }
 82 
 83     file.close();
 84 
 85 
 86 }
 87 void _DesDecrypt(unsigned char* NTResponse, unsigned char* key, unsigned char* decrypted_result);
 88 
 89 const CHAR hex_digits[] = "0123456789ABCDEF";
 90 
 91 void print_array(const BYTE* array, DWORD length)
 92 {
 93     printf("0x");
 94     for (DWORD i = 0; i < length; i++)
 95         printf("%c%c", hex_digits[array[i] >> 4],
 96             hex_digits[array[i] & 0xf]);
 97 }
 98 
 99 
100 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge);
101 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash);
102 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response);
103 void _NtPasswordHashHash(const HCRYPTPROV hProv, BYTE* password, BYTE* password_hash);
104 void GenerateNTResponse(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, const wchar_t* password, BYTE* response)
105 {
106     BYTE challenge[8];
107     BYTE password_hash[16];
108 
109     _ChallengeHash(hProv, peer_challenge, auth_challenge, user_name, challenge);
110     _NtPasswordHash(hProv, password, password_hash);
111     _ChallengeResponse(hProv, challenge, password_hash, response);
112 }
113 void GenerateAuthenticatorResponse(const HCRYPTPROV hProv, const wchar_t* password_unicode, const BYTE* NTResponse, const BYTE* PeerChalleng, const BYTE* AuthenticatorChallenge, const char* UserName)
114 {
115     BYTE Magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
116     0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
117     0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
118     0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
119     BYTE Magic2[41] = { 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
120     0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
121     0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
122     0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
123     0x6E };
124     BYTE Passwordhash[16];
125     _NtPasswordHash(hProv, password_unicode, Passwordhash);
126     BYTE PasswordHashhash[16];
127     _NtPasswordHashHash(hProv, Passwordhash, PasswordHashhash);
128     SHA_CTX Context;
129     SHA1_Init(&Context);
130     SHA1_Update(&Context, PasswordHashhash, 16);
131     SHA1_Update(&Context, NTResponse, 24);
132     SHA1_Update(&Context, Magic1, 39);
133     unsigned char aa[SHA_DIGEST_LENGTH];
134     SHA1_Final(aa, &Context);
135     BYTE Challenge[8];
136     _ChallengeHash(hProv, PeerChalleng, AuthenticatorChallenge, UserName, Challenge);
137     SHA1_Init(&Context);
138     SHA1_Update(&Context, aa, sizeof(aa));
139     SHA1_Update(&Context, Challenge, 8);
140     SHA1_Update(&Context, Magic2, 41);
141     unsigned char bb[SHA_DIGEST_LENGTH];
142     SHA1_Final(bb, &Context);
143     cout << endl << "AuthenticatorResponse: s=";
144     for (int i = 0; i < 20; i++)
145     {
146         printf("%02X", (unsigned char)bb[i]);
147     }
148 }
149 #define SHA1LEN 20
150 
151 void _ChallengeHash(const HCRYPTPROV hProv, const BYTE* peer_challenge, const BYTE* auth_challenge, const char* user_name, BYTE* challenge)
152 {
153     HCRYPTHASH hHash = 0;
154     if (!CryptCreateHash(hProv, CALG_SHA1, 0, 0, &hHash))
155         throw "CryptCreateHash failed (SHA1)";
156     if (!CryptHashData(hHash, peer_challenge, 16, 0))
157         throw "CryptHashData failed (peer challenge)";
158     if (!CryptHashData(hHash, auth_challenge, 16, 0))
159         throw "CryptHashData failed (auth challenge)";
160     if (!CryptHashData(hHash, (const BYTE*)user_name,
161         (DWORD)strlen(user_name), 0))
162         throw "CryptHashData failed (user name)";
163     DWORD hash_len = SHA1LEN;
164     BYTE hash_buffer[SHA1LEN];
165     if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer,
166         &hash_len, 0))
167         throw "CryptGetHashParam failed (challenge hash)";
168     memcpy(challenge, hash_buffer, 8);
169 
170 }
171 
172 
173 #define MD4LEN 16
174 
175 void _NtPasswordHash(const HCRYPTPROV hProv, const wchar_t* password, BYTE* password_hash)
176 {
177     HCRYPTHASH hHash = 0;
178     if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHash))
179         throw "CryptCreateHash failed (MD4)";
180     if (!CryptHashData(hHash, (const BYTE*)password, lstrlenW(password) << 1, 0))
181         throw "CryptHashData failed (user password)";
182     DWORD hash_len = MD4LEN;
183     BYTE hash_buffer[MD4LEN];
184     if (!CryptGetHashParam(hHash, HP_HASHVAL, hash_buffer, &hash_len, 0))
185         throw "CryptGetHashParam failed (NT password hash)";
186     memcpy(password_hash, hash_buffer, 16);
187 }
188 void _NtPasswordHashHash(const HCRYPTPROV hProv, BYTE* passwordhash, BYTE* password_hashhash)
189 {
190     HCRYPTHASH hHashhash = 0;
191     if (!CryptCreateHash(hProv, CALG_MD4, 0, 0, &hHashhash))
192         throw "CryptCreateHash failed (MD4)";
193     if (!CryptHashData(hHashhash, passwordhash, 16, 0))
194         throw "CryptHashData failed (user password)";
195     DWORD hash_len = MD4LEN;
196     BYTE hash_buffer[MD4LEN];
197     if (!CryptGetHashParam(hHashhash, HP_HASHVAL, hash_buffer, &hash_len, 0))
198         throw "CryptGetHashParam failed (NT password hash)";
199 
200     memcpy(password_hashhash, hash_buffer, 16);
201     //cout << endl; print_array(password_hashhash, 16);
202 }
203 
204 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result);
205 void _ChallengeResponse(const HCRYPTPROV hProv, const BYTE* challenge, const BYTE* password_hash, BYTE* response)
206 {
207     BYTE z_password_hash[21];
208     memset(z_password_hash, 0, 21);
209     memcpy(z_password_hash, password_hash, 16);
210 
211     _DesEncrypt(hProv, challenge, z_password_hash, response);
212     _DesEncrypt(hProv, challenge, z_password_hash + 7, response + 8);
213     _DesEncrypt(hProv, challenge, z_password_hash + 14, response + 16);
214 }
215 typedef struct {
216     BLOBHEADER key_header;
217     DWORD key_length;
218     BYTE key_data[8];
219 } DESKey;
220 
221 void EXPAND(BYTE* key);
222 
223 void _DesEncrypt(const HCRYPTPROV hProv, const BYTE* data, const BYTE* key, BYTE* result)
224 {
225     // Fill CryptoAPI-required key structure
226     DESKey des_key;
227     des_key.key_header.bType = PLAINTEXTKEYBLOB;
228     des_key.key_header.bVersion = CUR_BLOB_VERSION;
229     des_key.key_header.reserved = 0;
230     des_key.key_header.aiKeyAlg = CALG_DES;
231     des_key.key_length = 8;
232     memcpy(des_key.key_data, key, 7);
233     EXPAND(des_key.key_data);
234 
235     HCRYPTKEY hKey;
236     // import key BLOB
237     if (!CryptImportKey(hProv, (BYTE*)&des_key,
238         sizeof(des_key), 0, 0, &hKey))
239         throw "CryptImportKey failed";
240     // set ECB mode required by RFC
241     DWORD des_mode = CRYPT_MODE_ECB;
242     if (!CryptSetKeyParam(hKey, KP_MODE, (BYTE*)&des_mode, 0))
243         throw "CryptSetKeyParam failed (ECB mode)";
244     // set initialization vector
245     BYTE IV[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
246     if (!CryptSetKeyParam(hKey, KP_IV, &IV[0], 0))
247         throw "CryptSetKeyParam failed (init vector)";
248     // encrypt
249     DWORD data_len = 8;
250     memcpy(result, data, 8);    // encrypt in-place
251     if (!CryptEncrypt(hKey, 0, FALSE, 0, (BYTE*)&result[0], &data_len, 8))
252         throw "CryptEncrypt failed (DES)";
253 }
254 
255 void EXPAND(BYTE* key)
256 {
257     BYTE left_octet, right_octet;
258     BYTE new_key[8];
259     // split original key into eight 7-bit packs:
260     // 00000001 11111122 22222333 3333444 44455555 55666666 67777777
261     for (int i = 0; i < 8; i++)
262     {
263         // fetch two adjacent octets of key containing current 7 bits
264         left_octet = key[i > 0 ? i - 1 : 0];
265         right_octet = key[i < 7 ? i : 6];
266         // clear all bits except current 7 ones
267         left_octet &= 0xFF >> (8 - i);
268         right_octet &= 0xFF << (i + 1);
269         // shift bits to their final position
270         left_octet = left_octet << (8 - i);
271         right_octet = right_octet >> i;
272         // combine into resulting octet
273         new_key[i] = left_octet | right_octet;
274     }
275     memcpy(key, new_key, 8);
276 }
View Code

 

二、根据前期PPTP实验中捕获的数据包中CHAP协议的挑战响应认证数据,在未知用户口令情况下编程实现CHAP认证口令的破解

思路:编写一个challenge1()函数,创建一个自己的字典,使用字典进行爆破,每次读入字典中的一个密码文本,然后调用GenerateNTResponse()函数计算NT-Response,与正确的进行比对,如果成功,则输出密码

1.编写challenge1()函数

 1 void Challenge1(const HCRYPTPROV hProv, const BYTE* auth_challenge, const BYTE* peer_challenge, const char* user_name, BYTE* response)
 2 {
 3     std::ifstream file("passlib.txt");
 4     if (!file) {
 5         std::cerr << "无法打开文件" << std::endl;
 6     }
 7 
 8     string line;
 9     while (std::getline(file, line)) {
10         BYTE Nt_response[24];
11         wchar_t* wc = new wchar_t[line.size()];
12         swprintf(wc, 100, L"%S", line.c_str());
13         // wcout << wc << endl;
14         GenerateNTResponse(hProv, auth_challenge, peer_challenge, user_name, wc, Nt_response);
15         //cout << Nt_response << endl;
16         for (int i = 0; i < 24; i++)
17         {
18 
19             if (Nt_response[i] != nt_response[i])
20             {
21                 cout << "破解失败" << endl;
22                 break;
23             }
24             else
25             {
26                 cout << "破解成功!密码为:";
27                 wcout << wc << endl;
28                 break;
29             }
30             // 处理读取到的数据
31         }
32     }
33 
34     file.close();
35 
36 
37 }

运行字典爆破

 

 

三、在单向数据条件下(仅能截获用户数据)实现CHAP认证口令的破解

思路:单向数据条件下,意味着只能获取用户数据NT-Response、PeerChallenge、UserName。那么根据NtResponse的计算原理,采用逆向破解的思路。首先:由于NtResponse是由passwordhash分成三部分作密钥对challenge加密的密文连接得到,那么先将NtResponse拆分成三部分,对应每部分passwordhash对challenge做DES加密的结果。

   因此,破解思路是,每次读入字典里的密钥做hash作为密钥,然后将NtResponse三个部分分别作为DES函数的输入,记录DES函数的输出,比较三个输出是否相同,如果相同,则破解成功。

注意,我能运行成功是因为我的目录里面有passlib.txt字典文件,大家可以创建自己的字典文件

1.编写代码

源码如下,注意,已经折叠,点击展开

  1 #define _CRT_SECURE_NO_WARNINGS C4996
  2 #include <iostream>
  3 #include <stdio.h>
  4 #include <openssl/ssl.h>
  5 #include <stdlib.h>
  6 #include <string.h>
  7 #include <openssl/sha.h>
  8 #include <openssl/md4.h>
  9 #include <openssl/des.h>
 10 
 11 int  readfile(char* pass[]) {
 12     FILE* file = fopen("./passlib.txt", "r");
 13     if (file == NULL) {
 14         perror("Error opening file");
 15         return 0;
 16     }
 17 
 18     char buffer[100];
 19     int count = 0;
 20 
 21     while (fgets(buffer, sizeof(buffer), file) != NULL) {
 22         buffer[strcspn(buffer, "\n")] = 0;
 23         pass[count] = _strdup(buffer);
 24         count++;
 25     }
 26 
 27     fclose(file);
 28 
 29 
 30     return count;
 31 }
 32 
 33 void des_decrypt(unsigned char* key, unsigned char* NTResponse, unsigned char* decrypted_result) {
 34     DES_cblock des_key;
 35     memcpy(des_key, key, 8);
 36     DES_key_schedule key_schedule;
 37     DES_set_key_unchecked(&des_key, &key_schedule);
 38     DES_ecb_encrypt((DES_cblock*)NTResponse, (DES_cblock*)decrypted_result, &key_schedule, DES_DECRYPT);
 39 }
 40 void expand(unsigned char* t, unsigned char* k)
 41 {
 42     k[0] = t[0] & 0xfe;
 43     k[1] = (t[0] << 7 | t[1] >> 1) & 0xfe;
 44     k[2] = (t[1] << 6 | t[2] >> 2) & 0xfe;
 45     k[3] = (t[2] << 5 | t[3] >> 3) & 0xfe;
 46     k[4] = (t[3] << 4 | t[4] >> 4) & 0xfe;
 47     k[5] = (t[4] << 3 | t[5] >> 5) & 0xfe;
 48     k[6] = (t[5] << 2 | t[6] >> 6) & 0xfe;
 49     k[7] = (t[6] << 1) & 0xfe;
 50 }
 51 int main()
 52 {
 53     unsigned char peer_challenge[17] = { 0xC1,0xD4,0x43,0x9D,0xBD,0x65,0xA2,0x74,0xB9,0x7A,0xF0,0x3F,0x98,0x00,0x6D,0x75 ,'\0' };
 54     char username[7] = { 0x77,0x61,0x5f,0x30,0x34,0x32,'\0' };
 55     unsigned char challenge[8] = { 0x00 };
 56     unsigned char NTResponse[25] = { 0xDA ,0x21,0x91 ,0xE8 ,0x66 ,0x78 ,0x23 ,0x1E ,0x62 ,0xB5
 57         ,0xD6 ,0x28 ,0xCB ,0xA8 ,0x59 ,0x03 ,0x1B ,0x1E ,0x60 ,0x82 ,0x53 ,0x3B ,0x32 ,0xB5,'\0' };
 58 
 59     const char* pass[100];
 60     int loop = readfile((char**)pass);
 61     int has_found = 0;
 62 
 63     unsigned char response1[9] = { 0x00 };
 64     unsigned char response2[9] = { 0x00 };
 65     unsigned char response3[9] = { 0x00 };
 66 
 67     strncat((char*)response1, (char*)NTResponse, 8);
 68     response1[8] = '\0';
 69     strncat((char*)response2, (char*)NTResponse + 8, 8);
 70     response2[8] = '\0';
 71     strncat((char*)response3, (char*)NTResponse + 16, 8);
 72     response3[8] = '\0';
 73     printf("read the password in pass.txt:\n");
 74     for (int i = 0; i < loop; i++) {
 75         printf("%s\n", pass[i]);
 76     }
 77 
 78     for (int j = 0; j < loop; j++)
 79     {
 80 
 81         unsigned char password_hash[21] = { 0x00 };
 82         unsigned char password[10] = { 0x00 };
 83         strncpy((char*)password, pass[j], strlen(pass[j]));
 84 
 85         password[strlen(pass[j])] = '\0';
 86 
 87         int pass_len = strlen((char*)password);
 88 
 89         unsigned char* unicode_password;
 90         unicode_password = (unsigned char*)malloc(sizeof(unsigned char) * pass_len * 2);
 91         for (int i = 0; i < pass_len; i++) {
 92             unicode_password[i * 2] = password[i];
 93             unicode_password[i * 2 + 1] = 0;
 94         }
 95 
 96         MD4((unsigned char*)unicode_password, pass_len * sizeof(unsigned short), password_hash);
 97 
 98         char zero[5] = { 0x00,0x00,0x00,0x00,0x00 };
 99 
100         strncat((char*)password_hash, zero, 5);
101 
102         unsigned char ks1[9] = { 0x00 };
103         unsigned char ks2[9] = { 0x00 };
104         unsigned char ks3[9] = { 0x00 };
105 
106         unsigned char pass1[8] = { 0x00 };
107         unsigned char pass2[8] = { 0x00 };
108         unsigned char pass3[8] = { 0x00 };
109 
110         for (int i = 0; i < 7; i++)
111         {
112             pass1[i] = password_hash[i];
113         }
114         for (int i = 0; i < 7; i++)
115         {
116             pass2[i] = password_hash[i + 7];
117         }
118         for (int i = 0; i < 7; i++)
119         {
120             pass3[i] = password_hash[i + 14];
121         }
122 
123         expand(pass1, ks1);
124 
125         expand(pass2, ks2);
126 
127         expand(pass3, ks3);
128 
129 
130         unsigned char result1[32] = { 0x00 };
131         unsigned char result2[32] = { 0x00 };
132         unsigned char result3[32] = { 0x00 };
133 
134         des_decrypt(ks1, response1, result1);
135         des_decrypt(ks2, response2, result2);
136         des_decrypt(ks3, response3, result3);
137 
138         if (strcmp((char*)result1, (char*)result2) == 0)
139         {
140             if (strcmp((char*)result1, (char*)result3) == 0)
141             {
142                 printf("\n");
143                 printf("Find! The password is:%s", pass[j]);
144                 has_found = 1;
145                 break;
146             }
147         }
148 
149     }
150     for (int i = 0; i < loop; i++) {
151         free((void*)pass[i]);
152     }
153 
154     if (has_found == 0)
155         printf("Cannot Find the password");
156 }
单向数据源码

2.运行代码

 至此,实验结束

顺便附上python源码,欢迎批评指正!

  1 # coding = utf-8
  2 import binascii
  3 from builtins import bytes
  4 
  5 from Crypto.Hash import SHA1,SHA,MD4
  6 from Crypto.Cipher import DES,ARC4
  7 
  8 def GenerateNTResponse(AuthenticatorChallenge,PeerChalleng,UserName,password_unicode):
  9     PasswordHash=Nt_password_hash(password_unicode)
 10     #print_hex(PasswordHash)
 11     Challenge=ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName)
 12     challenge_resposn=ChallengeResponse(PasswordHash,Challenge)
 13     return challenge_resposn
 14 
 15 def ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName):
 16     Challenge=SHA1.new()
 17     Challenge.update(PeerChalleng)
 18     Challenge.update(AuthenticatorChallenge)
 19     Challenge.update(UserName)
 20     return Challenge.digest()[:8]
 21 def Expand(rawkey):  #expand 7Bytes to 8 Bytes
 22     tmp_key = []
 23     for i in rawkey[:7]:
 24         tmp_key.append(i)
 25     key = []
 26     for i in range(8):
 27         key.append(b'\x00')
 28     # -------------------------
 29     key[0] = tmp_key[0]
 30     for i in range(1, 7):
 31         key[i] = ((tmp_key[i - 1] << (8 - i)) & 0xff) | (tmp_key[i] >> i)
 32     key[7] =  (tmp_key[6] << 1) & 0xff
 33     global b
 34     for i in range(len(key)):
 35         b = 1
 36         for j in range(1, 8):
 37             t = (key[i] >> j)
 38             b = (t ^ b) & 0x1 #
 39         key[i] = (key[i] & 0xfe) | b
 40     ans = b''
 41     for i in range(8):
 42         ans += bytes([key[i]])
 43     return ans
 44 def ChallengeResponse(PasswordHash,Challenge):
 45     zero = b'\x00'
 46     while(len(PasswordHash) < 21): # important
 47         PasswordHash += zero #zero-padded to 21 octets
 48     res = DES.new(Expand(PasswordHash[0:7]), DES.MODE_ECB).encrypt(Challenge)
 49     res += DES.new(Expand(PasswordHash[7:14]), DES.MODE_ECB).encrypt(Challenge)
 50     res += DES.new(Expand(PasswordHash[14:21]), DES.MODE_ECB).encrypt(Challenge)
 51     return res
 52 
 53 def Nt_password_hash(Password):
 54     PasswordHash=MD4.new(Password)
 55     return PasswordHash.digest()
 56 
 57 def Nt_password_hashhash(Passwordhash):
 58     PasswordHashhash=MD4.new(Passwordhash)
 59     return PasswordHashhash.digest()
 60 
 61 def GenerateAuthenticatorResponse(password_unicode,NTResponse,PeerChalleng,AuthenticatorChallenge,UserName):
 62     Magic1 =b'\x4D\x61\x67\x69\x63\x20\x73\x65\x72\x76\x65\x72\x20\x74\x6F\x20\x63\x6C\x69\x65\x6E\x74\x20\x73\x69\x67\x6E\x69\x6E\x67\x20\x63\x6F\x6E\x73\x74\x61\x6E\x74'
 63     Magic2 = bytes([0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,0x6E])
 64     Passwordhash=Nt_password_hash(password_unicode)
 65     PasswordHashhash=Nt_password_hashhash(Passwordhash)
 66     Context=SHA.new()
 67     Context.update(PasswordHashhash)
 68     Context.update(NTResponse)
 69     Context.update(Magic1)
 70     aa=Context.digest()
 71     Challenge=ChallengeHash(PeerChalleng,AuthenticatorChallenge,UserName)
 72     Context=SHA.new()
 73     Context.update(aa)
 74     Context.update(Challenge)
 75     Context.update(Magic2)
 76     aa=Context.digest()
 77     return b'S=' + binascii.hexlify(aa)[:40]
 78 def print_hex(s):
 79     for i in s:
 80         print('\%#x' % i, end='')
 81     print('')
 82 
 83 
 84 if __name__ == "__main__":
 85 
 86     UserName = 'WA_042'.encode("utf8")
 87     password = '123456'.encode("utf8")
 88     AuthenticatorChallenge = b'\x11\xD8\x2A\x82\x0F\xBE\xB0\x30\x73\xB1\xF7\x03\x73\x22\x26\xBF'
 89     PeerChalleng = b'\xC1\xD4\x43\x9D\xBD\x65\xA2\x74\xB9\x7A\xF0\x3F\x98\x00\x6D\x75'
 90     password_unicode = b''
 91     for ch in password:
 92         password_unicode += bytes([ch])
 93         password_unicode +=b'\x00'
 94     NTResponse=GenerateNTResponse(AuthenticatorChallenge, PeerChalleng, UserName, password_unicode)
 95     PasswordHashhash = Nt_password_hashhash(Nt_password_hash(password_unicode))
 96     AuthenticatorResponse=GenerateAuthenticatorResponse(password_unicode,NTResponse,PeerChalleng,AuthenticatorChallenge,UserName)
 97     print("用户名:",UserName)
 98     print("密码:",password)
 99     print("挑战响应值:")
100     print_hex(NTResponse)
101     print("AuthenticatorResponse: ",AuthenticatorResponse)
View Code

 

与全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!相似的内容:

全网首一份!你最需要的PPTP MS-CHAP V2 挑战响应编程模拟计算教程!代码基于RFC2759,附全部源码!

本文基于网络密码课上的实验 本来想水一水就过去,代码就网上找找,不行就GPT写,但是!一份都找不到,找到的代码都是跑不了的,总会是就是乱七八糟。所以准备认真的写一份。 代码编译成功的前提是要预先装好openssl库! 本随笔主要有三个内容: 编写程序,模拟计算NTResponse、Authentic

算法金 | 没有思考过 Embedding,不足以谈 AI

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 抱个拳,送个礼 在当今的人工智能(AI)领域,Embedding 是一个不可或缺的概念。如果你没有深入理解过 Embedding,那么就无法真正掌握 AI 的精髓。接下来,我们将深入探讨

算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法,它广泛应用于分类和回归问题中。它的核心思想是将复杂的决策过程分解成一系列简单的决策,通过不断地将数据集分割成更小的子集来进行预测。本文将带你详细了解决

接口设计的18条军规

前言 之前写过一篇文章《表设计的18条军规》,发表之前,在全网广受好评。 今天延续设计的话题,给大家总结了接口设计的18条军规,希望对你会有所帮助。 1. 签名 为了防止API接口中的数据被篡改,很多时候我们需要对API接口做签名。 接口请求方将请求参数 + 时间戳 + 密钥拼接成一个字符串,然后通

算法金 | 再见!!!KNN

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 KNN算法的工作原理简单直观,易于理解和实现,这使得它在各种应用场景中备受青睐。 我们将深入探讨KNN算法,从基本概念到实现细节,从算法优化到实际应用,我们都会一一展开。通过本文,你将了

[转帖]全球共有多少MySQL实例在运行?这里有一份数据

https://www.cnblogs.com/zhoujinyi/p/16377269.html 摘要 Shadowserver Foundation在5月31日发布了一份全网的MySQL扫描报告,共发现了暴露在公网的360万个MySQL实例。因为这份报告基数够大,而且信息也非常完整,从数据库专业

加密,各种加密,耙梳加密算法(Encryption)种类以及开发场景中的运用(Python3.10)

不用说火爆一时,全网热议的Web3.0区块链技术,也不必说诸如微信支付、支付宝支付等人们几乎每天都要使用的线上支付业务,单是一个简简单单的注册/登录功能,也和加密技术脱不了干系,本次我们耙梳各种经典的加密算法,试图描摹加密算法在开发场景中的运用技巧。 可逆加密算法(对称加密) 加密算法是一种将原始数

算法金 | 一个强大的算法模型,GPR !!

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 抱个拳,送个礼 高斯过程回归(GPR)是一种非参数化的贝叶斯方法,用于解决回归问题。与传统的线性回归模型不同,GPR 能够通过指定的核函数捕捉复杂的非线性关系,并提供不确定性的估计。在本

算法金 | 一个强大的算法模型:t-SNE !!

大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 t-SNE(t-Distributed Stochastic Neighbor Embedding)是一种用于降维和数据可视化的非线性算法。它被广泛应用于图像处理、文本挖掘和生物信息学等

算法金 | 一文看懂人工智能、机器学习、深度学习是什么、有什么区别!

大侠幸会,在下全网同名[算法金] 0 基础转 AI 上岸,多个算法赛 Top [日更万日,让更多人享受智能乐趣] 引言:走进智能的世界 曾经,人工智能(AI)是科幻小说中的概念,与飞船、外星人并肩而立。 然而,随着时间的推移,AI不再仅仅是幻想的产物,它已经成为我们日常生活中不可或缺的一部分。 在A