经典的加解密算法有AES,DES,RSA等,通过加密我们可以保护我们的数据。

windows自带加密库

windows提供了一组CryptoAPI来对用户的敏感私钥数据提供保护,并以灵活的方式对数据进行加密或签名。接下来介绍HASH值计算、AES加解密、RSA加解密。
CSP:加密服务提供程序

HASH值的计算

Hash算法是一种单向加密算法,HASH就是把任意长度的输入通过HASH算法变换成固定长度的输出,该值就是HASH值。HASH值的空间远小于输入值的空间,不同的输入可能得到相同的输出,所以无法利用HASH值来确定唯一的输入值。基于这种特性,HASH值一般用来执行数据完整性校验。如MD5,SHA等。

实现过程

//用于获取指定CSP内特定密钥容器的句柄。
BOOL CryptAcquireContext(
HCRYPTPROV *phProv,//指向csp句柄的指针,通过CryptReleaseContext释放。
LPCSTR szContainer,//密钥容器名称,一般设置NULL
LPCSTR szProvider,//包含要使用CSP名称的空终止字符串,一般设置NULL。
DWORD dwProvType,//指定要获取提供程序的类型,如PROV_RSA_AES支持RSA签名算法,AES加密算法以及HASH算法。
DWORD dwFlags);//标志值,一般设置0。//创建一个空HASH对象
BOOL CryptCreateHash(
HCRYPTPROV hProv,//调用CryptAcquireContext创建的CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值。
HCRYPTKEY  hKey,//对于非键控算法,设置为0.
DWORD dwFlags,//通常为0.
HCRYPTHASH *phHash);//HASH对象句柄。//将数据添加到HASH对象,并进行HASH计算。
BOOL WINAPI CryptHashData(
HCRYPTHASH hHash, //HASH对象句柄
BYTE* pbData,//指向要添加到HASH对象数据缓冲区的指针
DWORD dwDataLen, //要添加到HASH对象缓冲区的指针
DWORD dwFlags//通常为0
);//从HASH对象获取指定参数值
BOOL CRYPTFUNC CryptGethashParam(
HCRYPTHASH hHash,//HASH对象句柄
DWORD dwParam, //查询类型 HP_ALGID,HP_HASHSIZE,HP_HASHVAL。
BYTE *pbData, //指向接受指定值的数据缓冲区的指针。
DWORD *pdwDataLen, //指向指定的pdData缓冲区大小的DWORD值得指针,
DWORD dwFlags//0
);//释放HASH对象句柄
BOOL CRYPTFUNC CryptDestroyHash(
HCRYPTHASH hHash
);//释放CSP句柄
BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlag
);

1 任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄。因为这里是计算数据HASH值,所以将提供程序类型设置为PROV_RSA_AES,该类型支持常用HASH算法。
2 调用CryptCreateHash在CSP中创建一个空的HASH对象并获取句柄,并可以指定HASH对象使用的HASH算法,如CALG_MD5表示MD5 HASH算法。
3 调用CryptGetHashParam从HASH对象获取指定参数,可以获取的参数有三种:
HP_ALGID:HASH算法
HP_HASHSIZE:HASH值的数据长度
HP_HASHVAL:HASH值
因为HASH算法不同,HASH值大小不固定,所以在获取HASH值之前,需要先获取HASH值长度,并申请足够的缓冲区存放HASH值。

编码实现

