需求是这样的,原本的项目由两部分程序构成,一个是unity主程序,一个是用c#写的控制台程序作为untiy的启动器,然后现在要在启动器里隐藏一个序列化到配置文件的id。
我的设想是这样的,既然涉及到加解密,那就一定要上c++ 了,增加破解难度,目标平台是pc,就打出dll提供给两个程序使用。
一开始选择用获取主板id作为密钥,然后用aes做对称加密。
找到了一个简单的 aes c++实现
AES.h
#ifndef _AES_H_
#define _AES_H_
#include<cstring>
#include <iostream>
#include <stdio.h>
using namespace std;
class AES
{
private:
int Nb;
int Nk;
int Nr;
unsigned int blockBytesLen;
void SubBytes(unsigned char **state);
void ShiftRow(unsigned char **state, int i, int n); // shift row i on n positions
void ShiftRows(unsigned char **state);
unsigned char xtime(unsigned char b); // multiply on x
unsigned char mul_bytes(unsigned char a, unsigned char b);
void MixColumns(unsigned char **state);
void MixSingleColumn(unsigned char *r);
void AddRoundKey(unsigned char **state, unsigned char *key);
void SubWord(unsigned char *a);
void RotWord(unsigned char *a);
void XorWords(unsigned char *a, unsigned char *b, unsigned char *c);
void Rcon(unsigned char * a, int n);
void InvSubBytes(unsigned char **state);
void InvMixColumns(unsigned char **state);
void InvShiftRows(unsigned char **state);
unsigned char* PaddingNulls(unsigned char in[], unsigned int inLen, unsigned int alignLen);
unsigned int GetPaddingLength(unsigned int len);
void KeyExpansion(unsigned char key[], unsigned char w[]);
void EncryptBlock(unsigned char in[], unsigned char out[], unsigned char key[]);
void DecryptBlock(unsigned char in[], unsigned char out[], unsigned char key[]);
void XorBlocks(unsigned char *a, unsigned char * b, unsigned char *c, unsigned int len);
public:
AES(int keyLen = 256);
unsigned char *EncryptECB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned int &outLen);
unsigned char *DecryptECB(unsigned char in[], unsigned int inLen, unsigned char key[]);
unsigned char *EncryptCBC(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv, unsigned int &outLen);
unsigned char *DecryptCBC(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv);
unsigned char *EncryptCFB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv, unsigned int &outLen);
unsigned char *DecryptCFB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv);
void printHexArray (unsigned char a[], unsigned int n);
};
const unsigned char sbox[16][16] = {
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
const unsigned char inv_sbox[16][16] = {
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, };
#endif
AES.cpp
#include "shared/AES.h"
AES::AES(int keyLen)
{
this->Nb = 4;
switch (keyLen)
{
case 128:
this->Nk = 4;
this->Nr = 10;
break;
case 192:
this->Nk = 6;
this->Nr = 12;
break;
case 256:
this->Nk = 8;
this->Nr = 14;
break;
default:
throw "Incorrect key length";
}
blockBytesLen = 4 * this->Nb * sizeof(unsigned char);
}
unsigned char * AES::EncryptECB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned int &outLen)
{
outLen = GetPaddingLength(inLen);
unsigned char *alignIn = PaddingNulls(in, inLen, outLen);
unsigned char *out = new unsigned char[outLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
for (unsigned int i = 0; i < outLen; i+= blockBytesLen)
{
EncryptBlock(alignIn + i, out + i, roundKeys);
}
delete[] alignIn;
delete[] roundKeys;
return out;
}
unsigned char * AES::DecryptECB(unsigned char in[], unsigned int inLen, unsigned char key[])
{
unsigned char *out = new unsigned char[inLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
for (unsigned int i = 0; i < inLen; i+= blockBytesLen)
{
DecryptBlock(in + i, out + i, roundKeys);
}
delete[] roundKeys;
return out;
}
unsigned char *AES::EncryptCBC(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv, unsigned int &outLen)
{
outLen = GetPaddingLength(inLen);
unsigned char *alignIn = PaddingNulls(in, inLen, outLen);
unsigned char *out = new unsigned char[outLen];
unsigned char *block = new unsigned char[blockBytesLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
memcpy(block, iv, blockBytesLen);
for (unsigned int i = 0; i < outLen; i+= blockBytesLen)
{
XorBlocks(block, alignIn + i, block, blockBytesLen);
EncryptBlock(block, out + i, roundKeys);
memcpy(block, out + i, blockBytesLen);
}
delete[] block;
delete[] alignIn;
delete[] roundKeys;
return out;
}
unsigned char *AES::DecryptCBC(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv)
{
unsigned char *out = new unsigned char[inLen];
unsigned char *block = new unsigned char[blockBytesLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
memcpy(block, iv, blockBytesLen);
for (unsigned int i = 0; i < inLen; i+= blockBytesLen)
{
DecryptBlock(in + i, out + i, roundKeys);
XorBlocks(block, out + i, out + i, blockBytesLen);
memcpy(block, in + i, blockBytesLen);
}
delete[] block;
delete[] roundKeys;
return out;
}
unsigned char *AES::EncryptCFB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv, unsigned int &outLen)
{
outLen = GetPaddingLength(inLen);
unsigned char *alignIn = PaddingNulls(in, inLen, outLen);
unsigned char *out = new unsigned char[outLen];
unsigned char *block = new unsigned char[blockBytesLen];
unsigned char *encryptedBlock = new unsigned char[blockBytesLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
memcpy(block, iv, blockBytesLen);
for (unsigned int i = 0; i < outLen; i+= blockBytesLen)
{
EncryptBlock(block, encryptedBlock, roundKeys);
XorBlocks(alignIn + i, encryptedBlock, out + i, blockBytesLen);
memcpy(block, out + i, blockBytesLen);
}
delete[] block;
delete[] encryptedBlock;
delete[] alignIn;
delete[] roundKeys;
return out;
}
unsigned char *AES::DecryptCFB(unsigned char in[], unsigned int inLen, unsigned char key[], unsigned char * iv)
{
unsigned char *out = new unsigned char[inLen];
unsigned char *block = new unsigned char[blockBytesLen];
unsigned char *encryptedBlock = new unsigned char[blockBytesLen];
unsigned char *roundKeys = new unsigned char[4 * Nb * (Nr + 1)];
KeyExpansion(key, roundKeys);
memcpy(block, iv, blockBytesLen);
for (unsigned int i = 0; i < inLen; i+= blockBytesLen)
{
EncryptBlock(block, encryptedBlock, roundKeys);
XorBlocks(in + i, encryptedBlock, out + i, blockBytesLen);
memcpy(block, in + i, blockBytesLen);
}
delete[] block;
delete[] encryptedBlock;
delete[] roundKeys;
return out;
}
unsigned char * AES::PaddingNulls(unsigned char in[], unsigned int inLen, unsigned int alignLen)
{
unsigned char *alignIn = new unsigned char[alignLen];
memcpy(alignIn, in, inLen);
memset(alignIn + inLen, 0x00, alignLen - inLen);
return alignIn;
}
unsigned int AES::GetPaddingLength(unsigned int len)
{
unsigned int lengthWithPadding = (len / blockBytesLen);
if (len % blockBytesLen) {
lengthWithPadding++;
}
lengthWithPadding *= blockBytesLen;
return lengthWithPadding;
}
void AES::EncryptBlock(unsigned char in[], unsigned char out[], unsigned char *roundKeys)
{
unsigned char **state = new unsigned char *[4];
state[0] = new unsigned char[4 * Nb];
int i, j, round;
for (i = 0; i < 4; i++)
{
state[i] = state[0] + Nb * i;
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++)
{
state[i][j] = in[i + 4 * j];
}
}
AddRoundKey(state, roundKeys);
for (round = 1; round <= Nr - 1; round++)
{
SubBytes(state);
ShiftRows(state);
MixColumns(state);
AddRoundKey(state, roundKeys + round * 4 * Nb);
}
SubBytes(state);
ShiftRows(state);
AddRoundKey(state, roundKeys + Nr * 4 * Nb);
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++)
{
out[i + 4 * j] = state[i][j];
}
}
delete[] state[0];
delete[] state;
}
void AES::DecryptBlock(unsigned char in[], unsigned char out[], unsigned char *roundKeys)
{
unsigned char **state = new unsigned char *[4];
state[0] = new unsigned char[4 * Nb];
int i, j, round;
for (i = 0; i < 4; i++)
{
state[i] = state[0] + Nb * i;
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++) {
state[i][j] = in[i + 4 * j];
}
}
AddRoundKey(state, roundKeys + Nr * 4 * Nb);
for (round = Nr - 1; round >= 1; round--)
{
InvSubBytes(state);
InvShiftRows(state);
AddRoundKey(state, roundKeys + round * 4 * Nb);
InvMixColumns(state);
}
InvSubBytes(state);
InvShiftRows(state);
AddRoundKey(state, roundKeys);
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++) {
out[i + 4 * j] = state[i][j];
}
}
delete[] state[0];
delete[] state;
}
void AES::SubBytes(unsigned char **state)
{
int i, j;
unsigned char t;
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++)
{
t = state[i][j];
state[i][j] = sbox[t / 16][t % 16];
}
}
}
void AES::ShiftRow(unsigned char **state, int i, int n) // shift row i on n positions
{
unsigned char *tmp = new unsigned char[Nb];
for (int j = 0; j < Nb; j++) {
tmp[j] = state[i][(j + n) % Nb];
}
memcpy(state[i], tmp, Nb * sizeof(unsigned char));
delete[] tmp;
}
void AES::ShiftRows(unsigned char **state)
{
ShiftRow(state, 1, 1);
ShiftRow(state, 2, 2);
ShiftRow(state, 3, 3);
}
unsigned char AES::xtime(unsigned char b) // multiply on x
{
return (b << 1) ^ (((b >> 7) & 1) * 0x1b);
}
/* Implementation taken from https://en.wikipedia.org/wiki/Rijndael_mix_columns#Implementation_example */
void AES::MixSingleColumn(unsigned char *r)
{
unsigned char a[4];
unsigned char b[4];
unsigned char c;
unsigned char h;
/* The array 'a' is simply a copy of the input array 'r'
* The array 'b' is each element of the array 'a' multiplied by 2
* in Rijndael's Galois field
* a[n] ^ b[n] is element n multiplied by 3 in Rijndael's Galois field */
for(c=0;c<4;c++)
{
a[c] = r[c];
/* h is 0xff if the high bit of r[c] is set, 0 otherwise */
h = (unsigned char)((signed char)r[c] >> 7); /* arithmetic right shift, thus shifting in either zeros or ones */
b[c] = r[c] << 1; /* implicitly removes high bit because b[c] is an 8-bit char, so we xor by 0x1b and not 0x11b in the next line */
b[c] ^= 0x1B & h; /* Rijndael's Galois field */
}
r[0] = b[0] ^ a[3] ^ a[2] ^ b[1] ^ a[1]; /* 2 * a0 + a3 + a2 + 3 * a1 */
r[1] = b[1] ^ a[0] ^ a[3] ^ b[2] ^ a[2]; /* 2 * a1 + a0 + a3 + 3 * a2 */
r[2] = b[2] ^ a[1] ^ a[0] ^ b[3] ^ a[3]; /* 2 * a2 + a1 + a0 + 3 * a3 */
r[3] = b[3] ^ a[2] ^ a[1] ^ b[0] ^ a[0]; /* 2 * a3 + a2 + a1 + 3 * a0 */
}
/* Performs the mix columns step. Theory from: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard#The_MixColumns_step */
void AES::MixColumns(unsigned char** state)
{
unsigned char *temp = new unsigned char[4];
for(int i = 0; i < 4; ++i)
{
for(int j = 0; j < 4; ++j)
{
temp[j] = state[j][i]; //place the current state column in temp
}
MixSingleColumn(temp); //mix it using the wiki implementation
for(int j = 0; j < 4; ++j)
{
state[j][i] = temp[j]; //when the column is mixed, place it back into the state
}
}
delete[] temp;
}
void AES::AddRoundKey(unsigned char **state, unsigned char *key)
{
int i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++)
{
state[i][j] = state[i][j] ^ key[i + 4 * j];
}
}
}
void AES::SubWord(unsigned char *a)
{
int i;
for (i = 0; i < 4; i++)
{
a[i] = sbox[a[i] / 16][a[i] % 16];
}
}
void AES::RotWord(unsigned char *a)
{
unsigned char c = a[0];
a[0] = a[1];
a[1] = a[2];
a[2] = a[3];
a[3] = c;
}
void AES::XorWords(unsigned char *a, unsigned char *b, unsigned char *c)
{
int i;
for (i = 0; i < 4; i++)
{
c[i] = a[i] ^ b[i];
}
}
void AES::Rcon(unsigned char * a, int n)
{
int i;
unsigned char c = 1;
for (i = 0; i < n - 1; i++)
{
c = xtime(c);
}
a[0] = c;
a[1] = a[2] = a[3] = 0;
}
void AES::KeyExpansion(unsigned char key[], unsigned char w[])
{
unsigned char *temp = new unsigned char[4];
unsigned char *rcon = new unsigned char[4];
int i = 0;
while (i < 4 * Nk)
{
w[i] = key[i];
i++;
}
i = 4 * Nk;
while (i < 4 * Nb * (Nr + 1))
{
temp[0] = w[i - 4 + 0];
temp[1] = w[i - 4 + 1];
temp[2] = w[i - 4 + 2];
temp[3] = w[i - 4 + 3];
if (i / 4 % Nk == 0)
{
RotWord(temp);
SubWord(temp);
Rcon(rcon, i / (Nk * 4));
XorWords(temp, rcon, temp);
}
else if (Nk > 6 && i / 4 % Nk == 4)
{
SubWord(temp);
}
w[i + 0] = w[i - 4 * Nk] ^ temp[0];
w[i + 1] = w[i + 1 - 4 * Nk] ^ temp[1];
w[i + 2] = w[i + 2 - 4 * Nk] ^ temp[2];
w[i + 3] = w[i + 3 - 4 * Nk] ^ temp[3];
i += 4;
}
delete []rcon;
delete []temp;
}
void AES::InvSubBytes(unsigned char **state)
{
int i, j;
unsigned char t;
for (i = 0; i < 4; i++)
{
for (j = 0; j < Nb; j++)
{
t = state[i][j];
state[i][j] = inv_sbox[t / 16][t % 16];
}
}
}
unsigned char AES::mul_bytes(unsigned char a, unsigned char b) // multiplication a and b in galois field
{
unsigned char p = 0;
unsigned char high_bit_mask = 0x80;
unsigned char high_bit = 0;
unsigned char modulo = 0x1B; /* x^8 + x^4 + x^3 + x + 1 */
for (int i = 0; i < 8; i++) {
if (b & 1) {
p ^= a;
}
high_bit = a & high_bit_mask;
a <<= 1;
if (high_bit) {
a ^= modulo;
}
b >>= 1;
}
return p;
}
void AES::InvMixColumns(unsigned char **state)
{
unsigned char s[4], s1[4];
int i, j;
for (j = 0; j < Nb; j++)
{
for (i = 0; i < 4; i++)
{
s[i] = state[i][j];
}
s1[0] = mul_bytes(0x0e, s[0]) ^ mul_bytes(0x0b, s[1]) ^ mul_bytes(0x0d, s[2]) ^ mul_bytes(0x09, s[3]);
s1[1] = mul_bytes(0x09, s[0]) ^ mul_bytes(0x0e, s[1]) ^ mul_bytes(0x0b, s[2]) ^ mul_bytes(0x0d, s[3]);
s1[2] = mul_bytes(0x0d, s[0]) ^ mul_bytes(0x09, s[1]) ^ mul_bytes(0x0e, s[2]) ^ mul_bytes(0x0b, s[3]);
s1[3] = mul_bytes(0x0b, s[0]) ^ mul_bytes(0x0d, s[1]) ^ mul_bytes(0x09, s[2]) ^ mul_bytes(0x0e, s[3]);
for (i = 0; i < 4; i++)
{
state[i][j] = s1[i];
}
}
}
void AES::InvShiftRows(unsigned char **state)
{
ShiftRow(state, 1, Nb - 1);
ShiftRow(state, 2, Nb - 2);
ShiftRow(state, 3, Nb - 3);
}
void AES::XorBlocks(unsigned char *a, unsigned char * b, unsigned char *c, unsigned int len)
{
for (unsigned int i = 0; i < len; i++)
{
c[i] = a[i] ^ b[i];
}
}
void AES::printHexArray (unsigned char a[], unsigned int n)
{
for (unsigned int i = 0; i < n; i++) {
printf("%02x ", a[i]);
}
}
接着遇到了问题,因为aes的函数参数和返回都是char指针,在c#和c++通讯中产生了只获取到第一位char的情况,所以放弃了这种经典的方式,改用都传char数组的方式,基本数据类型比较保险,对阿斯克码进行加密处理。
后来在c#控制台中运行良好,但是在unity中运行导致程序奔溃,经测试是获取主板id调用了WMI服务导致的,所以为了避免这个坑就换成mac地址来做密钥。
hw_info.h
#ifndef __HW_INFO_H__
#define __HW_INFO_H__
#include <string>
#ifndef _LINUX_
#define _WIN32_DCOM
#include <WbemIdl.h>
#include <comdef.h>
extern HRESULT hres;
extern IWbemLocator* pLoc;
extern IWbemServices* pSvc;
#endif
bool hw_init();
bool hw_cleanup();
bool get_board_serial_number(std::string & board_serial);
bool get_cpu_id(std::string & cpu_id);
bool get_disk_id(std::string & cpu_id);
bool get_mac_address(std::string & mac_address);
#endif
hw_info.cpp
#include "shared/hw_info.h"
#include <iostream>
#pragma comment(lib, "wbemuuid.lib")
using namespace std;
HRESULT hres;
IWbemLocator* pLoc = NULL;
IWbemServices* pSvc = NULL;
static std::wstring CharToWchar(const char* c, size_t m_encode = CP_ACP) {
std::wstring str;
int len = MultiByteToWideChar(m_encode, 0, c, strlen(c), NULL, 0);
wchar_t* m_wchar = new wchar_t[len + 1];
MultiByteToWideChar(m_encode, 0, c, strlen(c), m_wchar, len);
m_wchar[len] = '\0';
str = m_wchar;
delete m_wchar;
return str;
}
static std::string WcharToChar(const wchar_t* wp, size_t m_encode = CP_ACP) {
std::string str;
int len = WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), NULL, 0, NULL, NULL);
char* m_char = new char[len + 1];
WideCharToMultiByte(m_encode, 0, wp, wcslen(wp), m_char, len, NULL, NULL);
m_char[len] = '\0';
str = m_char;
delete m_char;
return str;
}
bool hw_init() {
bool ret = false;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres)) {
cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl;
return ret; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
if (FAILED(hres)) {
cout << "Failed to initialize security. Error code = 0x" << hex << hres << endl;
CoUninitialize();
return ret; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI ------------------------
hres = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLoc);
if (FAILED(hres)) {
cout << "Failed to create IWbemLocator object."
<< " Err code = 0x" << hex << hres << endl;
CoUninitialize();
return ret; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
// Connect to the root\cimv2 namespace with
// the current user and obtain pointer pSvc
// to make IWbemServices calls.
hres = pLoc->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
NULL, // User name. NULL = current user
NULL, // User password. NULL = current
0, // Locale. NULL indicates current
NULL, // Security flags.
0, // Authority (for example, Kerberos)
0, // Context object
&pSvc // pointer to IWbemServices proxy
);
if (FAILED(hres)) {
cout << "Could not connect. Error code = 0x" << hex << hres << endl;
pLoc->Release();
CoUninitialize();
return ret; // Program has failed.
}
cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// Step 5: --------------------------------------------------
// Set security levels on the proxy -------------------------
hres = CoSetProxyBlanket(pSvc, // Indicates the proxy to set
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres)) {
cout << "Could not set proxy blanket. Error code = 0x" << hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return ret; // Program has failed.
}
return true;
}
bool hw_cleanup() {
if (pSvc != NULL) {
pSvc->Release();
}
if (pLoc != NULL) {
pLoc->Release();
}
CoUninitialize();
return true;
}
bool get_os_disk(int& num) {
bool ret = false;
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT Name FROM Win32_OperatingSystem"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL,
&pEnumerator);
if (FAILED(hres)) {
cout << "Query for board serialnum failed."
<< " Error code = 0x" << hex << hres << endl;
return false; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn) {
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
std::string name = WcharToChar(vtProp.bstrVal);
VariantClear(&vtProp);
pclsObj->Release();
int pos = name.find("Harddisk") + strlen("Harddisk");
name = name.substr(pos );
num = atoi(name.c_str());
ret = true;
}
pEnumerator->Release();
return ret;
}
bool get_disk_id(std::string& serial) {
int num = 0;
bool ret = false;
if (!get_os_disk(num)) {
return false;
}
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT SerialNumber,DeviceID FROM Win32_DiskDrive"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL,
&pEnumerator);
if (FAILED(hres)) {
cout << "Query for board serialnum failed."
<< " Error code = 0x" << hex << hres << endl;
return false; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn) {
break;
}
VARIANT vtProp;
// Get the value of the DeviceId property
hr = pclsObj->Get(L"DeviceId", 0, &vtProp, 0, 0);
std::string deviceId = WcharToChar(vtProp.bstrVal);
VariantClear(&vtProp);
int pos = deviceId.find("PHYSICALDRIVE") + strlen("PHYSICALDRIVE");
deviceId = deviceId.substr(pos);
if (num == atoi(deviceId.c_str())) {
// Get the value of the Name property
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
serial = WcharToChar(vtProp.bstrVal);
VariantClear(&vtProp);
ret = true;
break;
}
pclsObj->Release();
}
pEnumerator->Release();
return ret;
}
bool get_board_serial_number(std::string& board_serial) {
bool ret = false;
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_BaseBoard"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL,
&pEnumerator);
if (FAILED(hres)) {
cout << "Query for board serialnum failed."
<< " Error code = 0x" << hex << hres << endl;
return false; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn) {
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"SerialNumber", 0, &vtProp, 0, 0);
board_serial = WcharToChar(vtProp.bstrVal);
VariantClear(&vtProp);
pclsObj->Release();
ret = true;
}
pEnumerator->Release();
return ret;
}
bool get_cpu_id(std::string& cpu_id) {
bool ret = false;
IEnumWbemClassObject* pEnumerator = NULL;
hres = pSvc->ExecQuery(bstr_t("WQL"), bstr_t("SELECT * FROM Win32_Processor"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL,
&pEnumerator);
if (FAILED(hres)) {
cout << "Query for board serialnum failed."
<< " Error code = 0x" << hex << hres << endl;
return false; // Program has failed.
}
// Step 7: -------------------------------------------------
// Get the data from the query in step 6 -------------------
IWbemClassObject* pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator) {
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
if (0 == uReturn) {
break;
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"ProcessorId", 0, &vtProp, 0, 0);
cpu_id = WcharToChar(vtProp.bstrVal);
VariantClear(&vtProp);
pclsObj->Release();
ret = true;
}
pEnumerator->Release();
return ret;
}
bool get_mac_address(std::string& mac_address) {
return true;
}
中途因为unity是64位的,然后c#启动器是32位的,为了统一兼容,把c#启动器和c++ 加解密库都升级成64位
最后制作c++ 加解密库的步骤如下
- 在visual studio 2019中创建如下工程
需要在visual studio installer中安装桌面开发套件
这种方式打出来的dll是非托管模式的动态库,用起来没有托管模式下的方便,在unity中就直接放入Plugins目录下,然后使用的话如下
[DllImport("VframeDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr Vdecrypt(int[] sid, int size);
[DllImport("VframeDll.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr test(int[] sid, int size);
但是在c#控制台程序中也是这么用
c#程序将所有dll打进一个exe中,托管模式下的dll可以用nuget安装Costura.Fody,完事直接打包就可以,然而我们c++ 提供了非托管模式的dll,据说fody也可以打进非托管模式的dll,但是我尝试了很久都没有成功,就换个思路,将这个dll以资源的形式打进exe,然后在程序启动的时候动态释放出来加载,
在visual studio里右键解决方案选属性
然后选资源,添加资源
在c#中这样加载
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpFileName);
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern void FreeLibrary(IntPtr module);
static void UnzipAndLoad()
{
// var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var folderPath = Path.GetDirectoryName(@"C:\ProgramData\");
var dllPath = Path.Combine(folderPath, $"{nameof(Resources.VframeDll)}.dll");//解压输出的路径
if (File.Exists(dllPath))
{
File.Delete(dllPath);
}
if (!File.Exists(dllPath))
File.WriteAllBytes(dllPath, Resources.VframeDll);
LoadDll(dllPath);//应该每次都加载非托管
}
private static IntPtr vframeDllPtr;
/// <summary>
/// 加载非托管DLL
/// </summary>
/// <param name="dllName"></param>
public static void LoadDll(string dllName)
{
vframeDllPtr = LoadLibrary(dllName);
if (vframeDllPtr == IntPtr.Zero)
{
Exception e = new Win32Exception();
Console.WriteLine($"Unable to load library: {dllName} {e}");
}
Console.WriteLine("Load library successful");
}
打赏