openssl3.2 - osslsigncode工程的学习
文章目录
- openssl3.2 - osslsigncode工程的学习
- 概述
- 笔记
- 工程库地址
- 工程的编译
- osslsigncodeM工程文件列表
- osslsigncodeM工程搭建细节
- 原始工程实现的改动
- 自己封装的包含openssl和curl的实现
- osslsigncodeM工程命令行的用法
- 备注 - VS2019调试环境
- 备注 - 如果要单步openssl的API
- 学学作者怎么对签好的PE进行验签(code sgin)
- 取PE中的签名位置和签名数据长度
- 算PE的实际文件累加和
- 将签名数据(DER格式)转为PKCS7数据
- 将签名数据保存成文件供调试
- 备注
- END
openssl3.2 - osslsigncode工程的学习
概述
github上有个osslsigncode工程, 可以对PE文件进行code sign签名和验签.
想在自己工程中, 对正在运行的的PE本身文件进行完整性校验, 应该能从osslsigncode工程中学到东西.
在看资料时, 看到一个逆向工程师, patch掉一个用土法进行EXE本身的完整性校验的案例.
这个案例, 程序里面自己算了本身的校验和, 然后和程序中预留的校验和进行比对, 这样很容易被别人搞掉.
好一些的方法是, 用osslsigncode工程里面这种用公版openssl的验签的方法, 不存在直接的比对.
发现不对了, 悄悄的不干活就是了.
如果逆向工程师硬要暴力patch, 后续再结合其他措施, 让被加密的配置文件不能正常解密, 达到程序不能让非正版用户使用的目的就行了.
笔记
工程库地址
https://github.com/mtrojnar/osslsigncode.git
工程的编译
作者的使用环境是在like-unix环境下, 他没有认真考虑使用者在windows环境下的编译.
用作者的脚本, 无法正常在(vs2019 + cmake)环境下编译通过. 编译脚本给的真的有问题.
主要是cmake + vcpkg的依赖太复杂, 要是像我重建的工程这样整简单一些(工程依赖项都是编译好的, 只考虑工程本身). 那工程的学习和使用者就方便太多了.
这个工程用了openssl3.1.x + curl作为组件, 其他是作者自己的实现.
openssl3.x接口都是兼容的, 我这里就使用openssl3.2.
openssl3.2我自己编译好了(openssl3.2 - 编译).
curl编译挺复杂的, 我直接用了官方的x64版(curl8.6.0 - CURLE_PEER_FAILED_VERIFICATION).
基于上面2个现成的组件(openssl3.2 + curl8.6.0), 自己复原了一个osslsigncode的VS2019X64工程, 命名为osslsigncodeM (M means modify)
osslsigncodeM工程文件列表
| .
| appx.c
| cab.c
| cat.c
| helpers.c
| helpers.h
| msi.c
| my_curl_lib.c
| my_curl_lib.h
| my_openSSL_lib.c
| my_openSSL_lib.h
| osslsigncode.c
| osslsigncode.h
| pe.c
| prj_files.txt
| prj_template.sln
| prj_template.vcxproj
| prj_template.vcxproj.filters
| prj_template.vcxproj.user
|
+---bin_x64
| case1.exe
| exe_for_sign_x64.exe.org
| libcrypto-3-x64.dll
| libcurl-x64.dll
| libssl-3-x64.dll
| my_app_cert.p12
| my_app_cert.pem
| my_app_key.pem
| my_log.txt
| my_root_ca_cert.pem
| my_zlib_1d3.dll
| osslsigncodeM.exe
| test_sign.cmd
|
\---docINSTALL.W32.mdNEWS.mdREADME.mdREADME_softhsm-example-token.mdTODO.md
osslsigncodeM工程搭建细节
工程搭建完成后, 编译有一些警告, 都是作者打印信息时的字符串格式化的选项不合适, 不影响用, 不管了.
预处理选项中, 要加2个宏, 否则编译不过.
ENABLE_CURL
Z_SOLO
头文件包含路径 - 指定openssl和curl的头文件路径
.\;D:\my_dev\lib\openssl_3d2\include;D:\my_dev\lib\curl-8.6.0_1-win64-mingw\include
库文件包含路径 - 指定openssl, curl, zlib的库文件包换路径
zlib是openssl要用的, curl中用的zlib是静态包含的.
D:\my_dev\lib\openssl_3d2\lib;D:\my_dev\lib\curl-8.6.0_1-win64-mingw\lib;D:\my_dev\lib\zlib_1d3
输出文件路径改一下, 生成到工程目录下的单独文件夹中, 可以和中间输出文件分开, 方便调试和归档.
$(ProjectDir)\bin_x64\$(TargetName)$(TargetExt)
原始工程实现的改动
osslsigncode.h 要包含openssl和curl库, 需要加2句.
/*! \file osslsigncode.h * Copyright (C) 2021-2023 Michał Trojnara <Michal.Trojnara@stunnel.org>* Author: Małgorzata Olszówka <Malgorzata.Olszowka@stunnel.org>*/// add by ls
#include "my_openSSL_lib.h"
#include "my_curl_lib.h"#define OPENSSL_API_COMPAT 0x10100000L
#define OPENSSL_NO_DEPRECATED
自己封装的包含openssl和curl的实现
/*!
\file my_openSSL_lib.h
*/#ifndef __MY_OPENSSL_LIB_H__
#define __MY_OPENSSL_LIB_H__#ifdef _WIN32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib") // for select()#include <windows.h>#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")#pragma comment(lib, "my_zlib_1d3.lib")#endif /* #ifdef _WIN32 */// --------------------------------------------------------------------------------
// 开关宏 - begin
// --------------------------------------------------------------------------------#define MY_USE_APPLINK// --------------------------------------------------------------------------------
// 开关宏 - END
// --------------------------------------------------------------------------------#endif /* #ifndef __MY_OPENSSL_LIB_H__ */
/*!
* \file D:\my_dev\my_local_git_prj\study\openSSL\nmake_test\test_c\prj_005_afalgtest.c\my_openSSL_lib.c
*/#include "my_openSSL_lib.h"#ifdef MY_USE_APPLINK
#include <openssl/applink.c> /*! for OPENSSL_Uplink(00007FF8B7EF0FE8,08): no OPENSSL_Applink */
#endif // #ifdef MY_USE_APPLINK
/*!
\file my_curl_lib.h
*/#ifndef __MY_CURL_LIB_H__
#define __MY_CURL_LIB_H__#include <curl/curl.h>
#pragma comment(lib, "libcurl.dll.a")#endif /* #ifndef __MY_CURL_LIB_H__ */
/*!
* \file my_curl_lib.c
*/#include "my_curl_lib.h"
osslsigncodeM工程命令行的用法
osslsigncode实现还没看, 主要试一下是否工程复原成功了. 如果工程搭建出来没有原始工程的效果, 还得找原因或放弃.
看osslsigncode的文档, 一共5个, 都有点用.
整理了一个脚本, 试了好使.
实验用到的证书, 都是自己前面做openssl实验(openssl3.2 - use openssl cmd create ca and p12)时准备好的.
脚本如下
@echo offrem \file test_sign.cmdrem 官方用法
rem osslsigncode sign -certs <cert-file> -key <der-key-file> -n "Your Application" -i http://www.yourwebsite.com/ -in yourapp.exe -out yourapp-signed.exe
rem osslsigncode sign -certs <cert-file> -key <key-file> -pass <key-password> -n "Your Application" -i http://www.yourwebsite.com/ -in yourapp.exe -out yourapp-signed.exe
rem osslsigncode sign -certs <cert-file> -key <key-file> -pass <key-password> -n "Your Application" -i http://www.yourwebsite.com/ -t http://timestamp.digicert.com -in yourapp.exe -out yourapp-signed.exe
rem osslsigncode sign -pkcs12 <pkcs12-file> -pass <pkcs12-password> -n "Your Application" -i http://www.yourwebsite.com/ -t http://timestamp.digicert.com -in yourapp.exe -out yourapp-signed.exe
rem osslsigncode.exe add -addUnauthenticatedBlob -in your_signed_file.exe -out out.exerem my_app_key.pem 口令 = pwd_app_222222, 配套证书 my_app_cert.pem
rem my_app_cert.p12 口令 = pwd_exp_333333rem 自己的实际 case
rem osslsigncodeM.exe sign -certs my_app_cert.pem -key my_app_key.pem -n "Your Application" -i http://www.yourwebsite.com/ -in case1.exe -out yourapp-signed1.exe
rem osslsigncodeM.exe sign -certs my_app_cert.pem -key my_app_key.pem -pass pwd_app_222222 -n "Your Application" -i http://www.yourwebsite.com/ -in case1.exe -out yourapp-signed2.exe
rem osslsigncodeM.exe sign -certs my_app_cert.pem -key my_app_key.pem -pass pwd_app_222222 -n "Your Application" -i http://www.yourwebsite.com/ -t http://timestamp.digicert.com -in case1.exe -out yourapp-signed3.exe
rem osslsigncodeM.exe sign -pkcs12 my_app_cert.p12 -pass pwd_exp_333333 -n "Your Application" -i http://www.yourwebsite.com/ -t http://timestamp.digicert.com -in case1.exe -out yourapp-signed4.exe
rem osslsigncodeM.exe.exe add -addUnauthenticatedBlob -in your_signed_file4.exe -out out.exerem PE code sign 验签
osslsigncodeM.exe verify -CAfile my_root_ca_cert.pem -ignore-timestamp yourapp-signed4.exe
根据工程文档, osslsigncode的功能主要有3个.
- 对PE文件进行签名(code sign), 作者给了例子.
- 对PE文件进行验签(code sign), 作者没给例子, 自己猜读/实验, 搞定了.
- 对已经签名的PE文件附加一段1024bytes的数据, 作者给了例子.
对于第3个用法, 有可能杀毒软件有不好的提示, 如果没有特殊用场, 这个用法还是别用, 正常的用程序来实现, 而不是在签名后再附加数据. 正常程序都不会这么玩.
对于第1个用法, 除非自己写签名(code sign)程序, 正常的都是用第三方程序来签名. 这个我不用.
我比较关注的是第2个用法, 学学人家怎么用openssl来验签(code sign).
我自己实验整理出来的验签命令行, 加了-ignore-timestamp, 不验证时间戳, 对于我自己的工程应用就够了.
osslsigncode的细节用法, 远不止作者列出的这2种(签名, 附加数据), e.g. 验签
因为不是作者, 如果感兴趣, 只能自己去看命令行参数和程序实现, 自己去实验了.
对于开源工程, 小白大致只能看作者发布的文档, 作者没说的, 又看着大概好像有的功能, 只能自己去工程中去深挖了.
D:\my_dev\my_local_git_prj\study\openSSL\osslsigncodeM\bin_x64>osslsigncodeM.exe --help
osslsigncode custom build, using:OpenSSL 3.2.0 23 Nov 2023 (Library: OpenSSL 3.2.0 23 Nov 2023)libcurl/8.6.0 LibreSSL/3.8.2 zlib/1.3.1 brotli/1.1.0 zstd/1.5.5 WinIDN libpsl/0.21.5 libssh2/1.11.0 nghttp2/1.59.0 ngtcp2/1.2.0 nghttp3/1.1.0osslsigncode is a small tool that implements part of the functionality of the Microsoft
tool signtool.exe - more exactly the Authenticode signing and timestamping.
It can sign and timestamp PE (EXE/SYS/DLL/etc), CAB and MSI files,
supports getting the timestamp through a proxy as well.
osslsigncode also supports signature verification, removal and extraction.--version | -v = print osslsigncode version and usage
--help = print osslsigncode help menuCommands:
add = add an unauthenticated blob or a timestamp to a previously-signed file
attach-signature = sign file using a given signature
extract-signature = extract signature from a previously-signed file
remove-signature = remove sections of the embedded signature on a file
sign = digitally sign a file
verify = verifies the digital signature of a fileFor help on a specific command, enter osslsigncodeM.exe <command> --helpUsage: osslsigncodeM.exe[ --version | -v ][ --help ][ sign ] ( -certs | -spc <certfile> -key <keyfile> | -pkcs12 <pkcs12file> |[ -pkcs11engine <engine> ] -pkcs11module <module> -pkcs11cert <pkcs11 cert id> |-certs <certfile> -key <pkcs11 key id>)[ -nolegacy ][ -pass <password> [ -readpass <file> ][ -ac <crosscertfile> ][ -h {md5,sha1,sha2(56),sha384,sha512} ][ -n <desc> ] [ -i <url> ] [ -jp <level> ] [ -comm ][ -ph ][ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ][ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ][ -TSA-certs <TSA-certfile> ] [ -TSA-key <TSA-keyfile> ][ -TSA-time <unix-time> ][ -time <unix-time> ][ -addUnauthenticatedBlob ][ -nest ][ -verbose ][ -add-msi-dse ][ -pem ][ -in ] <infile> [-out ] <outfile>extract-data [ -pem ][ -h {md5,sha1,sha2(56),sha384,sha512} ][ -ph ][ -add-msi-dse ][ -in ] <infile> [ -out ] <datafile>add [-addUnauthenticatedBlob][ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ][ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ][ -TSA-certs <TSA-certfile> ] [ -TSA-key <TSA-keyfile> ][ -TSA-time <unix-time> ][ -h {md5,sha1,sha2(56),sha384,sha512} ][ -index <index> ][ -verbose ][ -add-msi-dse ][ -in ] <infile> [ -out ] <outfile>attach-signature [ -sigin ] <sigfile>[ -CAfile <infile> ][ -CRLfile <infile> ][ -TSA-CAfile <infile> ][ -TSA-CRLfile <infile> ][ -time <unix-time> ][ -h {md5,sha1,sha2(56),sha384,sha512} ][ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ][ -nest ][ -add-msi-dse ][ -in ] <infile> [ -out ] <outfile>extract-signature [ -pem ][ -in ] <infile> [ -out ] <sigfile>remove-signature [ -in ] <infile> [ -out ] <outfile>verify [ -in ] <infile>[ -c | -catalog <infile> ][ -CAfile <infile> ][ -CRLfile <infile> ][ -TSA-CAfile <infile> ][ -TSA-CRLfile <infile> ][ -index <index> ][ -ignore-timestamp ][ -time <unix-time> ][ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ][ -verbose ]D:\my_dev\my_local_git_prj\study\openSSL\osslsigncodeM\bin_x64>
备注 - VS2019调试环境
如果输出的路径自己改了, 调试时的命令, 也要改成自己实际输出的那个EXE全路径, 否则VS2019找不到要调试的程序.
备注 - 如果要单步openssl的API
- 供APP用的openssl本身就是就要是debug版的才行. 我将安装后的openssl做了备份 openssl-3.2.0_debug_build_ok.7z
- 给APP用的openssl的DLL同级目录要有对应的Debug版的.PDB, 没有PDB, 即使是用的debug版的openssl DLL, 也无法步入.
- .PDB中记录的.c实现文件, 都是绝对路径, 所以编译出的源码工程位置不能变. 如果openssl源码目录改个名字, 就会跳出某个文件找不到, 让调试者自己找的浏览框.
学学作者怎么对签好的PE进行验签(code sgin)
取PE中的签名位置和签名数据长度
/** Verify mapped PE file and create PE format specific structure.* [in] indata: mapped PE file* [in] filesize: size of PE file* [returns] pointer to PE format specific structure*/
static PE_CTX *pe_ctx_get(char *indata, uint32_t filesize)
{PE_CTX *pe_ctx;uint32_t header_size, pe32plus, pe_checksum, nrvas, sigpos, siglen;uint16_t magic;if (filesize < 64) {printf("Corrupt DOS file - too short\n");return NULL; /* FAILED */}/* SizeOfHeaders field specifies the combined size of an MS-DOS stub, PE header,* and section headers rounded up to a multiple of FileAlignment.* SizeOfHeaders must be < filesize and cannot be < 0x0000002C (44) in Windows 7* because of a bug when checking section names for compatibility purposes */header_size = GET_UINT32_LE(indata + 60);if (header_size < 44 || header_size > filesize) {printf("Unexpected SizeOfHeaders field: 0x%08X\n", header_size);return NULL; /* FAILED */}if (filesize < header_size + 176) {printf("Corrupt PE file - too short\n");return NULL; /* FAILED */}if (memcmp(indata + header_size, "PE\0\0", 4)) {printf("Unrecognized DOS file type\n");return NULL; /* FAILED */}/* Magic field identifies the state of the image file. The most common number is* 0x10B, which identifies it as a normal executable file,* 0x20B identifies it as a PE32+ executable,* 0x107 identifies it as a ROM image (not supported) */magic = GET_UINT16_LE(indata + header_size + 24);if (magic == 0x20b) {pe32plus = 1;} else if (magic == 0x10b) {pe32plus = 0;} else {printf("Corrupt PE file - found unknown magic %04X\n", magic);return NULL; /* FAILED */}/* The image file checksum */pe_checksum = GET_UINT32_LE(indata + header_size + 88);/* NumberOfRvaAndSizes field specifies the number of data-directory entries* in the remainder of the optional header. Each describes a location and size. */nrvas = GET_UINT32_LE(indata + header_size + 116 + pe32plus * 16);if (nrvas < 5) {printf("Can not handle PE files without certificate table resource\n");return NULL; /* FAILED */}/* Certificate Table field specifies the attribute certificate table address (4 bytes) and size (4 bytes) */sigpos = GET_UINT32_LE(indata + header_size + 152 + pe32plus * 16);siglen = GET_UINT32_LE(indata + header_size + 152 + pe32plus * 16 + 4);// 对于未签名的PE, 得到的sigpos = 0, siglen = 0/* Since fix for MS Bulletin MS12-024 we can really assumethat signature should be last part of file */if ((sigpos != 0 || siglen != 0) &&(sigpos == 0 || siglen == 0 || sigpos >= filesize || sigpos + siglen != filesize)) {printf("Ignoring PE signature not at the end of the file\n");sigpos = 0;siglen = 0;}pe_ctx = OPENSSL_zalloc(sizeof(PE_CTX));pe_ctx->header_size = header_size;pe_ctx->pe32plus = pe32plus;pe_ctx->magic = magic;pe_ctx->pe_checksum = pe_checksum;pe_ctx->nrvas = nrvas;pe_ctx->sigpos = sigpos;pe_ctx->siglen = siglen;pe_ctx->fileend = filesize;return pe_ctx; /* OK */
}
算PE的实际文件累加和
/** Compute a checkSum value of the signed or unsigned PE file.* [in] ctx: structure holds input and output data* [returns] checksum*/
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx)
{uint32_t n = 0, checkSum = 0, offset = 0;BIO *bio = BIO_new(BIO_s_mem());unsigned short *buf = OPENSSL_malloc(SIZE_64K);/* calculate the checkSum */while (n < ctx->pe_ctx->fileend) {size_t i, written, nread;size_t left = ctx->pe_ctx->fileend - n;unsigned short val;if (left > SIZE_64K)left = SIZE_64K;if (!BIO_write_ex(bio, ctx->options->indata + n, left, &written))goto err; /* FAILED */(void)BIO_seek(bio, 0);n += (uint32_t)written;if (!BIO_read_ex(bio, buf, written, &nread))goto err; /* FAILED */for (i = 0; i < nread / 2; i++) {val = LE_UINT16(buf[i]);if (offset == ctx->pe_ctx->header_size + 88|| offset == ctx->pe_ctx->header_size + 90) {val = 0; // 如果读到PE文件中累加和的位置的4个字节, 跳过去.}checkSum += val;checkSum = LOWORD(LOWORD(checkSum) + HIWORD(checkSum));offset += 2;}}checkSum = LOWORD(LOWORD(checkSum) + HIWORD(checkSum));checkSum += offset; // 文件内容的实际累加和还要加上文件size
err:OPENSSL_free(buf);BIO_free(bio);return checkSum;
}
将签名数据(DER格式)转为PKCS7数据
/** Retrieve and verify a decoded PKCS#7 structure corresponding* to the existing signature of the PE file.* [in] indata: mapped PE file* [in] pe_ctx: PE format specific structures* [returns] pointer to PKCS#7 structure*/
static PKCS7 *pe_pkcs7_get_file(char *indata, PE_CTX *pe_ctx)
{uint32_t pos = 0;if (pe_ctx->siglen == 0 || pe_ctx->siglen > pe_ctx->fileend) {printf("Corrupted signature length: 0x%08X\n", pe_ctx->siglen);return NULL; /* FAILED */}while (pos < pe_ctx->siglen) {uint32_t len = GET_UINT32_LE(indata + pe_ctx->sigpos + pos);uint16_t certrev = GET_UINT16_LE(indata + pe_ctx->sigpos + pos + 4);uint16_t certtype = GET_UINT16_LE(indata + pe_ctx->sigpos + pos + 6);if (certrev == WIN_CERT_REVISION_2_0 && certtype == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {// 如果找到一个签名版本合适的签名数据, 就出去了.// 可以看到, 只取了第一个合适版本的签名数据/* skip 8 bytes from the attribute certificate table */const u_char *blob = (u_char *)indata + pe_ctx->sigpos + pos + 8;return d2i_PKCS7(NULL, &blob, len - 8);}// 如果是多个签名的PE, 又没找到合适的签名数据版本, 就继续找剩下的签名数据./* quadword align data */len += len % 8 ? 8 - len % 8 : 0;pos += len;}return NULL; /* FAILED */
}
将签名数据保存成文件供调试
static PKCS7 *pe_pkcs7_get_file(char *indata, PE_CTX *pe_ctx)
{uint32_t pos = 0;if (pe_ctx->siglen == 0 || pe_ctx->siglen > pe_ctx->fileend) {printf("Corrupted signature length: 0x%08X\n", pe_ctx->siglen);return NULL; /* FAILED */}while (pos < pe_ctx->siglen) {uint32_t len = GET_UINT32_LE(indata + pe_ctx->sigpos + pos);uint16_t certrev = GET_UINT16_LE(indata + pe_ctx->sigpos + pos + 4);uint16_t certtype = GET_UINT16_LE(indata + pe_ctx->sigpos + pos + 6);if (certrev == WIN_CERT_REVISION_2_0 && certtype == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {/* skip 8 bytes from the attribute certificate table */const u_char *blob = (u_char *)indata + pe_ctx->sigpos + pos + 8;// 将下面d2i_PKCS7时的签名数据保存成文件, 单独用工具来查看.do {FILE* fp = fopen("my_code_sign.dat", "wb");size_t nWr = 0;if (NULL != fp){nWr = fwrite(indata + pe_ctx->sigpos + pos + 8, sizeof(char), len - 8, fp);assert(nWr == (len - 8));fclose(fp);fp = NULL;}} while (false);return d2i_PKCS7(NULL, &blob, len - 8);}/* quadword align data */len += len % 8 ? 8 - len % 8 : 0;pos += len;}return NULL; /* FAILED */
}
从网上找了一个Asn1View
打开保存的数据时报错如下:
看报错提示, 好像是程序做了额外的检查. 可能是这个签名数据后面有多余的没用内容.
用010Editor打开看, 后面是4个0,
应该这4个0, 只是为了对齐用的.
这4个0应该是没用的. 用010Editor将这4个0删掉.
另存为数据文件, 再用Asn1View打开正常.
这说明Asn1View判断的还挺严格的. 其实可以不报错(数据是够的, 只是附加有对齐用的数据), 将后面的数据标记为无用数据也行啊.
ASN.1 Editor有个好用功能, 可以将数据按照文本树来显示, 这样查找字符串就方便多了.
文本可以复制下来, 贴到文本文件中供调试用.
备注
这个工程关于code sign的验签, 写的挺复杂的. 以后慢慢理解吧.
估计自己得将关于验签的部分整体搬进自己工程来改. 用实验的方法来判断PE的完整性是否被篡改了.
- 改了文件内容, 签名不改, 是否能验签错误?
- 模拟别人移除签名, 改文件内容, 再用不是自己的APP证书来签名. 用自己的CA证书, 是否能判断出来签名失败?
END
相关文章:
openssl3.2 - osslsigncode工程的学习
文章目录 openssl3.2 - osslsigncode工程的学习概述笔记工程库地址工程的编译osslsigncodeM工程文件列表osslsigncodeM工程搭建细节原始工程实现的改动自己封装的包含openssl和curl的实现osslsigncodeM工程命令行的用法备注 - VS2019调试环境备注 - 如果要单步openssl的API学学…...
HTML 超文本标记语言
超文本标记语言 HTML 在一个客户程序主窗口上显示出的万维网文档称为页面 (page)。 页面制作的标准语言:HTML。 超文本标记语言 HTML (HyperText Markup Language) 是一种制作万维网页面的标准语言,它消除了不同计算机之间信息交流的障碍,…...
sklearn:机器学习 分类特征编码category_encoders
文章目录 category_encoders简介OrdinalEncoder 序列编码OneHotEncoder 独热编码TargetEncoder 目标编码Binary Encoder 二进制编码BaseNEncoder 贝叶斯编码LeaveOneOutEncoder 留一法HashingEncoder 哈希编码CatBoostEncoder catboost目标编码CountEncoder 频率编码WOEEncoder…...
C++错误[错误] call of overloaded ‘min(int, int)‘ is ambiguous
错误代码: #include<iostream> using namespace std;template <class T> T min(T x,T y){if(x<y){return x;}else return y; }int main(){int n12,n210;double d11.5,d25.6;cout<<min(n1,n2)<<endl;cout<<min(d1,d2)<<endl…...
2024全栈元年-thinkphp-数据操作
thinkphp 数据相关操作 1.单数据查询 1、单数据查询 ,Db::table(‘tp_stu’) 必须加前缀 2、如果只是查询符合条件的使用where find,如果没有符合条件的返回null 3、使用 findOrFail 没有数据会抛出异常 4、使用 findOrEmpty 没有数据会返回【】 5、得到最近一个原生SQL …...
HTML世界之第二重天
目录 一、HTML 格式化 1.HTML 文本格式化标签 2.HTML "计算机输出" 标签 3.HTML 引文, 引用, 及标签定义 二、HTML 链接 1.HTML 链接 2.HTML 超链接 3.HTML 链接语法 4.文本链接 5.图像链接 6.锚点链接 7.下载链接 8.Target 属性 9.Id 属性 三、HTML …...
社区经营的好处与优势:为何越来越多的人选择社区店?
社区店,这个曾经被视为小型、局限的商业模式,如今正逐渐崭露头角,成为众多创业者和消费者的首选。 特别是在鲜奶吧这样的细分市场中,社区店更是展现出了其独特的魅力和优势。作为一名拥有五年鲜奶吧经营经验的创业者,…...
C语言系列1——详解C语言:变量、常量与数据类型
目录 写在开始1. 变量与常量的概念1.1. 变量1.2. 常量1.3. 变量与常量的比较1.4. 选择变量还是常量 2. C语言中的基本数据类型2.1. 整型(Integer Types)2.2. 浮点型(Floating-Point Types)2.3. 字符型(Character Type&…...
WordPress修改所有用户名并发送邮件通知的插件Easy Username Updater
前面跟大家介绍了『如何修改WordPress后台管理员用户名?推荐2种简单方法』一文,但是对于有很多用户的站长来说,操作有点复杂,而且无法发邮件通知对方,所以今天boke112百科向大家推荐一款可以直接在WordPress后台修改所…...
C语言中的数据类型-强转
强制类型转换 概念:将某种类型的数据转化我们需要的数据类型,注意强制类型转化是临时强转,不会改变本身的数据类型。 强转又分为显式强转和隐式转化 显示强转是按照我们的要求进行转化 格式:(需要转化数据类型)变量名 #inclu…...
大数据可视化BI分析工具Apache Superset结合内网穿透实现远程访问
文章目录 前言1. 使用Docker部署Apache Superset1.1 第一步安装docker 、docker compose1.2 克隆superset代码到本地并使用docker compose启动 2. 安装cpolar内网穿透,实现公网访问3. 设置固定连接公网地址 前言 Superset是一款由中国知名科技公司开源的“现代化的…...
C# 线程与线程池的使用方法、注意事项
在C#中,线程和线程池是两种用于实现多线程编程的方式。线程用于执行并发任务,而线程池提供了一种更有效率的方式来管理和复用线程资源。 C# 线程(System.Threading.Thread) 创建和启动线程: Thread thread new Thre…...
2024年华为OD机试真题-按身高和体重排队-Python-OD统一考试(C卷)
题目描述: :某学校举行运动会,学生们按编号(1、2、3…n)进行标识,现需要按照身高由低到高排列,对身高相同的人,按体重由轻到重排列;对于身高体重都相同的人,维持原有的编号顺序关系。请输出排列后的学生编号。 输入描述:两个序列,每个序列由n个正整数组成(0 < n …...
openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O
文章目录 openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O218.1 查看I/O状况218.2 性能参数分析 openGauss学习笔记-218 openGauss性能调优-确定性能调优范围-硬件瓶颈点分析-I/O 获取openGauss节点的CPU、内存、I/O和网络资源使用情况…...
去除vue自带的边距
使用vue时发现总有去不掉的外边距,在index.vue里面怎样设置样式都不管用 查阅资料后发现要在vue项目自带的index.html文件内添加下面的样式代码才行 <style>*{margin: 0;padding: 0;}body,html{margin: 0;padding: 0;} </style>...
ElasticSearch级查询Query DSL上
目录 ES高级查询Query DSL match_all 返回源数据_source 返回指定条数size 分页查询from&size 指定字段排序sort 术语级别查询 Term query术语查询 Terms Query多术语查询 exists query ids query range query范围查询 prefix query前缀查询 wildcard query通…...
120.乐理基础-五线谱-五线谱的多声部与指法问题
内容参考于:三分钟音乐社 上一个内容:119.乐理基础-五线谱-五线谱的标记-CSDN博客 五线谱多声部与简谱的多声部一样:简谱的多声部 五线谱多声部例子:钢琴谱 另一个例子:在纵向上有多个音符 然后放大之后,…...
YOLOv5独家改进:上采样算子 | 超轻量高效动态上采样DySample,效果秒杀CAFFE,助力小目标检测
💡💡💡本文独家改进:一种超轻量高效动态上采样DySample, 具有更少的参数、FLOPs,效果秒杀CAFFE和YOLOv5网络中的nn.Upsample 💡💡💡在多个数据集下验证能够涨点,尤其在小目标检测领域涨点显著。 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/cate…...
洛谷 P1102 A-B 数对 (Java)
洛谷 P1102 A-B 数对 (Java) 传送门:P1102 A-B 数对 题目: A-B 数对 题目背景 出题是一件痛苦的事情! 相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 AB Problem,改用 A-B 了哈哈! 题目描…...
情人节到了,写一份爱心程序(python)
前言 情人节到了,写一份爱心代码给喜欢的人呀 公式 首先我们介绍下爱心的公式的参数方程: x 16 s i n 3 ( t ) x 16sin^3(t) x16sin3(t) y 13 c o s ( t ) − 5 c o s ( 2 t ) − 2 c o s ( 3 t ) − c o s ( 4 t ) y 13cos(t) - 5cos(2t) - 2co…...
Java图形化界面编程—— 基本组件和对话框 笔记
2.5 AWT中常用组件 2.5.1 基本组件 组件名功能ButtonButtonCanvas用于绘图的画布Checkbox复选框组件(也可当做单选框组件使用)CheckboxGroup选项组,用于将多个Checkbox 组件组合成一组, 一组 Checkbox 组件将只有一个可以 被选中…...
使用Docker,拉取Nginx镜像,创建并运行Nginx容器
查看DockerHub,拉取Nginx镜像,创建并运行Nginx容器。 在DockerHub中搜索Ningx镜像,查看镜像名称 拉取Nginx镜像 [rootservice ~]# systemctl start docker [rootservice ~]# mkdir -p /etc/docker [rootservice ~]# tee /etc/docker/daemo…...
InstantBox:开箱即用的临时 Linux 环境
在云计算和虚拟化技术日益成熟的今天,我们有时需要一个快速、简单、临时的 Linux 环境来进行各种任务。这就是 InstantBox 的用武之地。 什么是 InstantBox? InstantBox 是一个开源项目,它可以快速启动临时的 Linux 系统,并提供…...
【面试】国家公务员考试复试,面试内容准备方向(非技术面试考察点)
【面试】国家公务员考试复试,面试内容准备方向(非技术面试考察点) 说明: csdn无法发部分考试相关例题(提示涉z),所以本文主要还是针对评分标准和仪表等相关因素。 文章目录 1、面试试卷与评分1.…...
点餐|外卖订餐小程序|基于微信小程序的外卖订餐系统设计与实现(源码+数据库+文档)
点餐|外卖订餐小程序目录 目录 基于微信小程序的外卖订餐系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户微信端功能模块 2、管理员服务端功能模块 3、商家务端功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设…...
使用Cargo创建、编译与运行Rust项目
在 Rust 开发中,Cargo 是一个非常重要的工具,它负责项目的构建、管理和依赖管理。以下是如何使用 Cargo 创建、编译和运行 Rust 项目的详细步骤。 1. 创建新项目 首先确保你已经在计算机上安装了 Rust 和 Cargo。然后,在命令行中输入以下命…...
Js中toFixed(2)精度问题的原因及解决办法
toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。例如将数据Num保留2位小数,则表示为:toFixed(Num);但是其四舍五入的规则与数学中的规则不同,使用的是银行家舍入规则,银行家舍入:所谓银行家舍入…...
【医学大模型 知识增强】SMedBERT:结构化语义知识 + 医学大模型 = 显著提升大模型医学文本挖掘性能
SMedBERT:结构化语义知识 医学大模型 显著提升医学文本挖掘任务性能 名词解释结构化语义知识预训练语言模型医学文本挖掘任务 提出背景具体步骤提及-邻居混合注意力机制实体嵌入增强实体描述增强三元组句子增强 提及-邻居上下文建模域内词汇权重学习领域自监督任务…...
Python爬虫:安全与会话管理
源码分享 https://docs.qq.com/sheet/DUHNQdlRUVUp5Vll2?tabBB08J2 在进行网站数据抓取时,会话管理是保持与目标网站通信连续性的一种机制。这对于模拟登录、保持用户状态、维护cookie等场景至关重要。同时,安全性也是我们不可忽视的一个方面…...
[Python进阶] 识别验证码
11.3 识别验证码 我们再开发某些项目的时候,如果遇到要登录某些网页,那么会经常遇到输入验证码的情况,而每次人工输入验证码的话,比较浪费时间。于是,可以通过调用某些接口进行识别。 11.3.1 调用百度文字识别接口 …...
引物在线设计网站/百度惠生活推广怎么收费
一、背景 客户要求Home键退出app时再次打开时要重现界面,最开始想的每次打开activity就将activity放入集合中,操作难度很大也很麻烦。后来发现android:alwaysRetainTaskState这个属性。 二、使用说明 这个属性用来标记应用的task是否保持原来的状态&a…...
中国佛山营销网站建设/搭建一个网站
“下面是将一行输入读取到数组中的代码:” char charr[20]; cin.getline(charr, 20); “这种句点表示法表明,函数getline()是istream类的一个类方法。” “下面是将一行输入读取到string对象中的代码:” string str; getline(cin, str);“…...
网站开发工程师招聘要求/网站软件下载大全
目录 描述 语法 参数 返回值 使用示例 注意事项 1. 当分隔符不存在于原字符串中时 2. 当分隔符是空字符串时 3. 参数都为空字符串 4. 当s为空字符串,sep不为空字符串时 描述 strings.Split函数用于将指定的分隔符切割字符串,并返回切割后的字…...
设计师专用网站/网站优化包括对什么优化
变动通知:为整合社区服务,筹备上线更便捷实用的礼品兑换功能,礼品兑换活动已暂停。51CTO博客无忧币奖励计划全新登场!为答谢博客用户对51CTO长久的支持和关注,特推出无忧币兑换礼品活动,无忧币给你带来丰富…...
国内永久免费网络游戏推荐/广州seo外包多少钱
https://aprico-media.com/posts/2518...
wordpress action filter/百度大盘指数
正如书上所说,本题需要用IDA*算法求解 启发函数是3dh>3maxd(d为当前操作步骤数,h为当前逆序对数,maxd为当前枚举的最大步骤数) 可见迭代递归的核心思想是枚举ans去dfs是否可行,相反常规搜索是dfs去需找a…...