/*
Function:HASH值计算
pData:需要计算的内容
dwDataLength:内容长度
algHashType:HASH算法类型 如CALG_MD5,CALG_SHA1,CALG_SHA256等
ppHashData:存放计算的的HASH值
pdwHashDataLength:计算的HASH值长度
*/
BOOL CalculateHash(BYTE *pData, DWORD dwDataLength, ALG_ID algHashType, BYTE **ppHashData, DWORD *pdwHashDataLength)
{HCRYPTPROV hCryptProv = NULL;//CSP句柄HCRYPTHASH hCryptHash = NULL;//HASH对象句柄BYTE *pHashData = NULL;DWORD dwHashDataLength = 0;DWORD dwTemp = 0;BOOL bRet = FALSE;do{// 获得指定CSP的密钥容器的句柄        bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 创建一个HASH对象, 指定HASH算法        bRet = ::CryptCreateHash(hCryptProv, algHashType, NULL, NULL, &hCryptHash);if (FALSE == bRet){ShowError("CryptCreateHash");break;}// 计算HASH数据bRet = ::CryptHashData(hCryptHash, pData, dwDataLength, 0);if (FALSE == bRet){ShowError("CryptHashData");break;}// 获取HASH结果的大小dwTemp = sizeof(dwHashDataLength);bRet = ::CryptGetHashParam(hCryptHash, HP_HASHSIZE, (BYTE *)(&dwHashDataLength), &dwTemp, 0);if (FALSE == bRet){ShowError("CryptGetHashParam");break;}// 申请内存pHashData = new BYTE[dwHashDataLength];if (NULL == pHashData){bRet = FALSE;ShowError("new");break;}::RtlZeroMemory(pHashData, dwHashDataLength);// 获取HASH结果数据bRet = ::CryptGetHashParam(hCryptHash, HP_HASHVAL, pHashData, &dwHashDataLength, 0);if (FALSE == bRet){ShowError("CryptGetHashParam");break;}// 返回数据*ppHashData = pHashData;*pdwHashDataLength = dwHashDataLength;} while (FALSE);// 释放关闭if (FALSE == bRet){if (pHashData){delete[]pHashData;pHashData = NULL;}}if (hCryptHash){::CryptDestroyHash(hCryptHash);}if (hCryptProv){ ::CryptReleaseContext(hCryptProv, 0);}return bRet;
}

测试结果

AES加解密

AES是最常见的对称加密算法,所谓对称加密算法就是加密和解密使用相同密钥的加密算法。AES为分组密码,就是把明文分成一组一组的,每组长度相同,每次加密一组数据,直到加密完整个明文。AES对称加密算法的优势在于算法公开,计算量小,加密效率高。
在AES标准规范中,分组长度只能是128位(16个字节)。密钥长度可以是:
128:CALG_AES_128(16字节)
192:CALG_AES_192(24字节)
256:CALG_AES_256(32字节)

实现过程

//CryptDeriveKey:生成从基础数据值(可以是密码或者别的)派生出的加密会话密钥。
BOOL CRYPTFUNC CryptDeriveKey(
HCRYPTPROV hProv,//CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值
HCRYPTHASH hBaseData,//HASH对象句柄
DWORD dwFlags,//指定生成密钥的类型
HCRYPTKEY* phKey);//加密会话密钥//对数据进行加密
BOOL CRYPTFUNC CryptEncrypt
(HCRYPTKEY hKey,//加密会话密钥
HCRYPTHASH hHash,//HASH对象句柄
BOOL Final,//指定它是否是加密系列中的最后一部分
DWORD dwFlags, //0
BYTE* pbData,//指向要加密的明文缓冲区的指针,该缓冲区文本会被生成的密文所覆盖。
DWORD* pdwDataLen,//pbData缓冲区中明文长度
DWORD dwBufLen//指定输入pbData缓冲区的总大小
);BOOL CryptDecrypt(
HCRYPTKEY  hKey,//加密会话密钥
HCRYPTHASH hHash,//HASH对象句柄
BOOL       Final,//指定它是否是加密系列中的最后一部分
DWORD      dwFlags,//0
BYTE       *pbData,//指向要解密的明文缓冲区的指针,该缓冲区文本会被生成的明文所覆盖。
DWORD      *pdwDataLen//pbData缓冲区的总大小
);//释放密钥句柄
BOOL CRYPTFUNC CryptDestroyKey(HCRYPTKEY hKey);

1 任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄.由于这里使用的是AES对称加密算法,所以将提供程序类型设置为PROV_RSA_AES,该类型支持AES算法。
2 下面并不直接使用明文密码作为AES的加密密码,而是把明文密码的MD5值作为密码,再调用CryptDeriveKey函数来派生出AES的加密密钥。先CryptCreateHash创建HASH对象,然后CryptHashData对传入的密码进行MD5值计算,再CryptDeriveKey通过MD5来派生密钥,CALG_AES_128表示用于128位AES加密。
3 派生完密钥之后,可以调用CryptEncrypt来根据派生密钥中指定的加密算法进行加密。

