openssl3.2 - exp - 用base64后的字符串作为配置项的值

文章目录

    • openssl3.2 - exp - 用base64后的字符串作为配置项的值
    • 概述
    • 笔记
      • 配置项的值长度有限制
    • 配置项的值不能是base64之后的直接值,需要处理之后才行。
    • openssl配置项的值并不是所有可见字符都可以
    • 例子
    • 现在用的base64的类
      • cipher_base64.h
      • cipher_base64.cpp
    • 现在用的openssl配置文件读写类
      • CMyOsslConfig.h
      • CMyOsslConfig.cpp
      • CMyOsslMem.h
      • CMyOsslMem.cpp
    • END

openssl3.2 - exp - 用base64后的字符串作为配置项的值

概述

今天解析自己封装的内存型的openssl 配置数据,发现openssl并不支持配置项的值为base64之后的直接字符串。

笔记

配置项的值长度有限制

配置项的值(字符串)的长度好像有512字节的限制,具体没实验。看源码能看出来。

bool CMyOsslConfig::updateConfig()
{
	bool b_rc = false;
	int i_rc = 0;
	long lineSn = 0;
	int len1 = 0;
	int len2 = 0;

	do {
		if ((NULL == m_pMyOsslMem) || (NULL == m_conf))
		{
			break;
		}

		// BIO_seek(m_bio, 0);
		// len1 = this->bio_get_length(m_bio);
		assert(NULL != m_pMyOsslMem->get_bio());
		i_rc = NCONF_load_bio(m_conf, m_pMyOsslMem->get_bio(), &lineSn); // !
		// len2 = this->bio_get_length(m_bio);
		// m_bio有东西有数据长度, 但是经过NCONF_load_bio()后, m_conf里面有东西了, 但是m_bio的东西没了
		// 所以, 经过NCONF_load_bio后(), 就得当m_bio是空的来处理.
		// 如果是向m_bio中写东西, 然后写配置.
		// 如果要更新m_bio中的配置内容, 就必须重新写.

		if (i_rc <= 0)
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

跟进NCONF_load_bio(), 可以看到openssl读一行开的缓冲区为512字节。

#define CONFBUFSIZE     512

虽然配置项的值一般没有那么长。不过如果要存一段很大的数据转成的base64值,那么就可能遇到问题。
解决的方法:自己做个多buffer的封装类(C++ - 多个buffer合并成一个buffer的管理类),比较长的信息,自己来管理。配置项值比较短的值,用openssl配置函数来读取。

openssl的配置文件(可以自己封装为内存操作的函数),并没有写入配置的API. 只有读取配置项的API.
如果需要用程序来写openssl的配置文件,可以自己封装一个。写入的格式和普通的ini格式一样(如果简单的用)。

配置项的值不能是base64之后的直接值,需要处理之后才行。

原因 :base64之后的值,每隔64个字符,有一个’\n’, 没有了这个’\n’, 用openssl的unbase64就不会成功。
折中的方法 :
在base64之后,写入配置项之前,处理一下,将’\n’去掉后,再写入openssl配置文件。
在读取配置项值之后(自己知道是base64的字符串),再处理一下,每64个字符加一个‘\n’(如果不是整除64个字符,bas64的尾巴上也要加’\n’), 然后再调用openssl的unbase64的API, 就能执行成功。

openssl配置项的值并不是所有可见字符都可以

有’\n’不行,openssl读取时,是整行(以回车’\n’为结尾)读取的,导致opensslbase64之后的值被截断(如果base64之后的值长度>64),导致unbase64不成功.

有’\'不行,e.g. 一个路径值(“d:\my_tmp\xx.dat”), 用openssl配置接口读取出来时,‘\’字符消失了, 变为了"d:my_tmpxx.dat",这就乱套了。
如果正好有\d这样的字符,读出的配置项的值直接乱码了。

如果配置项的值中有上述字符,都要base64之后才能正常读取。

例子

将base64写入openssl配置文件(去掉’\n’后,再base64, 然后写入配置项的值)

	// 
	obj.add_config_section("PE");

	//
	ptszBuf = (TCHAR*)m_PeFileCheck.getFilePathName();
	// 需要base64,否则'\'都没了,还有乱码
	strTmp = strOpt.my_W2A(ptszBuf);
	base64.base64(true, (UCHAR*)strTmp.data(), (int)strTmp.size(), pOsslBufTmp, len_OsslBufTmp);
	// base64之后,每65个字符之后有一个\n, 不能移除,base64解码时,如果不是每64个字符后面带一个\n, 会unbase64失败
	strTmp = base64.remove_char_0a(pOsslBufTmp, len_OsslBufTmp);
	
	obj.add_config_item_after_section("getFilePathName", (const char*)strTmp.data());
	MY_OPENSSL_FREE(pOsslBufTmp);

用openssl配置文件的一方,先读出配置项的值,加上’\n’(每64个字符), 再unbase64.

		pszKeyValue = _ossl_cfg.get_conf_item_value("PE", "getFilePathName");
		if (NULL == pszKeyValue)
		{
			break;
		}

		strTmp = base64.add_char_0a((UCHAR*)pszKeyValue, (int)strlen(pszKeyValue));
		b = base64.base64(false, (UCHAR*)strTmp.data(), (int)strTmp.size(), pOsslBufTmp, lenOsslBufTmp);
		if (!b)
		{
			break;
		}

现在用的base64的类

cipher_base64.h

//! \file cipher_base64.h

#ifndef __CIPHER_BASE64_H__
#define __CIPHER_BASE64_H__

class CBase64
{
public:
	CBase64();
	virtual ~CBase64();

	bool base64(bool isEncode, UCHAR* pucBufIn, int lenBufIn, UCHAR*& pucBufOut, int& lenBufOut);
	std::string remove_char_0a(UCHAR* pucBufIn, int lenBufIn);
	std::string add_char_0a(UCHAR* pucBufIn, int lenBufIn);
};

#endif // #ifndef __CIPHER_BASE64_H__

cipher_base64.cpp

//! \file cipher_base64.cpp

#include "pch.h"
#include "cipher_base64.h"

#include "openssl/bio.h"
#include "openssl/evp.h"

#include "memOpt/my_debug_new_define.h"

CBase64::CBase64()
{

}

CBase64::~CBase64()
{

}

std::string CBase64::remove_char_0a(UCHAR* pucBufIn, int lenBufIn)
{
	std::string str_rc;
	int i = 0;

	do {
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		for (i = 0; i < lenBufIn; i++)
		{
			if (pucBufIn[i] != '\n')
			{
				str_rc += pucBufIn[i];
			}
		}
	} while (false);

	return str_rc;
}

std::string CBase64::add_char_0a(UCHAR* pucBufIn, int lenBufIn)
{
	std::string str_rc;
	int cnt = 0;
	int i = 0;

	// openssl每一行(64个字符一行,最后一行除外)后面都有一个'\n', 才能正确解码
	do {
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		for (i = 0; i < lenBufIn; i++)
		{
			str_rc += pucBufIn[i];
			if (++cnt == 64)
			{
				cnt = 0;
				str_rc += '\n';
			}
		}
	} while (false);

	if (cnt > 0)
	{
		str_rc += '\n';
	}
	
	return str_rc;
}

bool CBase64::base64(bool isEncode, UCHAR* pucBufIn, int lenBufIn, UCHAR*& pucBufOut, int& lenBufOut)
{
	bool b_rc = false;
	BIO* bio_container = NULL;
	BIO* bio_to_base64 = NULL;

	BIO* bio_header = NULL; // BIO链头
	BIO* bio_tail = NULL; // BIO链尾

	BIO* bio_to_write = NULL; // 将数据写入的BIO指针
	BIO* bio_read_from = NULL; // 将数据读出的BIO指针

	size_t sz_wt = 0;
	size_t sz_rd = 0;
	int i_rc = 0;
	int len = 0;

	do {
		pucBufOut = NULL;
		lenBufOut = 0;
		if ((NULL == pucBufIn) || (lenBufIn <= 0))
		{
			break;
		}

		bio_container = BIO_new(BIO_s_mem());
		if (NULL == bio_container)
		{
			break;
		}

		bio_to_base64 = BIO_new(BIO_f_base64());
		if (NULL == bio_to_base64)
		{
			break;
		}

		bio_header = BIO_push(bio_to_base64, bio_container);
		bio_tail = bio_container;

		if (isEncode)
		{
			bio_to_write = bio_header;
			bio_read_from = bio_tail;
		}
		else {
			// ! base64解码时, 是从链尾写入, 从链头读取
			bio_to_write = bio_tail;
			bio_read_from = bio_header;
		}

		i_rc = BIO_write_ex(bio_to_write, pucBufIn, lenBufIn, &sz_wt);
		if ((1 != i_rc) || (lenBufIn != sz_wt))
		{
			break;
		}

		BIO_flush(bio_to_write); // 数据写完后, 必须对写入的BIO执行 BIO_flush.

		// 必须从bio_read_from读取处理完的数据长度, 才是处理之后的数据长度
		len = BIO_pending(bio_read_from); // 必须BIO_flush()之后, 才能读取到BIO内可以读出的数据长度. 否则读出的长度是0
		// 当解码时, 得到的处理完的长度还是没解码之前的长度, 不过不影响
		// 拿这个长度开buffer, 实际数据处理完的长度按照从bio_read_from()中累计出的数据长度为准

		// 将处理过的数据从bio_header中读出来
		pucBufOut = (UCHAR*)OPENSSL_malloc(len + 1); // 再多加1个字节的空间, 便于观察得到的可见字符串
		if (NULL == pucBufOut)
		{
			break;
		}

		pucBufOut[len] = '\0';

		do {
			// 不能从bio_header读取, 因为读取后, 还是原来的数据长度
			i_rc = BIO_read_ex(bio_read_from, pucBufOut + lenBufOut, len - lenBufOut, &sz_rd);
			if (i_rc <= 0)
			{
				// 多次读, 直到读空了, 不算错
				break;
			}

			lenBufOut += (int)sz_rd;
		} while (true);

		if (NULL != pucBufOut)
		{
			pucBufOut[lenBufOut] = 0x00;
		}

		b_rc = true;
	} while (false);

	if (NULL != bio_header)
	{
		BIO_free_all(bio_header);
		bio_header = NULL;
	}

	return b_rc;
}

现在用的openssl配置文件读写类

CMyOsslConfig.h

//! \file CMyOsslConfig.h

#ifndef __CMYOSSLCONFIG_H__
#define __CMYOSSLCONFIG_H__

#include <openssl/bio.h>
#include <openssl/conf.h> // for CONF
#include "CMyOsslMem.h"

class CMyOsslConfig
{
public:
	CMyOsslConfig();
	virtual ~CMyOsslConfig();

	bool init();
	void uninit();
	bool add_config_section(const char* pszIn);
	bool add_config_item_after_section(const char* pszItemName, const char* pszItemContent);
	bool append_to_bio(uint8_t* pBuf, int lenBuf);
	bool updateConfig();
	char* get_conf_item_value(const char* group, const char* name);

	CMyOsslMem* getMem() { return m_pMyOsslMem; }

private:
	CMyOsslMem* m_pMyOsslMem;
	CONF* m_conf;
};

#endif // #ifndef __CMYOSSLCONFIG_H__

CMyOsslConfig.cpp

//! \file CMyOsslConfig.cpp

#include "pch.h"

#include "CMyOsslConfig.h"
#include <string.h>
#include <openssl/err.h>
#include <cassert>
#include "CMyOsslMem.h"

#include "memOpt/my_debug_new_define.h"

CMyOsslConfig::CMyOsslConfig()
	:m_pMyOsslMem(NULL),
	m_conf(NULL)
{

}

CMyOsslConfig::~CMyOsslConfig()
{
	// 必须在程序退出前,主动调用uninit(), 否则在mem_unhook()时,就会报mem leak的错
}

void CMyOsslConfig::uninit()
{
	if (NULL != m_pMyOsslMem)
	{
		m_pMyOsslMem->uninit();
		delete m_pMyOsslMem;
		m_pMyOsslMem = NULL;
	}

	if (NULL != m_conf)
	{
		NCONF_free(m_conf);
		m_conf = NULL;
	}
}

bool CMyOsslConfig::init()
{
	bool b_rc = false;

	do {
		if (NULL == m_pMyOsslMem)
		{
			m_pMyOsslMem = new CMyOsslMem();
			assert(NULL != m_pMyOsslMem);
			if (NULL == m_pMyOsslMem)
			{
				break;
			}

			m_pMyOsslMem->init();
		}

		if (NULL == m_conf)
		{
			m_conf = NCONF_new_ex(OSSL_LIB_CTX_get0_global_default(), NULL);
			if (NULL == m_conf)
			{
				break;
			}
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::add_config_section(const char* pszIn)
{
	bool b_rc = false;
	char szBuf[1024];
	int len = 0;

	do {
		if (NULL == pszIn)
		{
			break;
		}

		len = (int)strlen(pszIn);
		if (len > (sizeof(szBuf) - 0x10))
		{
			break;
		}

		sprintf(szBuf, "[ %s ]\n", pszIn);
		assert(NULL != m_pMyOsslMem);
		if (!m_pMyOsslMem->append_to_bio((uint8_t*)szBuf, (int)strlen(szBuf)))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::add_config_item_after_section(const char* pszItemName, const char* pszItemContent)
{
	bool b_rc = false;
	char szBuf[1024];
	int len = 0;

	do {
		if ((NULL == pszItemName) || (NULL == pszItemContent))
		{
			break;
		}

		len = (int)(strlen(pszItemName) + strlen(pszItemContent));
		if (len > (sizeof(szBuf) - 0x10))
		{
			break;
		}

		sprintf(szBuf, "%s = %s\n", pszItemName, pszItemContent);

		if (NULL == m_pMyOsslMem)
		{
			break;
		}

		assert(NULL != m_pMyOsslMem);
		if (!m_pMyOsslMem->append_to_bio((uint8_t*)szBuf, (int)strlen(szBuf)))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslConfig::append_to_bio(uint8_t* pBuf, int lenBuf)
{
	assert(NULL != m_pMyOsslMem);
	return m_pMyOsslMem->append_to_bio(pBuf, lenBuf);
}

bool CMyOsslConfig::updateConfig()
{
	bool b_rc = false;
	int i_rc = 0;
	long lineSn = 0;
	int len1 = 0;
	int len2 = 0;

	do {
		if ((NULL == m_pMyOsslMem) || (NULL == m_conf))
		{
			break;
		}

		// BIO_seek(m_bio, 0);
		// len1 = this->bio_get_length(m_bio);
		assert(NULL != m_pMyOsslMem->get_bio());
		i_rc = NCONF_load_bio(m_conf, m_pMyOsslMem->get_bio(), &lineSn);
		// len2 = this->bio_get_length(m_bio);
		// m_bio有东西有数据长度, 但是经过NCONF_load_bio()后, m_conf里面有东西了, 但是m_bio的东西没了
		// 所以, 经过NCONF_load_bio后(), 就得当m_bio是空的来处理.
		// 如果是向m_bio中写东西, 然后写配置.
		// 如果要更新m_bio中的配置内容, 就必须重新写.

		if (i_rc <= 0)
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

char* CMyOsslConfig::get_conf_item_value(const char* group, const char* name)
{
	char* res = NULL;
	
	int i_rc = 0;

	do {
		if (NULL == m_conf)
		{
			break;
		}

		res = NCONF_get_string(m_conf, group, name);
		if (NULL == res)
		{
			ERR_pop_to_mark();
		}
		else {
			ERR_clear_last_mark();
		}
	} while (false);

	return res;
}

CMyOsslMem.h

//! \file CMyOsslMem.h

#ifndef __C_MY_OSSL_MEM_H__
#define __C_MY_OSSL_MEM_H__

#include <openssl/bio.h>
#include <openssl/conf.h> // for CONF

class CMyOsslMem
{
public:
	CMyOsslMem();
	virtual ~CMyOsslMem();

	bool init();
	void uninit();

	BIO* get_bio();

	bool append_to_bio(uint8_t* pBuf, int lenBuf);
	bool bio_to_buf(BIO* bio, uint8_t*& pBuf, int& lenBuf); // 执行之后,东西还在. 需要调用者释放pBuf

	bool to_file(const TCHAR* psz_file_pathname); // for test only
	bool get_bio_buffer(uint8_t*& pBuf, int& lenBuf); // 需要自己释放(OPENSSL_free)

	size_t get_length();
	size_t bio_get_length(BIO* bio);

	unsigned char* get_bio_data_tail(); // only for test

private:
	BIO* m_bio;

	// only for test
	unsigned char* m_bio_data;
	int m_bio_len;
};

#endif // #ifndef __C_MY_OSSL_MEM_H__

CMyOsslMem.cpp

//! \file CMyOsslMem.cpp

#include "pch.h"

#include "CMyOsslMem.h"
#include <string.h>
#include <openssl/err.h>
#include <cassert>

#include "memOpt/my_debug_new_define.h"

CMyOsslMem::CMyOsslMem()
	:m_bio(NULL),
	m_bio_len(0),
	m_bio_data(NULL)
{

}

CMyOsslMem::~CMyOsslMem()
{
	// 必须在程序退出前,主动调用uninit(), 否则在mem_unhook()时,就会报mem leak的错
}

void CMyOsslMem::uninit()
{
	if (NULL != m_bio)
	{
		BIO_free(m_bio);
		m_bio = NULL;
	}
}

bool CMyOsslMem::init()
{
	bool b_rc = false;

	do {
		if (NULL == m_bio)
		{
			m_bio = BIO_new(BIO_s_mem());
			if (NULL == m_bio)
			{
				assert(false);
				break;
			}
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslMem::append_to_bio(uint8_t* pBuf, int lenBuf)
{
	bool b_rc = false;
	int len = 0;
	size_t szLen = 0;
	int bio_len_before = 0;
	int bio_len_after = 0;

	unsigned char* pDebug = NULL;
	int lenDebug = 0;

	do {
		if ((NULL == pBuf) || (lenBuf <= 0))
		{
			break;
		}

		if (NULL == m_bio)
		{
			assert(false);
			break;
		}

		// szLen = bio_get_length(m_bio);
		// BIO_seek(m_bio, szLen);
		bio_len_before = (int)bio_get_length(m_bio);
		len = BIO_write(m_bio, pBuf, lenBuf);
		bio_len_after = (int)bio_get_length(m_bio);
		if (len != lenBuf)
		{
			break;
		}

		if (len != (bio_len_after - bio_len_before))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;

}

BIO* CMyOsslMem::get_bio()
{
	if (NULL != m_bio)
	{
		// BIO_seek(m_bio, 0);
	}

	return m_bio;
}

size_t CMyOsslMem::get_length()
{
	return bio_get_length(this->get_bio());
}

size_t CMyOsslMem::bio_get_length(BIO* bio)
{
	size_t bio_length = 0;

	do {
		if (NULL == bio)
		{
			break;
		}

		// BIO_seek(bio, 0);
		bio_length = BIO_ctrl_pending(bio);
	} while (false);

	return bio_length;
}

unsigned char* CMyOsslMem::get_bio_data_tail()
{
	unsigned char* p_rc = NULL;

	do {
		if (NULL == m_bio)
		{
			assert(false);
			break;
		}

		// 取出的指针是bio内部的buffer地址,不用释放
		m_bio_len = BIO_get_mem_data(m_bio, &m_bio_data);
		p_rc = m_bio_data + m_bio_len;
	} while (false);

	return p_rc;
}

bool CMyOsslMem::to_file(const TCHAR* psz_file_pathname)
{
	bool b_rc = false;
	FILE* pf = NULL;
	uint8_t* pBuf = NULL;
	int len = 0;
	size_t sz_rc = 0;

	do {
		if (NULL == psz_file_pathname)
		{
			break;
		}

		pf = _tfopen(psz_file_pathname, TEXT("w+b"));
		if (NULL == pf)
		{
			break;
		}

		if (!get_bio_buffer(pBuf, len))
		{
			break;
		}

		if ((NULL == pBuf) || (len <= 0))
		{
			break;
		}

		sz_rc = fwrite(pBuf, sizeof(char), len, pf);
		assert(sz_rc == len);

		b_rc = true;
	} while (false);

	if (NULL != pf)
	{
		fclose(pf);
		pf = NULL;
	}
	
	if (NULL != pBuf)
	{
		OPENSSL_free(pBuf);
		pBuf = NULL;
	}

	return b_rc;
}

bool CMyOsslMem::get_bio_buffer(uint8_t*& pBuf, int& lenBuf)
{
	bool b_rc = false;

	do {
		if (!bio_to_buf(get_bio(), pBuf, lenBuf))
		{
			break;
		}

		if ((NULL == pBuf) || (lenBuf <= 0))
		{
			break;
		}

		b_rc = true;
	} while (false);

	return b_rc;
}

bool CMyOsslMem::bio_to_buf(BIO* bio, uint8_t*& pBuf, int& lenBuf)
{
	bool b_rc = false;
	int i_rc = 0;

	do {
		if (NULL == bio)
		{
			break;
		}

		lenBuf = (int)bio_get_length(bio);
		pBuf = (uint8_t*)OPENSSL_malloc(lenBuf + 1);
		if (NULL == pBuf)
		{
			break;
		}

		pBuf[lenBuf] = '\0';
		i_rc = BIO_read(bio, pBuf, lenBuf);
		BIO_seek(bio, 0); // ! 读完了, 将数据读指针恢复.

		b_rc = (i_rc == lenBuf);
	} while (false);

	return b_rc;
}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/560166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Android开发——Fragment

Demo fragment_blank.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_pare…

Java Web3-2 - tomcat

https://github.com/heibaiying/Full-Stack-Notes/blob/master/notes/Tomcat_架构解析.md https://zhuanlan.zhihu.com/p/40249834 早期&#xff0c;web技术主要用于浏览静态页面 时间发展&#xff0c;用户已经不满足于仅浏览静态页面。用户需要一些交互操作&#xff0c;获取…

追溯历史:SIEM 中的生成式人工智能革命

作者&#xff1a;来自 Elastic Mike Nichols, Mike Paquette 网络安全领域仿佛是现实世界的一个映射&#xff0c;安全运营中心&#xff08;security operation center - SOC&#xff09;就像是你的数字警察局。网络安全分析师就像是警察&#xff0c;他们的工作是阻止网络犯罪分…

【Web】DASCTF X GFCTF 2024|四月开启第一局 题解

目录 EasySignin cool_index web1234 web4打破防了&#x1f92e;&#xff0c;应该很接近解出来了&#xff0c;感兴趣的师傅续上吧 EasySignin 先随便注册个账号登录&#xff0c;然后拿bp抓包改密码(username改成admin) 然后admin / 1234567登录 康好康的图片功能可以打SS…

ros仿真启动小龟

1.启动RosMaster&#xff08;管理Ros中各个节点的“大管家”&#xff0c;每次启动Ros时需要首先启动RosMaster&#xff09; roscorefangfang-inspiron-5580:~/ros2/download/rosdistro$ roscore ... logging to /home/fang/.ros/log/6ec2d790-fe1d-11ee-aba8-1c1bb5cdec7c/ros…

MySQL-实验-单表、多表数据查询和嵌套查询

目录 0.简单子查询 &#xff08;1&#xff09;带比较运算符的子查询 &#xff08;2&#xff09;关键字子查询 1.多表查询 3.子查询 4.多表子查询 0.简单子查询 &#xff08;1&#xff09;带比较运算符的子查询 在右侧编辑器补充代码&#xff0c;查询大于所有平均年龄的员…

10 SQL进阶 -- 综合练习题 -- 10道经典SQL题目,配套数据与解答

1. 创建表结构和导入数据 1.1 新建数据库 1.2 执行建表语句 点击下方链接直接下载创建数据表脚本:http://tianchi-media.oss-cn-beijing.aliyuncs.com/dragonball/SQL/create_table.sql 执行建表语句执行成功查看创建的表1.3 导入数据 点击下方链接直接下载插入数据脚本:htt…

VBA脚本终章编译器崩溃

一、介绍 本篇文章为VBA脚本隐藏技术的最后一篇&#xff0c;将介绍如何在保证VBA脚本正常执行的情况下&#xff0c;使分析人员无法打开编译器。 那么为什么需要分析人员无法打开编译器呢&#xff1f; 首先&#xff0c;我们需要引入一个知识点。 在上篇《VBA隐藏技术stompin…

笔记本wifi连接外网 网线连接办公内网 设置路由实现内外网可同时访问

工作提供的办公网络是企业内网,接上企业内网网线后 通过无线在连接手机wifi ,会发现内外网无法同时访问,我自己电脑是接上内网网线 也是只能访问外网,除非把外网无线暂时关闭,才可以访问内网 频繁切换很不方便 1.查看外网无线 wifi网卡信息 IPv4 地址: 192.168.18.114 IP…

数据结构学习记录

数据结构 数组 & 链表 相连性 | 指向性 数组可以迅速定位到数组中某一个节点的位置 链表则需要通过前一个元素指向下一个元素&#xff0c;需要前后依赖顺序查找&#xff0c;效率较低 实现链表 // head > node1 > node2 > ... > nullclass Node {constructo…

AI原生时代,操作系统为何是创新之源?

一直以来&#xff0c;操作系统都是软件行业皇冠上的明珠。 从上世纪40、50年代&#xff0c;汇编语言和汇编器实现软件管理硬件&#xff0c;操作系统的雏形出现&#xff1b;到60年代&#xff0c;高级编程语言和编译器诞生&#xff0c;开发者通过操作系统用更接近人的表达方式去…

面向对象(一)

一.类与对象的定义 (1)类(设计图):是对象共同特征的描述: (2)对象:是真实存在的具体东西。 在Java中,必须先设计类&#xff0c;才能获取对象。 二.如何定义类 public class 类名{1.成员变量(代表属性,一般是名词) 2.成员方法(代表行为,一般是动词) 3.构造器 4.代码块 5.内部…

Liunx入门学习 之 基础操作指令讲解(小白必看)

股票的规律找到了&#xff0c;不是涨就是跌 一、Linux下基本指令 1.ls 指令 2.pwd 命令 3.cd 指令 4.touch 指令 5.mkdir 指令 6.rmdir指令 && rm 指令 7.man 指令 8.cp 指令 9.mv指令 10.cat 11.more 指令 12.less 指令 13.head 指令 14.tail 指令 15…

论文解读-Contiguitas: The Pursuit of Physical Memory Contiguity in Datacenters

研究背景&#xff1a; 在内存容量飞速增长的背景下&#xff0c;使用小页管理内存会带来巨大的内存管理开销&#xff08;地址转换开销高&#xff09;。近些年来不少研究尝试给应用分配大段连续区域&#xff0c;或者改善页表结构&#xff08;如使用hash结构的页表&#xff09;以降…

质谱原理与仪器2-笔记

质谱原理与仪器2-笔记 常见电离源电子轰击电离源(EI)碎片峰的产生典型的EI质谱图 化学电离源(CI)快原子轰击源(FAB)基体辅助激光解析电离(MALDI)典型的MALDI质谱图 大气压电离源(API)电喷雾离子源(ESI)大气压化学电离源(APCI)APCI的正负离子模式 大气压光电离源(APPI) 常见电离…

玄子Share-计算机网络参考模型

玄子Share-计算机网络参考模型 分层思想 利用七层参考模型&#xff0c;便于在网络通信过程中&#xff0c;快速的分析问题&#xff0c;定位问题并解决问题 将复杂的流程分解为几个功能相对单一的子过程 整个流程更加清晰&#xff0c;复杂问题简单化 更容易发现问题并针对性的…

线上频繁fullgc问题-SpringActuator的坑

整体复盘 一个不算普通的周五中午&#xff0c;同事收到了大量了cpu异常的报警。根据报警表现和通过arthas查看&#xff0c;很明显的问题就是内存不足&#xff0c;疯狂无效gc。而且结合arthas和gc日志查看&#xff0c;老年代打满了&#xff0c;gc不了一点。既然问题是内存问题&…

Python练习03

题目 解题思路 Demo58 通过字符串切片来进行反转操作 def _reverse():"""这是一个反转整数的函数"""num input("请输入想要反转的整数")print(num[::-1]) 运行结果 Demo61 首先制作一个判断边长的函数&#xff0c;通过三角形两边…

又成长了,异常掉电踩到了MySQL主从同步的坑!

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

Google Earth Engine 洪水制图 - 使用 Sentinel-1 SAR GRD

Sentinel-1 提供从具有双极化功能的 C 波段合成孔径雷达 (SAR) 设备获得的信息。该数据包括地面范围检测 (GRD) 场景,这些场景已通过 Sentinel-1 工具箱进行处理,以创建经过校准和正射校正的产品。该集合每天都会更新,新获得的资产会在可用后两天内添加。 该集合包含所有 G…
最新文章