uboot添加密码保护功能
让boot更具安全性
linux在进入系统后,一般都有用户名和密码验证,可以有效的防止非授权用户获取操作权限。密码一般密存在/etc/passwd文件中。
但是我们常用的uboot、pmon等bootloader程序,很少看到有做密码保护功能。进入boot循环后,可以操作内核及文件系统分区,或则设置成其它的启动方式,一样可以绕过linux系统的保护功能,到达损坏或则窃取系统信息的目的,这个问题好像也一直没有引起行业的重视。
直到一次和友商的合作中,看到他们所有的平台都作了boot的密码保护功能,所以赶紧研究了以下把现有的平台都实现了bootloader的密码功能。
其实在CSDN网上也有博主分享了怎么实现uboot密码的功能,此功能的完善也是借鉴了很多同行的无私分享,在此表示感谢。
但是网上能够搜索到的资料有限,只讲了一些基础的实现方式,既然做了就一定要把他做好。所以参考了linux系统的密码实现方式,使用了MD5算法对密码进行保护,同时支持密码重置、超级密码等功能,通过算法保护的密码,即使对boot镜像进行dump打印,也是无法获取到原始密码的,所以这也是很多安全密码只允许重置,不允许找回的原因,因为就算是后台也不能通过密存的数据反推出原始密码。当然有些平台非法的操作就不保证了。
后来读了以下pmon的源码,这个boot是考虑了密码保护的,但是配置一番之后还是没有跑起来。所以也按照下文介绍的方式,写了一个减配版的pmon密码保护程序。
MD5介绍
MD5是一种单向散列函数,可以将任意长度的一段数据散列成固定长度,这样就保证了环境变量中存放的密码是固定长度的认证码,通过该认证码是无法反推出源数据的,这样就保证系统管理员甚至看到代码以及将执行文件dump出来也无法获取到超级密码以及用户密码。这样就大大提高了我们的boot程序以及系统的安全性。不过据说国外有教授已经破解了MD5的算法,不过用于我们普通的boot保护,也已经完全足够了。
添加uboot的密码保护后效果
用户敲击指定按键,打断uboot的引导,进入boot命令行,系统提示输入密码:
Hit SPACE key to stop autoboot: 0
Please input uboot password:
********
WARNING: Incorrect password, re-enter the password.
Please input uboot password:
输入错误密码(密码通过*号保护),系统提示密码错误重新输入密码;
如果不想继续输入密码,执行ctrl+c可以打断本次boot,uboot将重启,这样就可以正常引导系统,而不需要断电重启。
输入默认密码:admin123或者超级密码(boot代码中固定,且不可修改)可以直接进入boot命令行;如下所示:
Hit SPACE key to stop autoboot: 0
Please input uboot password:
********(输入的是默认密码admin123)
boot#
Hit SPACE key to stop autoboot: 0
Please input uboot password:
****************(输入的是超级密码)
boot#
进入命令行后执行printenv,或者printenv ubootpwd,可以看到密码是被*符号隐藏了的;因为密码是以环境变量的方式保存在flash中,所以需要对密码环境变量进行基础的保护,避免进入boot循环后就可以查看密码。
后来想想这个操作可能有点多余了,既然已经将密码通过MD5散列,那么直接展示散列值同样是可以起到隐藏密码的功能。后续按照我这个方法移植的,可以采用这个思路,而不用去搞很复杂的环境变量的隐藏工作。
boot# printenv
baudrate=115200
bootargs=console=ttyS1,115200 mem=256M@0x0 ip=off init=/linuxrc rootfstype=jffs2 root=/dev/mtdblock3 rw flashtype=nor
bootcmd=set uImage 0x80a00000; set dtb 0x83000000; sfcnor read 0x40000 0x600000 ${uImage}; sfcnor read 0x640000 0x20000 ${dtb}; bootm ${uImage} - ${dtb}
bootdelay=3
ethact=GMAC-9161
ethaddr=00:11:22:33:44:55
gatewayip=192.168.4.1
ipaddr=192.168.4.145
loads_echo=1
netmask=255.255.255.0
serverip=192.168.4.13
stderr=serial
stdin=serial
stdout=serial
ubootpwd=******** (可见密码内容及长度已被隐藏)
Environment size: 565/4092 bytes
boot#
boot# printenv ubootpwd
ubootpwd=******** (可见密码内容及长度已经被隐藏)
boot#
密码可以被修改,但是不能不删除(一般的删除环境变量的方式为:setenv env_name,不传入env_value则删除),使用setenv ubootpwd命令删除该环境变量,直接返回,不执行删除动作;
boot# setenv ubootpwd (删除ubootpwd环境变量命令)
boot# saveenv (执行保存)
save ok!!
boot# printenv
.
.
.
ubootpwd=******** (可以看到该环境变量任然存在)
.
.
.
Environment size: 565/4092 bytes
boot#
修改uboot密码的方式为:
setenv ubootpwd password
输入的password会被MD5散列成一段16字节长度的认证码,然后将该认证码转换为字符串后(16进制源数据中常包含‘\0’,不能作为环境变量存放),存放到环境变量中去。
boot# setenv ubootpwd password
密码无法找回,请牢记登录密码:password
执行<saveenv>命令使密码生效
boot# saveenv
save ok!!
boot#
密码修改成功,此时输入的password密码将被散列,散列的16位数据将以字符串的方式存放到环境变量中,下一次进行密码验证的时候,也是将键入的密码进行散列后进行字符串比对。
添加过程
先完成一个计算密码字符串MD5值的函数代码,后面的增加初始密码和超级密码也需要用的上。
- 计算初始MD5
这里只附上主程序代码,ctx16_md5函数和boot中添加的一样,后面附上。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ctx16_md5.h"
static void HexToStr(char *pszDest, char* pbSrc, int nLen)
{
char ddl,ddh;
int i;
for(i = 0; i < nLen; i++)
{
ddh = 48 + ((pbSrc[i] >> 4) & 0xf);
ddl = 48 + (pbSrc[i] &0xf);
if(ddh > 57)
ddh = ddh + 7;
if(ddl > 57)
ddl = ddl + 7;
pszDest[i*2] = ddh;
pszDest[i*2 + 1] = ddl;
}
pszDest[nLen * 2] = '\0';
}
static void StrToHex(char *pbDest, char *pbSrc, int nLen)
{
char h1, h2;
char s1, s2;
int i;
for(i = 0; i < nLen / 2; i++)
{
h1 = pbSrc[2*i];
h2 = pbSrc[2*i+1];
s1 = toupper(h1) - 0x30;
if(s1 > 9)
s1 -= 7;
s2 = toupper(h2) - 0x30;
if(s2 > 9)
s2 -= 7;
pbDest[i] = s1*16 + s2;
}
}
int main(int argc, char* argv[])
{
int ret;
MD5_CTX md5;
MD5Init(&md5);
unsigned char ctx_root[16] = {0};
unsigned char ctx_root_str[33] = {0};
MD5Update(&md5, argv[1], strlen(argv[1])); //如果argv[1]输入的特殊字符需要用'\'转义
MD5Final(&md5, ctx_root);
HexToStr(ctx_root_str, ctx_root, sizeof(ctx_root));
printf("%s\n", ctx_root_str);
return 0;
}
- 修改入口文件
主要入口修改文件:common/main.c中的readline函数;该函数的修改时为了打断uboot直接进入命令行,提供密码输入交互入口;
/*
* Prompt for input and read a line.
* If CONFIG_BOOT_RETRY_TIME is defined and retry_time >= 0,
* time out when time goes past endtime (timebase time in ticks).
* Return: number of read characters
* -1 if break
* -2 if timed out
*/
int readline (const char *const prompt)
{
#ifdef CONFIG_UBOOT_PWD
char pwd[64];
char c;
int index;
static int bPwd = 1;
unsigned long ts;
unsigned char ctx_root[16] = {0};
unsigned char ctx_root_str[33] = {0};
MD5_CTX md5;
while(bPwd)
{
puts("Please input uboot password:\n");
index = 0;
while((c = getc()) != '\r'){
if (c == 8) /*backspace*/
{
if(index > 0){
printf("\b \b");
index--;
}
continue;
}
else if (c == 3){
do_reset(NULL, 0, 0, NULL);
}
putc('*');
pwd[index] = c;
index++;
}
pwd[index] = '\0';
putc('\n');
char *s;
s = getenv("ubootpwd"); /*从环境变量中获取密码密文*/
if(!s){ /*如果没有该环境变量,则使用以下秘闻*/
s = "21232f297a57a5a743894a0e4a801fc3"; /*admin*/
}
/*通过输入的密码计算出MD5值*/
MD5Init(&md5);
MD5Update(&md5, pwd, strlen((char*)pwd));
MD5Final(&md5, ctx_root);
/*将MD5值转换成字符串*/
HexToStr(ctx_root_str, ctx_root, sizeof(ctx_root));
if(!strcmp(ctx_root_str,s)) //环境变量密码
{
bPwd = 0; //推出循环
}
else if(!strcmp(ctx_root_str,CONFIG_SUPER_UBOOTPWD)) //超级密码MD5值和输入密码的MD5值对比
{
bPwd = 0;
}
else{ //密码错误
puts("WARNING: Incorrect password, re-enter the password.\n\n");
udelay(50000);
}
}
#endif
/*
* If console_buffer isn't 0-length the user will be prompted to modify
* it instead of entering it from scratch as desired.
*/
console_buffer[0] = '\0';
return readline_into_buffer(prompt, console_buffer, 0);
}
以上函数中调用了HexToStr()16进制转字符串的函数,在测试代码中有实现,函数比较简单,直接实现在各自文件中就可以,不再独立提取出来,移植过程中有需要的地方复制到对应文件中即可。
- 添加配置文件
修改环境变量配置文件,路径如下:include/env_default.h
主要代码:
#ifdef CONFIG_UBOOT_PWD
"ubootpwd=" CONFIG_UBOOTPWD "\0"
#endif
该修改是为了让uboot的执行文件中包含默认的环境变量以及变量值,即ubootpwd=admin123
修改板级配置文件:include/config/目标板卡.h
#define CONFIG_UBOOT_PWD
#define CONFIG_UBOOTPWD "0192023A7BBD73250516F069DF18B500"
#define CONFIG_SUPER_UBOOTPWD "C357EC5A045A962AF062ED9341DAC732"
密码散列值就是用初始MD5程序计算就可以,可以测试CONFIG_UBOOTPWD是否为admin123散列计算后的值。
- 环境变量ubootpwd通过MD5散列加密
进入uboot的命令行,通过setenv命令,可以修改ubootpwd变量值,即修改了环境变量密码,此时ubootpwd这个变量值则是设置的uboot密码,设置的密码被MD5算法散列后,保存到环境变量中;
以下修改时为了对输入的ubootpwd环境变量的值进行MD5散列加密存储,同时禁止删除ubootpwd环境变量;
修改文件:common/cmd_nvedit.c文件下的_do_env_set ()函数
/*
* Set a new environment variable,
* or replace or delete an existing one.
*/
static int _do_env_set(int flag, int argc, char * const argv[])
{
int i, len;
char *name, *value, *s;
ENTRY e, *ep;
int env_flag = H_INTERACTIVE;
MD5_CTX md5;
debug("Initial value for argc=%d\n", argc);
while (argc > 1 && **(argv + 1) == '-') {
char *arg = *++argv;
--argc;
while (*++arg) {
switch (*arg) {
case 'f': /* force */
env_flag |= H_FORCE;
break;
default:
return CMD_RET_USAGE;
}
}
}
debug("Final value for argc=%d\n", argc);
name = argv[1];
value = argv[2];
if (strchr(name, '=')) {
printf("## Error: illegal character '='"
"in variable name \"%s\"\n", name);
return 1;
}
env_id++;
/* Delete only ? */
if (argc < 3 || argv[2] == NULL) {
if(!strncmp("ubootpwd",argv[1],strlen("ubootpwd"))) //密码不允许设空,"setenv ubootpwd" 命令不执行
return 0;
int rc = hdelete_r(name, &env_htab, env_flag);
return !rc;
}
/*
* Insert / replace new value
*/
for (i = 2, len = 0; i < argc; ++i)
len += strlen(argv[i]) + 1;
value = malloc(len);
if (value == NULL) {
printf("## Can't malloc %d bytes\n", len);
return 1;
}
for (i = 2, s = value; i < argc; ++i) {
char *v = argv[i];
while ((*s++ = *v++) != '\0')
;
*(s - 1) = ' ';
}
if (s != value)
*--s = '\0';
e.key = name;
e.data = value;
//检测到时设置密码,启动MD5计算
if(!strncmp("ubootpwd",e.key,strlen("ubootpwd")))
{
MD5Init(&md5);
unsigned char decrypt_hex[16];
unsigned char decrypt_str[33] = {0};
MD5Update(&md5,e.data,strlen((char*)e.data));
MD5Final(&md5,decrypt_hex);
HexToStr(decrypt_str, decrypt_hex, sizeof(decrypt_hex));
printf("密码无法找回,请牢记登录密码:%s\n",e.data);
printf("执行<saveenv>命令使密码生效\n");
//printf("密码哈希校验:%s\n",decrypt_str);
e.data = decrypt_str; //密存密码信息
}
hsearch_r(e, ENTER, &ep, &env_htab, env_flag);
free(value);
if (!ep) {
printf("## Error inserting \"%s\" variable, errno=%d\n",
name, errno);
return 1;
}
return 0;
}
- 隐藏环境变量中的ubootpwd变量值
进入boot命令行后,可以查看环境变量,所以需要同时隐藏ubootpwd这个变量值的长度和字符,所有秘密默认显示为“********”,不论是通过printenv 遍历还是 printenv ubootpwd直接读取,都显示为密码样式。
通过修改common/cmd_nvedit.c文件下的static int env_print()函数,可以在该函数下修改指定变量为ubootpwd时候的变量值显示,即执行printenv ubootpwd时,显示密码样式:
关键代码:
if (name) { /* print a single name */
ENTRY e, *ep;
e.key = name;
e.data = NULL;
hsearch_r(e, FIND, &ep, &env_htab, flag);
if (ep == NULL)
return 0;
if(strncmp("ubootpwd",e.key,strlen("ubootpwd")))
len = printf("%s=%s\n", ep->key, ep->data);
else
len = printf("%s=********\n", ep->key); //隐藏密码字符串以及密码长度
return len;
}
而当调用printenv命令显示所有环境变量值时,则需要修改哈希查找内部的函数,将哈希查找到的ubootpwd变量值保护起来;修改地址是:lib/hashtable.c
修改的关键函数是复制修改一个原有的函数:
ssize_t hexport_r_pwd(struct hsearch_data *htab, const char sep, int flag,
char **resp, size_t size,
int argc, char * const argv[])
if(0 == strncmp("ubootpwd", list[i]->key, strlen("ubootpwd")))
{
s = "********"; //隐藏密码字符串以及密码长度
while (*s) {
if ((*s == sep) || (*s == '\\'))
*p++ = '\\'; /* escape */
{
*p++ = *s++;
}
}
}
把以关于MD5的代码,添加到common/路径下,并修改Makefile,选择CONFIG_UBOOT_PWD条件编译。
ctx16_md5.c
/*#include <string.h>*/
#include "ctx16_md5.h"
unsigned char PADDING[]={0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
void MD5Init(MD5_CTX *context)
{
context->count[0] = 0;
context->count[1] = 0;
context->state[0] = 0x67452301;
context->state[1] = 0xEFCDAB89;
context->state[2] = 0x98BADCFE;
context->state[3] = 0x10325476;
}
void MD5Update(MD5_CTX *context,unsigned char*input,unsigned int inputlen)
{
unsigned int i = 0,index = 0,partlen = 0;
index = (context->count[0] >> 3) & 0x3F;
partlen = 64 - index;
context->count[0] += inputlen << 3;
if(context->count[0] < (inputlen << 3))
context->count[1]++;
context->count[1] += inputlen >> 29;
if(inputlen >= partlen)
{
memcpy(&context->buffer[index],input,partlen);
MD5Transform(context->state,context->buffer);
for(i = partlen;i+64 <= inputlen;i+=64)
MD5Transform(context->state,&input[i]);
index = 0;
}
else
{
i = 0;
}
memcpy(&context->buffer[index],&input[i],inputlen-i);
}
void MD5Final(MD5_CTX *context,unsigned char digest[16])
{
unsigned int index = 0,padlen = 0;
unsigned char bits[8];
index = (context->count[0] >> 3) & 0x3F;
padlen = (index < 56)?(56-index):(120-index);
MD5Encode(bits,context->count,8);
MD5Update(context,PADDING,padlen);
MD5Update(context,bits,8);
MD5Encode(digest,context->state,16);
}
void MD5Encode(unsigned char*output,unsigned int*input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[j] = input[i] & 0xFF;
output[j+1] = (input[i] >> 8) & 0xFF;
output[j+2] = (input[i] >> 16) & 0xFF;
output[j+3] = (input[i] >> 24) & 0xFF;
i++;
j+=4;
}
}
void MD5Decode(unsigned int*output,unsigned char*input,unsigned int len)
{
unsigned int i = 0,j = 0;
while(j < len)
{
output[i] = (input[j]) |
(input[j+1] << 8) |
(input[j+2] << 16) |
(input[j+3] << 24);
i++;
j+=4;
}
}
void MD5Transform(unsigned int state[4],unsigned char block[64])
{
unsigned int a = state[0];
unsigned int b = state[1];
unsigned int c = state[2];
unsigned int d = state[3];
unsigned int x[64];
MD5Decode(x,block,64);
FF(a, b, c, d, x[ 0], 7, 0xd76aa478);/* 1 */
FF(d, a, b, c, x[ 1], 12, 0xe8c7b756);/* 2 */
FF(c, d, a, b, x[ 2], 17, 0x242070db);/* 3 */
FF(b, c, d, a, x[ 3], 22, 0xc1bdceee);/* 4 */
FF(a, b, c, d, x[ 4], 7, 0xf57c0faf);/* 5 */
FF(d, a, b, c, x[ 5], 12, 0x4787c62a);/* 6 */
FF(c, d, a, b, x[ 6], 17, 0xa8304613);/* 7 */
FF(b, c, d, a, x[ 7], 22, 0xfd469501);/* 8 */
FF(a, b, c, d, x[ 8], 7, 0x698098d8);/* 9 */
FF(d, a, b, c, x[ 9], 12, 0x8b44f7af);/* 10 */
FF(c, d, a, b, x[10], 17, 0xffff5bb1);/* 11 */
FF(b, c, d, a, x[11], 22, 0x895cd7be);/* 12 */
FF(a, b, c, d, x[12], 7, 0x6b901122);/* 13 */
FF(d, a, b, c, x[13], 12, 0xfd987193);/* 14 */
FF(c, d, a, b, x[14], 17, 0xa679438e);/* 15 */
FF(b, c, d, a, x[15], 22, 0x49b40821);/* 16 */
/* Round 2 */
GG(a, b, c, d, x[ 1], 5, 0xf61e2562);/* 17 */
GG(d, a, b, c, x[ 6], 9, 0xc040b340);/* 18 */
GG(c, d, a, b, x[11], 14, 0x265e5a51);/* 19 */
GG(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);/* 20 */
GG(a, b, c, d, x[ 5], 5, 0xd62f105d);/* 21 */
GG(d, a, b, c, x[10], 9, 0x2441453);/* 22 */
GG(c, d, a, b, x[15], 14, 0xd8a1e681);/* 23 */
GG(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);/* 24 */
GG(a, b, c, d, x[ 9], 5, 0x21e1cde6);/* 25 */
GG(d, a, b, c, x[14], 9, 0xc33707d6);/* 26 */
GG(c, d, a, b, x[ 3], 14, 0xf4d50d87);/* 27 */
GG(b, c, d, a, x[ 8], 20, 0x455a14ed);/* 28 */
GG(a, b, c, d, x[13], 5, 0xa9e3e905);/* 29 */
GG(d, a, b, c, x[ 2], 9, 0xfcefa3f8);/* 30 */
GG(c, d, a, b, x[ 7], 14, 0x676f02d9);/* 31 */
GG(b, c, d, a, x[12], 20, 0x8d2a4c8a);/* 32 */
/* Round 3 */
HH(a, b, c, d, x[ 5], 4, 0xfffa3942);/* 33 */
HH(d, a, b, c, x[ 8], 11, 0x8771f681);/* 34 */
HH(c, d, a, b, x[11], 16, 0x6d9d6122);/* 35 */
HH(b, c, d, a, x[14], 23, 0xfde5380c);/* 36 */
HH(a, b, c, d, x[ 1], 4, 0xa4beea44);/* 37 */
HH(d, a, b, c, x[ 4], 11, 0x4bdecfa9);/* 38 */
HH(c, d, a, b, x[ 7], 16, 0xf6bb4b60);/* 39 */
HH(b, c, d, a, x[10], 23, 0xbebfbc70);/* 40 */
HH(a, b, c, d, x[13], 4, 0x289b7ec6);/* 41 */
HH(d, a, b, c, x[ 0], 11, 0xeaa127fa);/* 42 */
HH(c, d, a, b, x[ 3], 16, 0xd4ef3085);/* 43 */
HH(b, c, d, a, x[ 6], 23, 0x4881d05);/* 44 */
HH(a, b, c, d, x[ 9], 4, 0xd9d4d039);/* 45 */
HH(d, a, b, c, x[12], 11, 0xe6db99e5);/* 46 */
HH(c, d, a, b, x[15], 16, 0x1fa27cf8);/* 47 */
HH(b, c, d, a, x[ 2], 23, 0xc4ac5665);/* 48 */
/* Round 4 */
II(a, b, c, d, x[ 0], 6, 0xf4292244);/* 49 */
II(d, a, b, c, x[ 7], 10, 0x432aff97);/* 50 */
II(c, d, a, b, x[14], 15, 0xab9423a7);/* 51 */
II(b, c, d, a, x[ 5], 21, 0xfc93a039);/* 52 */
II(a, b, c, d, x[12], 6, 0x655b59c3);/* 53 */
II(d, a, b, c, x[ 3], 10, 0x8f0ccc92);/* 54 */
II(c, d, a, b, x[10], 15, 0xffeff47d);/* 55 */
II(b, c, d, a, x[ 1], 21, 0x85845dd1);/* 56 */
II(a, b, c, d, x[ 8], 6, 0x6fa87e4f);/* 57 */
II(d, a, b, c, x[15], 10, 0xfe2ce6e0);/* 58 */
II(c, d, a, b, x[ 6], 15, 0xa3014314);/* 59 */
II(b, c, d, a, x[13], 21, 0x4e0811a1);/* 60 */
II(a, b, c, d, x[ 4], 6, 0xf7537e82);/* 61 */
II(d, a, b, c, x[11], 10, 0xbd3af235);/* 62 */
II(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);/* 63 */
II(b, c, d, a, x[ 9], 21, 0xeb86d391);/* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
}
ctx16_md5.h
#ifndef CTX16_MD5_H
#define CTX16_MD5_H
typedef struct
{
unsigned int count[2];
unsigned int state[4];
unsigned char buffer[64];
}MD5_CTX;
#define F(x,y,z) ((x & y) | (~x & z))
#define G(x,y,z) ((x & z) | (y & ~z))
#define H(x,y,z) (x^y^z)
#define I(x,y,z) (y ^ (x | ~z))
#define ROTATE_LEFT(x,n) ((x << n) | (x >> (32-n)))
#define FF(a,b,c,d,x,s,ac) \
{ \
a += F(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define GG(a,b,c,d,x,s,ac) \
{ \
a += G(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define HH(a,b,c,d,x,s,ac) \
{ \
a += H(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
#define II(a,b,c,d,x,s,ac) \
{ \
a += I(b,c,d) + x + ac; \
a = ROTATE_LEFT(a,s); \
a += b; \
}
void MD5Init(MD5_CTX *context);
void MD5Update(MD5_CTX *context,unsigned char *input,unsigned int inputlen);
void MD5Final(MD5_CTX *context,unsigned char digest[16]);
void MD5Transform(unsigned int state[4],unsigned char block[64]);
void MD5Encode(unsigned char* output,unsigned int *input,unsigned int len);
void MD5Decode(unsigned int* output,unsigned char *input,unsigned int len);
#endif
后续
Pmon行业用的比较少,等哪天整理出来重新写一片文章来介绍。也可以参考以上思路,完成一个基本的密码保护功能完全足够。
标签:uboot,ubootpwd,int,密码保护,unsigned,char,密码,添加,MD5 From: https://blog.csdn.net/weixin_41972926/article/details/144259419