AES加密编码实现

/*
Function:AES加密
pPassword:加密密码
dwPasswordLength:密码长度
pData:指向要加密的明文缓冲区的指针
dwDataLength:pData缓冲区中明文的长度
dwBufferLength:pData缓冲区总大小:大于明文长度,因为加密完成后密文也会写道这个缓冲区。
*/
BOOL AesEncrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{BOOL bRet = TRUE;HCRYPTPROV hCryptProv = NULL;//CSP句柄HCRYPTHASH hCryptHash = NULL;//HASH对象句柄HCRYPTKEY hCryptKey = NULL;//加密密钥句柄do{// 获取CSP句柄//CryptAcquireContext:用于获取指定CSP内特定密钥容器的句柄。bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 创建HASH对象bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);if (FALSE == bRet){ShowError("CryptCreateHash");break;}// 对密钥进行HASH计算bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);if (FALSE == bRet){ShowError("CryptHashData");break;}// 使用HASH来生成密钥bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);if (FALSE == bRet){ShowError("CryptDeriveKey");break;}// 加密数据//CryptEncrypt:由CSP模块保存的密钥指定的加密算法来加密数据。//TRUE:表示该加密是AES加密数据中最后一组,这样系统会自动按照分组长度对数据进行填充并计算。bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);if (FALSE == bRet){ShowError("CryptEncrypt");break;}} while (FALSE);// 关闭释放if (hCryptKey){::CryptDestroyKey(hCryptKey);//释放密钥句柄}if (hCryptHash){::CryptDestroyHash(hCryptHash);//释放HASH对象句柄}if (hCryptProv){::CryptReleaseContext(hCryptProv, 0);//释放CSP句柄}return bRet;
}

由于AES是对称加密,所以加密解密都是同一个密码。为了获取相同的解密密钥,需要根据明文密码来计算出派生密钥。过程与上面相同。

AES解码编码实现

