Можно ли найти и использовать код функции из стандартной библиотеки?
Можно ли не вызывать функцию из библиотеки, а вставить в программу ее код. Например вместо GetComputerName найти ее код в библиотеке и вставить в программу? Не знаю насколько скрыт код библиотек.
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{
char buffer[256];
unsigned long size = 256;
GetComputerName( buffer, &size );
printf( "%s\n", buffer );
system( "pause" );
return 0;
}
Ответы (1 шт):
Привожу ранее обсуждённые в комментариях решения в виде моего ответа.
Ниже в коде я реализовал два самых простейших метода для получения имени компьютера без использования GetComputerName функции.
Первый вариант предложенный @AlexeyVesker это использование std::getenv для чтения переменной окружения под именем "COMPUTERNAME".
Второй вариант это чтение ключа реестра "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName", используя WinApi функции RegOpenKeyExA и RegQueryValueExA.
Если имя компьютера может содержать русские символы (или другие юникод) то нужно использовать варианты упомянутых выше функций с поддержкой юникода - _wgetenv_s, RegOpenKeyExW, RegQueryValueExW.
#define _CRT_SECURE_NO_WARNINGS 1
#include <string>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <cstdlib>
#include <windows.h>
#define ASSERT(cond) { if (!(cond)) throw std::runtime_error("Failed at line " + std::to_string(__LINE__)); }
inline static std::string GetComputerNameVar1() {
auto r = std::getenv("COMPUTERNAME");
ASSERT(r);
return r;
}
inline static std::string GetComputerNameVar2() {
HKEY hKey = 0;
ASSERT(RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName", 0, KEY_READ, &hKey) == ERROR_SUCCESS);
CHAR computer_name[1024] = {};
DWORD buf_size = sizeof(computer_name);
ASSERT(RegQueryValueExA(hKey, "ComputerName", 0, NULL, (LPBYTE)computer_name, &buf_size) == ERROR_SUCCESS);
return computer_name;
}
int main() {
try {
SetConsoleOutputCP(65001);
std::cout << GetComputerNameVar1() << std::endl;
std::cout << GetComputerNameVar2() << std::endl;
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Как было сказано, кроме сокрытия GetComputerName вызова, ещё нужно скрывать имена строк-констант "COMPUTERNAME" а также пути в реестре.
Для сокрытия строк можно использовать любой вид шифрования, просто для сокрытия от новичков (чтобы не было видно открытой строки в EXE файле) достаточно использовать самый простейший криптографически не-стойкий шифр, а именно шифр Цезаря, просто к каждому символу прибавляем например +3, зашифрованную строку сохраняем в константу, и потом в коде при использовании строковой константы делаем обратную (-3) операцию.
Пример кода для зашифрования (попробовать онлайн!):
std::string s = "COMPUTERNAME"; for (auto & c: s) c += 3;
std::cout << s;
Получаем на выходе строку "FRPSXWHUQDPH", затем в использующем его коде делаем обратную операцию (попробовать онлайн!):
std::string s = "FRPSXWHUQDPH"; for (auto & c: s) c -= 3;
std::cout << s;
получаем на выходе "COMPUTERNAME".
Аналогично шифруется и путь в реестре из первой программы выше. И любые строки использованные в программе и необходимые к сокрытию.
PS
Просто ради интереса я решил реализовать относительно стойкий шифр, для шифрования строк. Он использует 64-битный ключ, 32-битную синхропосылку, 16-битную имитовставку (усечённый Adler-32) и является шифром простого гаммирования используя сгенерированную случайную последовательность с помощью псевдо-случайного датчика 64-битного вихря Мерсенна.
Если 64-битный ключ хорошо запрятать в программе, например несколько раз сделать математические преобразования, так чтобы в EXE дампе не было его возможно найти в исходном виде, то кроме как дизассемблирования EXE либо полного 2^64 перебора пространства ключей, скорее всего уже не взломаешь шифр.
Приведённый код ниже может быть использован не только для шифрования строк, но и любых (включая бинарные) данные, только нужно учесть что шифр не слишком стойкий, т.к. всего 64-битный ключ, также синхропосылка всего 32 бит (значит на одном ключе нельзя шифровать больше чем 2^16-2^20 различных строк). Но для сокрытия от лишних глаз шифр вполне годится и занимает не много строк кода (и может быть просто вставлен в код).
Также шифр почти без изменений я могу улучшить до 128-битного (или сколько надо) ключа, это не сложно, если такой интерес будет.
Код ниже следует использовать так - 1) вначале при первом запуске генерируем 64-битный ключ, это то что выводится в первой строке в консоле, Random Encryption Key: ...... 2) сгенерированный ключ вбиваем в код например auto key = 0x71A1B4D23BC793E1ULL; 3) дальше на втором запуске шифруем все нужные нам строки, просто вызываем для примера Decrypt(key, Encrypt(key, "COMPUTERNAME")) (здесь Decrypt не обязателен, просто для доп контроля что зашифрованная строка успешно расшифровывается) 4) вызов функции зашифрования выведет в консоль зашифрованную последовательность 5) эту последовательность вбиваем в использующий строки код как например auto s_COMPUTERNAME = Decrypt(key, {0x78, 0xBF, 0xA3, 0x83, 0xE7, 0x10, 0x9C, 0xAE, 0x85, 0xF7, 0x07, 0x0D, 0x22, 0x20, 0xB7, 0x9F, 0xFA, 0x7E, });. 6) всё, s_COMPUTERNAME это строка типа std::string содержащая исходную "COMPUTERNAME" строку.
Если есть предложения, что не так в этом шифре, сообщайте.
#include <random>
#include <iostream>
#include <cstdint>
#include <vector>
#include <iomanip>
#include <stdexcept>
#include <cstring>
inline uint64_t GenRandomU64() {
std::random_device rd;
std::mt19937_64 rg((uint64_t(rd()) << 32) + uint64_t(rd()));
rg.discard(1000);
return rg();
}
template <typename T>
inline T UnalignedRead(void const * ptr) {
T v = T();
memcpy(&v, ptr, sizeof(T));
return v;
}
template <typename T>
inline void UnalignedWrite(void * ptr, T const & v) {
memcpy(ptr, &v, sizeof(T));
}
inline void CheckSystemRequirements() {
static_assert(std::numeric_limits<unsigned char>::digits == 8, "Only 8-bit byte is supported!");
static_assert(
std::numeric_limits<uint8_t>::digits == 8 &&
std::numeric_limits<uint16_t>::digits == 16 &&
std::numeric_limits<uint32_t>::digits == 32 &&
std::numeric_limits<uint64_t>::digits == 64, "Uints of unsupported bit sizes!");
uint64_t x = 0x0123456789ABCDEFULL;
if (UnalignedRead<uint8_t>(&x) != 0xEF)
throw std::runtime_error("Only little endian systems are supported!");
}
inline std::vector<uint8_t> Encrypt(uint64_t key, std::string const & s) {
CheckSystemRequirements();
uint32_t iv = uint32_t(GenRandomU64());
std::vector<uint8_t> r((uint8_t*)&iv, ((uint8_t*)&iv) + sizeof(iv));
std::mt19937_64 rng(key + iv);
rng.discard(1000);
for (size_t i = 0; i < (s.size() + 2 + 7) / 8; ++i) {
auto w = rng();
r.insert(r.end(), (uint8_t*)&w, ((uint8_t*)&w) + sizeof(w));
}
r.resize(sizeof(iv) + s.size() + 2);
uint32_t mac0 = 1, mac = 0;
for (size_t i = 0; i < s.size(); ++i) {
mac0 = (mac0 + uint8_t(s[i])) % 65521; mac = (mac + mac0) % 65521;
r[sizeof(iv) + i] ^= uint8_t(s[i]);
}
UnalignedWrite<uint16_t>(r.data() + r.size() - 2, UnalignedRead<uint16_t>(r.data() + r.size() - 2) ^ uint16_t(mac));
std::cout << "Encrypted \"" << s << "\": {";
for (size_t i = 0; i < r.size(); ++i)
std::cout << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(2) << size_t(r[i]) << ", ";
std::cout << "}" << std::endl;
return r;
}
inline std::string Decrypt(uint64_t key, std::vector<uint8_t> const & v) {
CheckSystemRequirements();
uint32_t iv = 0;
if (v.size() < sizeof(iv) + 2)
throw std::runtime_error("Decryption failed, data size less than IV + MAC size!");
iv = UnalignedRead<uint32_t>(v.data());
auto s_size = v.size() - sizeof(iv) - 2;
// Almost same as in Encrypt
std::vector<uint8_t> r((uint8_t*)&iv, ((uint8_t*)&iv) + sizeof(iv));
std::mt19937_64 rng(key + iv);
rng.discard(1000);
for (size_t i = 0; i < (s_size + 2 + 7) / 8; ++i) {
auto w = rng();
r.insert(r.end(), (uint8_t*)&w, ((uint8_t*)&w) + sizeof(w));
}
r.resize(sizeof(iv) + s_size + 2);
uint32_t mac0 = 1, mac = 0;
for (size_t i = 0; i < s_size; ++i) {
r[sizeof(iv) + i] ^= v[sizeof(iv) + i];
mac0 = (mac0 + r[sizeof(iv) + i]) % 65521; mac = (mac + mac0) % 65521;
}
if ((uint16_t(mac) ^ UnalignedRead<uint16_t>(r.data() + r.size() - 2)) != UnalignedRead<uint16_t>(v.data() + v.size() - 2))
throw std::runtime_error("Decryption failed, wrong MAC!");
return std::string((char*)(r.data() + sizeof(iv)), s_size);
}
int main() {
try {
std::cout << "Random Encryption Key: 0x" << std::hex << std::uppercase << GenRandomU64() << "ULL" << std::endl;
// Example of random encryption key generated by line of code above.
auto key = 0x71A1B4D23BC793E1ULL;
Decrypt(key, Encrypt(key, "COMPUTERNAME"));
// Example of encrypted string generated by line of code above.
auto s_COMPUTERNAME = Decrypt(key, {0xE5, 0x19, 0xF4, 0x41, 0x54, 0xBC, 0xBB, 0x45, 0x31, 0x13, 0xD4, 0x6B, 0x64, 0x3C, 0xFF, 0xBC, 0xA6, 0x3E, });
std::cout << "Decrypted: \"" << s_COMPUTERNAME << "\"" << std::endl;
return 0;
} catch (std::exception const & ex) {
std::cout << "Exception: " << ex.what() << std::endl;
return -1;
}
}
Один из примеров вывода:
Random Encryption Key: 0x7B0BD8ACD1E306D7ULL
Encrypted "COMPUTERNAME": {0x82, 0xB7, 0xFE, 0xF2, 0x09, 0xB0, 0x49, 0x79, 0x8E, 0x2C, 0x56, 0x24, 0x53, 0x0D, 0x3B, 0x27, 0xC1, 0x10, }
Decrypted: "COMPUTERNAME"