#include #include #include #include #include #include #include #include int readFile(const char* fileName, std::vector& buffer) { std::ifstream file(fileName, std::ifstream::binary); if (file) { /* * Get the size of the file */ file.seekg(0,std::ios::end); std::streampos length = file.tellg(); file.seekg(0,std::ios::beg); /* * Use a vector as the buffer. * It is exception safe and will be tidied up correctly. * This constructor creates a buffer of the correct length. * * Then read the whole file into the buffer. */ buffer.resize(length); file.read(&buffer[0],length); } else { return -1; } return buffer.size(); } int writeFile(const char* fileName, const std::vector& buffer) { std::ofstream file(fileName, std::ifstream::binary); if (file) { file.write(&buffer[0],buffer.size()); } else { return -1; } return buffer.size(); } std::string getFileName(const std::string& sysPath, const std::string& fileName) { return sysPath + '\\' + fileName; } std::string setCommand(const std::string& sysPath, const std::string& sysExe, const std::string& fileName) { return getFileName(sysPath, sysExe) + " -w 195936478 --globalBasicSettingsPath " + fileName; } size_t findAndSetOffset(const std::vector& exeBuffer, size_t idx, const char* search, size_t& location) { if (strcmp(&exeBuffer[idx], search) == 0) { if (location != 0) { return -1; } location = idx; } return 0; } int main(int argc, char** argv) { if (argc != 3) { std::cerr << "Patcher \n"; return 1; } std::vector cmpBufferOne; std::vector cmpBufferTwo; std::vector cmpBufferGold; std::vector exeBuffer; // Step 1: Run the program with the secret options. // "195936478" is the magic number = 0x0BADC0DE std::string sysPath(argv[1]); std::string sysExe(argv[2]); std::string sysCommand; std::string relFile; // filename with path std::string baseFile; // filename without path std::string exeFile = getFileName(sysPath, sysExe); // Run player with special options to dump the .text section to "buffer1.bin" // The dump will write: // .text // 32b int = Golden Hash, calculated // 32b int = Golden Hash to compare. baseFile = "buffer1.bin"; relFile = getFileName(sysPath, baseFile); sysCommand = setCommand(sysPath, sysExe, baseFile); std::cout << sysCommand << std::endl; system(sysCommand.c_str()); int fileErrorCode = 0; fileErrorCode = readFile(relFile.c_str(), cmpBufferOne); if (fileErrorCode < 0) { std::cerr << " couldn't read buffer1.bin.\n"; return 1; } const int kPadLen = sizeof(int) + sizeof(int); int codeLen = cmpBufferOne.size() - kPadLen; int bufferGhOffset = codeLen; // calculated goldHash offset in buffer (what app calculates) int bufferRhOffset = codeLen + sizeof(int); // referenced goldHash offset in buffer (what app checks against) std::cout << "codeLen = " << codeLen << std::endl; // Step 2: find the location that is different. // VM protect has a pointer that is different in .text. This will find the // location. Only run 3 times to find it. int maxIters = 3; int diffLocation = -1; baseFile = "buffer2.bin"; relFile = getFileName(sysPath, baseFile); sysCommand = setCommand(sysPath, sysExe, baseFile); while (maxIters) { system(sysCommand.c_str()); fileErrorCode = readFile(relFile.c_str(), cmpBufferTwo); if (fileErrorCode < 0) { std::cerr << "couldn't read buffer2.bin.\n"; return 1; } // make sure only one place in the file is different. // if multiple are different, make sure ASLR is disabled // (note that .rdata also has vtbls from some dll's...) for (int i = 0; i < codeLen; ++i) { if(cmpBufferOne[i] != cmpBufferTwo[i]) { if ((diffLocation >= 0) && ((i-diffLocation) > 4)) { std::cerr << "Multiple places have differences.\n"; return -1; } else { diffLocation = i & 0xFFFFFFFC; // ignore 2 lsb } } } if (diffLocation >= 0) { break; } else { --maxIters; } } if (maxIters == 0) { std::cerr << "Could not find differences between runs.\n"; return -1; } // Step 3: Find the locations to modify in the exe fileErrorCode = readFile(exeFile.c_str(), exeBuffer); if (fileErrorCode < 0) { std::cerr << "couldn't read executable.\n"; return 1; } int exeLen = exeBuffer.size(); // There is a common, unique prefix. Check to ensure there is only // one for maskAddr and one for goldHash size_t maskAddrOffset = 0; size_t goldHashOffset = 0; const char* magicMaskAddrCstr = "1(m$n9.[?y`z+f : a&Rn5