/*
Function:AES解密
pPassword:解密密码
dwPasswordLength:密码长度
pData:指向要解密的明文缓冲区的指针
dwDataLength:pData缓冲区中明文的长度
dwBufferLength:pData缓冲区总大小:大于明文长度,因为加密完成后密文也会写道这个缓冲区。
*/
BOOL AesDecrypt(BYTE *pPassword, DWORD dwPasswordLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{BOOL bRet = TRUE;HCRYPTPROV hCryptProv = NULL;HCRYPTHASH hCryptHash = NULL;HCRYPTKEY hCryptKey = NULL;do{// 获取CSP句柄bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 创建HASH对象bRet = ::CryptCreateHash(hCryptProv, CALG_MD5, NULL, 0, &hCryptHash);if (FALSE == bRet){ShowError("CryptCreateHash");break;}// 对密钥进行HASH计算bRet = ::CryptHashData(hCryptHash, pPassword, dwPasswordLength, 0);if (FALSE == bRet){ShowError("CryptHashData");break;}// 使用HASH来生成密钥bRet = ::CryptDeriveKey(hCryptProv, CALG_AES_128, hCryptHash, CRYPT_EXPORTABLE, &hCryptKey);if (FALSE == bRet){ShowError("CryptDeriveKey");break;}// 解密数据bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);if (FALSE == bRet){ShowError("CryptDecrypt");break;}} while (FALSE);// 关闭释放if (hCryptKey){::CryptDestroyKey(hCryptKey);}if (hCryptHash){::CryptDestroyHash(hCryptHash);}if (hCryptProv){::CryptReleaseContext(hCryptProv, 0);}return bRet;
}

测试结果

RSA加解密

RSA是一种非对称加密算法,加密密钥和解密密钥不相同。RSA算法的安全性非常高,极大整数进行因数分解的难度决定了RSA算法的可靠性。但是由于RSA进行的都是大数运算,所以RSA速度很慢,是同安全级别的对称密码算法的1/1000左右,一般RSA用于少量数据加密。

实现过程

//随机生成加密会话密钥或公钥/私钥对,密钥或密钥对的句柄在phKey返回。
BOOL CRYPTFUNC CryptGenKey(
HCRYPTPROV  hProv,//CSP句柄
ALG_ID Algid,//标识要使用的HASH算法的ALG_ID值
DWORD dwFlags,//指定生成的密钥类型
HCRYPTKEY* phKey//密钥句柄
);//以安全的方式从加密服务提供程序中导出加密密钥或密钥对。
BOOL CRYPTFUNC CryptExportKey(
HCRYPTKEY hKey,//指向要导出的密钥句柄
HCRYPTKEY hExpKey,//指向目标用户的密钥句柄,通常为NULL
DWORD dwBlobType,//指定要在pdData中导出键BLOB的类型。PUBLICKEYBLOB表示导出公钥,PRIVATEKEYBLOB表示导出私钥。
DWORD dwFlags,//0
BYTE* pbData,//指向接收关键BLOB数据的缓冲区的指针
DWORD* pdwbDataLen//pddate缓冲区大小
);//将加密密钥从密钥 BLOB传输到加密服务提供程序(CSP)
BOOL CryptImportKey(
HCRYPTPROV hProv,//CSP句柄
const BYTE *pbData,//指定一个BYTE数组,此密钥BOLO由CryptExportKey创建
DWORD dwDataLen,//包含关键BLOB长度
HCRYPTKEY  hPubKey,//解密存储在pdData中的加密密钥句柄
DWORD  dwFlags,//目前仅在将PRIVATEKEYBLOB形式的公钥/私钥对导入 CSP 时使用
HCRYPTKEY  *phKey//指向接受导入键句柄的指针
);

通常情况下,公钥用来加密数据,私钥用来解密数据,因此在数据加解/密前,需要先产生公钥和私钥密钥对。公钥和私钥成对,使用公钥加密的数据只能用唯一的私钥来解密。

1 首先,任何程序在使用CryptoAPI函数来执行数据计算或是加/解密之前,都需要调用CryptAcquireContext来获取CSP句柄.由于这里使用的是RSA非对称加密算法,所以将提供程序类型设置为PROV_RSA_FULL,该类型支持RSA算法。
2 调用CryptGenKey函数随机生成AT_KEYEXCHANGE交换密钥对,并设置生成的密钥对类型为CRYPT_EXPORTABLE,它是可导出的,可以使用CryptExportKey导出密钥。此时RSA密钥对已经生成。

编码实现

/*
Function:生成公钥和私钥
ppPublicKey:公钥
pdwPublicKeyLength:公钥长度
ppPrivateKey:私钥
pdwPrivateKeyLength:私钥长度
*/
BOOL GenerateKey(BYTE **ppPublicKey, DWORD *pdwPublicKeyLength, BYTE **ppPrivateKey, DWORD *pdwPrivateKeyLength)
{BOOL bRet = TRUE;HCRYPTPROV hCryptProv = NULL;//CSP句柄HCRYPTKEY hCryptKey = NULL;//密钥句柄BYTE *pPublicKey = NULL;//公钥内容DWORD dwPublicKeyLength = 0;//公钥长度BYTE *pPrivateKey = NULL;//私钥内容DWORD dwPrivateKeyLength = 0;//私钥长度do{// 获取CSP句柄bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 生成公私密钥对bRet = ::CryptGenKey(hCryptProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hCryptKey);if (FALSE == bRet){ShowError("CryptGenKey");break;}// 获取公钥密钥的长度和内容bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, NULL, &dwPublicKeyLength);if (FALSE == bRet){ShowError("CryptExportKey");break;}pPublicKey = new BYTE[dwPublicKeyLength];::RtlZeroMemory(pPublicKey, dwPublicKeyLength);bRet = ::CryptExportKey(hCryptKey, NULL, PUBLICKEYBLOB, 0, pPublicKey, &dwPublicKeyLength);if (FALSE == bRet){ShowError("CryptExportKey");break;}// 获取私钥密钥的长度和内容bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, NULL, &dwPrivateKeyLength);if (FALSE == bRet){ShowError("CryptExportKey");break;}pPrivateKey = new BYTE[dwPrivateKeyLength];::RtlZeroMemory(pPrivateKey, dwPrivateKeyLength);bRet = ::CryptExportKey(hCryptKey, NULL, PRIVATEKEYBLOB, 0, pPrivateKey, &dwPrivateKeyLength);if (FALSE == bRet){ShowError("CryptExportKey");break;}// 返回数据*ppPublicKey = pPublicKey;*pdwPublicKeyLength = dwPublicKeyLength;*ppPrivateKey = pPrivateKey;*pdwPrivateKeyLength = dwPrivateKeyLength;} while (FALSE);// 释放关闭if (hCryptKey){::CryptDestroyKey(hCryptKey);}if (hCryptProv){::CryptReleaseContext(hCryptProv, 0);}return bRet;
}/*
Function:公钥加密数据
*/
BOOL RsaEncrypt(BYTE *pPublicKey, DWORD dwPublicKeyLength, BYTE *pData, DWORD &dwDataLength, DWORD dwBufferLength)
{BOOL bRet = TRUE;HCRYPTPROV hCryptProv = NULL;HCRYPTKEY hCryptKey = NULL;do{// 获取CSP句柄bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 导入公钥bRet = ::CryptImportKey(hCryptProv, pPublicKey, dwPublicKeyLength, NULL, 0, &hCryptKey);if (FALSE == bRet){ShowError("CryptImportKey");break;}// 加密数据bRet = ::CryptEncrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength, dwBufferLength);if (FALSE == bRet){ShowError("CryptImportKey");break;}} while (FALSE);// 释放并关闭if (hCryptKey){::CryptDestroyKey(hCryptKey);}if (hCryptProv){::CryptReleaseContext(hCryptProv, 0);}return bRet;
}/*
Function:私钥解密数据
*/
BOOL RsaDecrypt(BYTE *pPrivateKey, DWORD dwProvateKeyLength, BYTE *pData, DWORD &dwDataLength)
{BOOL bRet = TRUE;HCRYPTPROV hCryptProv = NULL;HCRYPTKEY hCryptKey = NULL;do{// 获取CSP句柄bRet = ::CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0);if (FALSE == bRet){ShowError("CryptAcquireContext");break;}// 导入私钥bRet = ::CryptImportKey(hCryptProv, pPrivateKey, dwProvateKeyLength, NULL, 0, &hCryptKey);if (FALSE == bRet){ShowError("CryptImportKey");break;}// 解密数据bRet = ::CryptDecrypt(hCryptKey, NULL, TRUE, 0, pData, &dwDataLength);if (FALSE == bRet){ShowError("CryptDecrypt");break;}} while (FALSE);// 释放并关闭if (hCryptKey){::CryptDestroyKey(hCryptKey);}if (hCryptProv){::CryptReleaseContext(hCryptProv, 0);}return bRet;
}

测试结果

windows黑客编程技术之加密技术:windows自带的加密库相关推荐

  1. mfc编程vc6.0实现进程的创建和通信_免费送书:windows黑客编程技术详解

    01 书怎么送 点赞并留言,关注在下面的公众号后台回复「抽奖」,弹出小程序后点击参与. 开奖时间是 7 月 7 号 20:00 ,一定要留意微信消息,如果你中奖了,请尽快在中奖页面提交收件人信息并备注 ...

  2. Windows黑客编程基础

    俗话说:"万事开头难",编程也不例外,初学者如何入门关键要有一份正确的理论作指 导,下面的这篇文章虽不能说是至理名言,但我相信通过作者细腻的分析.讲解和引导, 定能给初学者起到启蒙 ...

  3. win11文件夹怎么加密?win11系统自带文件加密的方法步骤

    win11文件夹怎么加密?win11系统自带文件加密的方法步骤!升级Win11系统之后,因为换了新的系统,界面变了,设置改变了许多,所以很多功能不会使用了.比如今天要给大家说的,win11怎么给文件夹 ...

  4. windows黑客编程技术之隐藏技术(进程伪装,傀儡进程,进程隐藏)

    进程伪装:通过修改指定进程PEB中的路径和命令行信息实现伪装. 傀儡进程:通过进程挂起,替换内存数据再恢复执行,从而实现创建"傀儡进程". 进程隐藏:通过HOOK函数ZwQuery ...

  5. C++黑客编程:键盘记录器,HOOK技术实现

    有一种技术被称为HOOK,人们习惯上叫做钩子.钩子技术的应用范围比较广:输入监控,API拦截,消息捕获等等. 今天我们来做的是键盘记录器 编译工具:visual studio 2019 编程语言:自然 ...

  6. windows黑客编程系列(十一):按键记录

    文章目录 功能技术模块 按键记录 WINAPI RegisterRawInputDevices tagRAWINPUTDEVICE结构体 GetRawInputData 编码实现 功能技术模块 病毒木 ...

  7. windows黑客编程系列(四):修改注册表键值对之自启动

    文章目录 自启动技术 注册表 WINAPI介绍 RegOpenKeyEx函数 参数说明 返回值 RegSetValueEx 参数说明 返回值 编码 运行效果 自启动技术 对于一个病毒木马来说,重要的不 ...

  8. windows黑客编程系列(八):以固定频率获取屏幕截图

    文章目录 功能技术模块 桌面截屏 WIN API GetDC BitBlt ICONINFO结构体 编码实现 运行效果 功能技术模块 病毒木马的入侵并潜伏在用户计算机上总是有着某种目的,例如获取用户隐 ...

  9. windows黑客编程系列(九):使用ntdll.dll中并未公开的API进行压缩

    文章目录 压缩技术 利用WIN32自带API实现数据压缩 WIN API RtlGetCompressionWorkSpaceSize RtlCompressBuffer RtlDecompressB ...

最新文章

  1. 查看 python 的版本 以及 python 的安装位置路径
  2. 河套酒业集团远程应用K/3系统案例解析
  3. pandas常见的时间处理函数
  4. 中望cad插件_中望软件与狄诺尼达成战略合作,打造交通行业全国产CAD解决方案...
  5. mysql5717开发设置怎么调回来_Window 下安装Mysql5.7.17 及设置编码为utf8的方法
  6. Android 驱动(9)----设备树(一)linux内核主线了解dts
  7. 三方协议接收节点不存在_【花开法务】没有保密协议是否意味着员工不存在保密义务?...
  8. maven学习(3)
  9. Ubuntu 安装极点五笔 for ibus
  10. android 圆角图片 imageview,【android 图片圆角设定】CustomImageView简单一览
  11. mac虚拟摄像头开发
  12. scrapy---下载中国大学慕课课程视频及文件
  13. 清华姚班程序员,网上征婚被骂?
  14. Error while extracting response for type [class xxx] and content type application/xml;charset=UTF-8
  15. 2019年春节春晚红包大战战报!30亿!
  16. 普通壳的脱壳方法和脱壳技巧
  17. 神经网络深度(Deepth)的影响
  18. 从开发转到安全渗透工程师,是我做的最对的决定
  19. 基于python和tkinter实现的随机点名程序
  20. 每日一题之后缀表达式

热门文章

  1. HarmonyOS+Django登录页面
  2. gtav登录请确认不是机器人_关于GTA5登录要接收R星验证码
  3. 第105篇自嗨日记(扶摇生财思维)
  4. 效率提升5倍不止,弘玑RPA助力上海自贸区临港新片区政务一体化平台
  5. 真正通俗易懂让你搞懂Javascript 执行机制
  6. Effective C++:改善程序与设计的55个具体做法
  7. 重启计算机登录另外的本地账户,Win10退出Microsoft账户切换回本地帐户
  8. 独守千秋——感悟王安石
  9. JAVA入门学习 —方法使用
  10. 浮点数的表示及相关知识详解