#include "NativeCode.h" #include "Random.h" _NATIVE_CODE_LINK::_NATIVE_CODE_LINK() { XedDecodedInstZeroSetMode(&XedInstruction, &XedGlobalMachineState); Flags = 0UL; Next = Prev = NULL; Block = NULL; Label = 0UL; RawData = NULL; RawDataSize = 0UL; AsmOperations.clear(); } _NATIVE_CODE_LINK::_NATIVE_CODE_LINK(UINT LabelId, _NATIVE_CODE_BLOCK* B) : _NATIVE_CODE_LINK() { Block = B; Label = LabelId; Flags = CODE_FLAG_IS_LABEL; } _NATIVE_CODE_LINK::_NATIVE_CODE_LINK(UINT F, PVOID Rd, UINT Rds, BOOL Decode) : _NATIVE_CODE_LINK() { Flags = F; RawDataSize = Rds; RawData = new UCHAR[Rds]; if (Rd) { RtlCopyMemory(RawData, Rd, Rds); if (Decode) XedDecode(&XedInstruction, RawData, RawDataSize); } } _NATIVE_CODE_LINK::~_NATIVE_CODE_LINK() { if (RawData) delete[] RawData; for (STDPAIR CONST& Op : AsmOperations) { if (Op.second) delete[] Op.second; } } _NATIVE_CODE_BLOCK::_NATIVE_CODE_BLOCK() { Start = End = NULL; LabelIds.clear(); } VOID NcAppendToBlock(PNATIVE_CODE_BLOCK Block, PNATIVE_CODE_LINK Link) { if (!Link) return; Link->Block = Block; Link->Prev = Block->End; Link->Next = NULL; if (!Block->End || !Block->Start) { Block->Start = Block->End = Link; } else { Block->End->Next = Link; Block->End = Link; } } VOID NcPrependToBlock(PNATIVE_CODE_BLOCK Block, PNATIVE_CODE_LINK Link) { if (!Link) return; Link->Block = Block; Link->Next = Block->Start; Link->Prev = NULL; if (!Block->End || !Block->Start) { Block->Start = Block->End = Link; } else { Block->Start->Prev = Link; Block->Start = Link; } } VOID NcInsertLinkAfter(PNATIVE_CODE_LINK Link1, PNATIVE_CODE_LINK Link2) { if (Link1) { Link2->Prev = Link1; Link2->Next = Link1->Next; Link1->Next = Link2; if (Link2->Next) Link2->Next->Prev = Link2; } } VOID NcInsertLinkBefore(PNATIVE_CODE_LINK Link1, PNATIVE_CODE_LINK Link2) { if (Link1) { Link2->Next = Link1; Link2->Prev = Link1->Prev; Link1->Prev = Link2; if (Link2->Prev) Link2->Prev->Next = Link2; } } VOID NcUnlink(PNATIVE_CODE_LINK Link) { if (Link) { if (Link->Next) Link->Next->Prev = Link->Prev; if (Link->Prev) Link->Prev->Next = Link->Next; } } UINT NcCountInstructions(PNATIVE_CODE_BLOCK Block, BOOL CountCombinedAsOne) { UINT InstructionCount = 0; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) continue; if (CountCombinedAsOne && (T->Flags & CODE_FLAG_DO_NOT_DIVIDE) && !(T->Flags & CODE_FLAG_GROUP_END)) continue; ++InstructionCount; } return InstructionCount; } UINT NcCalcBlockSizeInBytes(PNATIVE_CODE_BLOCK Block) { UINT TotalSize = 0; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) continue; TotalSize += T->RawDataSize; } return TotalSize; } UINT NcGenUnusedLabelId(PNATIVE_CODE_BLOCK Block) { UINT ReturnLabelId = rand(); while (StdFind(Block->LabelIds.begin(), Block->LabelIds.end(), ReturnLabelId) != Block->LabelIds.end()) ReturnLabelId = rand(); Block->LabelIds.push_back(ReturnLabelId); return ReturnLabelId; } VOID NcChangeLabelId(PNATIVE_CODE_BLOCK Block, UINT Original, UINT New) { for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (((T->Flags & CODE_FLAG_IS_LABEL) || (T->Flags & CODE_FLAG_IS_REL_JMP)) && T->Label == Original) T->Label = New; } } VOID NcFixLabelsForBlocks(PNATIVE_CODE_BLOCK Block1, PNATIVE_CODE_BLOCK Block2) { for (PNATIVE_CODE_LINK T = Block2->Start; T && T != Block2->End->Next; T = T->Next) { if ((T->Flags & CODE_FLAG_IS_LABEL) && StdFind(Block1->LabelIds.begin(), Block1->LabelIds.end(), T->Label) != Block1->LabelIds.end()) { NcChangeLabelId(Block2, T->Label, NcGenUnusedLabelId(Block1)); } } } BOOL NcInsertBlockAfter(PNATIVE_CODE_LINK Link, PNATIVE_CODE_BLOCK Block, BOOL FixLabels) { if (!Link || !Link->Block || !Block || !Block->Start || !Block->End || Link->Block == Block) return FALSE; if (FixLabels && Block->LabelIds.size() && Link->Block->LabelIds.size()) NcFixLabelsForBlocks(Link->Block, Block); if (Link->Next) Link->Next->Prev = Block->End; Block->End->Next = Link->Next; Block->Start->Prev = Link; Link->Next = Block->Start; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) T->Block = Link->Block; return TRUE; } BOOL NcInsertBlockBefore(PNATIVE_CODE_LINK Link, PNATIVE_CODE_BLOCK Block, BOOL FixLabels) { if (!Link || !Link->Block || !Block || !Block->Start || !Block->End) return FALSE; if (FixLabels && Block->LabelIds.size() && Link->Block->LabelIds.size()) NcFixLabelsForBlocks(Link->Block, Block); if (Link->Prev) Link->Prev->Next = Block->Start; Block->Start->Prev = Link->Prev; Block->End->Next = Link; Link->Prev = Block->End; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) T->Block = Link->Block; return TRUE; } BOOL NcInsertBlockStartToEnd(PNATIVE_CODE_LINK Start, PNATIVE_CODE_LINK End, PNATIVE_CODE_BLOCK NewBlock) { NewBlock->Start->Prev = Start->Prev; NewBlock->End->Next = End->Next; if (Start->Prev) Start->Prev->Next = NewBlock->Start; if (End->Next) End->Next->Prev = NewBlock->End; if (Start->Block->Start == Start) Start->Block->Start = NewBlock->Start; if (Start->Block->End == End) Start->Block->End = NewBlock->End; //Update Block for the current isntructions for (PNATIVE_CODE_LINK T = NewBlock->Start; T && T != NewBlock->End->Next; T = T->Next) T->Block = Start->Block; PNATIVE_CODE_LINK EndBlock = End->Next; for (PNATIVE_CODE_LINK T = Start; T && T != EndBlock;) { PNATIVE_CODE_LINK RealNext = T->Next; delete T; T = RealNext; } return TRUE; } BOOL NcCreateLabels(PNATIVE_CODE_BLOCK Block) { UINT CurrentLabelId = 0; for (PNATIVE_CODE_LINK T = Block->Start; T; T = T->Next) { if (!(T->Flags & CODE_FLAG_IS_INST)) continue; XED_CATEGORY_ENUM Category = XedDecodedInstGetCategory(&T->XedInstruction); if (Category != XED_CATEGORY_COND_BR && Category != XED_CATEGORY_UNCOND_BR) continue; UINT OperandCount = XedDecodedInstNumOperands(&T->XedInstruction); if (OperandCount < 1) continue; CONST XED_INST* Inst = XedDecodedInstInst(&T->XedInstruction); if (!Inst) continue; CONST XED_OPERAND* Operand = XedInstOperand(Inst, 0); if (!Operand) continue; XED_OPERAND_TYPE_ENUM OperandType = XedOperandType(Operand); if (OperandType != XED_OPERAND_TYPE_IMM && OperandType != XED_OPERAND_TYPE_IMM_CONST) continue; INT32 BranchDisplacement = XedDecodedInstGetBranchDisplacement(&T->XedInstruction); PNATIVE_CODE_LINK JmpPos = NcValidateJmp(T, BranchDisplacement); if (!JmpPos) { printf("Failed to validate jump. Type: %s, Displacement: %d\n", XedCategoryEnumToString(Category), BranchDisplacement); return FALSE; } if (JmpPos->Prev && (JmpPos->Prev->Flags & CODE_FLAG_IS_LABEL)) { T->Label = JmpPos->Prev->Label; } else { NcInsertLinkBefore(JmpPos, new NATIVE_CODE_LINK(CurrentLabelId, Block)); Block->LabelIds.push_back(CurrentLabelId); T->Label = CurrentLabelId; ++CurrentLabelId; } T->Flags |= CODE_FLAG_IS_REL_JMP; } return TRUE; } PNATIVE_CODE_LINK NcValidateJmp(PNATIVE_CODE_LINK Jmp, INT32 Delta) { PNATIVE_CODE_LINK T; if (Delta > 0) { T = Jmp->Next; while (Delta > 0 && T) { if (!(T->Flags & CODE_FLAG_IS_LABEL)) Delta -= XedDecodedInstGetLength(&T->XedInstruction); T = T->Next; } if (Delta != 0 || !T) return NULL; while (T && (T->Flags & CODE_FLAG_IS_LABEL)) T = T->Next; return T; } else if (Delta < 0) { T = Jmp; while (T) { if (!(T->Flags & CODE_FLAG_IS_LABEL)) { Delta += XedDecodedInstGetLength(&T->XedInstruction); if (Delta >= 0) break; } T = T->Prev; } if (Delta != 0 || !T) return NULL; while (T && (T->Flags & CODE_FLAG_IS_LABEL)) T = T->Next; return T; } return Jmp->Next; } PNATIVE_CODE_LINK NcDeepCopyLink(PNATIVE_CODE_LINK Link) { if (Link->Flags & CODE_FLAG_IS_LABEL) { return new NATIVE_CODE_LINK(Link->Label, NULL); } else { PNATIVE_CODE_LINK NewLink = new NATIVE_CODE_LINK(Link->Flags, Link->RawData, Link->RawDataSize); NewLink->Label = Link->Label; XED_ERROR_ENUM DecodeError = XedDecode(&NewLink->XedInstruction, Link->RawData, Link->RawDataSize); if (DecodeError != XED_ERROR_NONE) { printf("XedDecode failed in NcDeepCopyLink: %s %u\n", XedErrorEnumToString(DecodeError), Link->RawDataSize); delete NewLink; return NULL; } if (Link->Flags & CODE_FLAG_HAS_ASM_OP) { for (STDPAIR CONST& Op : Link->AsmOperations) { NewLink->AsmOperations.emplace_back(Op.first, Op.second); } } return NewLink; } } BOOL NcDeepCopyPartialBlock(PNATIVE_CODE_LINK Start, PNATIVE_CODE_LINK End, PNATIVE_CODE_BLOCK Block) { if (!Start || !End || !Start->Block || Start->Block != End->Block || !Block) return FALSE; Block->LabelIds.clear(); Block->Start = Block->End = NULL; for (UINT L : Start->Block->LabelIds) Block->LabelIds.push_back(L); for (PNATIVE_CODE_LINK CurLink = Start; CurLink && CurLink != End->Next; CurLink = CurLink->Next) { PNATIVE_CODE_LINK Temp = NcDeepCopyLink(CurLink); if (!Temp) { NcDeleteBlock(Block); return FALSE; } NcAppendToBlock(Block, Temp); } return TRUE; } BOOL NcDeepCopyBlock(PNATIVE_CODE_BLOCK Block, PNATIVE_CODE_BLOCK BlockCopy) { return NcDeepCopyPartialBlock(Block->Start, Block->End, BlockCopy); } BOOL NcPromoteRelJmpTo32(PNATIVE_CODE_LINK Link) { if (XedDecodedInstGetBranchDisplacementWidth(&Link->XedInstruction) == 32) return TRUE; XED_ENCODER_INSTRUCTION EncoderInstruction; XED_ENCODER_REQUEST EncoderRequest; UCHAR EncodeBuffer[15]; UINT ReturnedSize; XED_ICLASS_ENUM IClass = XedDecodedInstGetIClass(&Link->XedInstruction); XedInst1(&EncoderInstruction, XedGlobalMachineState, IClass, 32, XedRelBr(0, 32)); XedEncoderRequestZeroSetMode(&EncoderRequest, &XedGlobalMachineState); if (!XedConvertToEncoderRequest(&EncoderRequest, &EncoderInstruction)) return FALSE; XED_ERROR_ENUM Err = XedEncode(&EncoderRequest, EncodeBuffer, 15, &ReturnedSize); if (XED_ERROR_NONE != Err) return FALSE; delete[] Link->RawData; Link->RawDataSize = ReturnedSize; Link->RawData = new UCHAR[ReturnedSize]; RtlCopyMemory(Link->RawData, EncodeBuffer, ReturnedSize); XedDecodedInstZeroSetMode(&Link->XedInstruction, &XedGlobalMachineState); if (XED_ERROR_NONE != XedDecode(&Link->XedInstruction, Link->RawData, Link->RawDataSize)) return FALSE; return TRUE; } BOOL NcPromoteAllRelJmpTo32(PNATIVE_CODE_BLOCK Block) { for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_REL_JMP) { if (!NcPromoteRelJmpTo32(T)) return FALSE; } } return TRUE; } BOOL NcGetDeltaToLabel(PNATIVE_CODE_LINK Link, PINT32 DeltaOut) { INT32 Delta = 0; //First checking backwards because I feel like thats the direction most jmps are in for (PNATIVE_CODE_LINK T = Link; T; T = T->Prev) { if (T->Flags & CODE_FLAG_IS_LABEL) { if (T->Label == Link->Label) { *DeltaOut = Delta; return TRUE; } continue; } Delta -= T->RawDataSize; } //Now check forwards Delta = 0; for (PNATIVE_CODE_LINK T = Link->Next; T; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) { if (T->Label == Link->Label) { *DeltaOut = Delta; return TRUE; } continue; } Delta += T->RawDataSize; } return FALSE; } BOOL NcGetDeltaToRandomLabel(PNATIVE_CODE_LINK Link, PINT32 DeltaOut) { std::vector Deltas; INT32 Delta = 0; //First checking backwards because I feel like thats the direction most jmps are in for (PNATIVE_CODE_LINK T = Link; T; T = T->Prev) { if (T->Flags & CODE_FLAG_IS_LABEL) { if (T->Label == Link->Label) { Deltas.push_back(Delta); } continue; } Delta -= T->RawDataSize; } //Now check forwards Delta = 0; for (PNATIVE_CODE_LINK T = Link->Next; T; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) { if (T->Label == Link->Label) { Deltas.push_back(Delta); } continue; } Delta += T->RawDataSize; } if (Deltas.size() == 0) return FALSE; *DeltaOut = Deltas[RndGetRandomInt(0, Deltas.size() - 1)]; return TRUE; } BOOL NcFixRelJmps(PNATIVE_CODE_BLOCK Block) { for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next;) { if (T->Flags & CODE_FLAG_IS_REL_JMP) { INT32 BranchDisp = 0; if (!NcGetDeltaToLabel(T, &BranchDisp)) return FALSE; UINT DispWidth = XedDecodedInstGetBranchDisplacementWidthBits(&T->XedInstruction); if (log2(abs(BranchDisp)) + 1 > DispWidth) { //duh oh if (DispWidth == 32) return FALSE; DispWidth = 32; //Encode new instruction XED_ENCODER_INSTRUCTION EncoderInstruction; XED_ENCODER_REQUEST EncoderRequest; UCHAR EncodeBuffer[15]; UINT ReturnedSize; XED_ICLASS_ENUM IClass = XedDecodedInstGetIClass(&T->XedInstruction); //Do the encoding XedInst1(&EncoderInstruction, XedGlobalMachineState, IClass, DispWidth, XedRelBr(0, DispWidth)); XedEncoderRequestZeroSetMode(&EncoderRequest, &XedGlobalMachineState); if (!XedConvertToEncoderRequest(&EncoderRequest, &EncoderInstruction)) return FALSE; XED_ERROR_ENUM Err = XedEncode(&EncoderRequest, EncodeBuffer, 15, &ReturnedSize); if (XED_ERROR_NONE != Err) return FALSE; //fixup T->RawData delete[] T->RawData; T->RawDataSize = ReturnedSize; T->RawData = new UCHAR[ReturnedSize]; RtlCopyMemory(T->RawData, EncodeBuffer, ReturnedSize); //Decode instruction so its proper and all that XedDecodedInstZeroSetMode(&T->XedInstruction, &XedGlobalMachineState); if (XED_ERROR_NONE != XedDecode(&T->XedInstruction, T->RawData, T->RawDataSize)) return FALSE; //Go back to the start and loop through all labels again because now this instruction is larger :)))) T = Block->Start; continue; } else { DispWidth = XedDecodedInstGetBranchDisplacementWidth(&T->XedInstruction); switch (DispWidth) { case 1: *(PINT8)&T->RawData[T->RawDataSize - DispWidth] = (INT8)BranchDisp; break; case 2: *(PINT16)&T->RawData[T->RawDataSize - DispWidth] = (INT16)BranchDisp; break; case 4: *(PINT32)&T->RawData[T->RawDataSize - DispWidth] = (INT32)BranchDisp; break; } } } T = T->Next; } return TRUE; } BOOL NcHasIllegalInstructions(PNATIVE_CODE_BLOCK Block) { //Iterate through and check for rip relative instructions /*for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) continue; UINT OperandCount = XedDecodedInstNumOperands(&T->XedInstruction); if (OperandCount == 0) continue; }*/ return FALSE; } BOOL NcDisassemble(PNATIVE_CODE_BLOCK Block, PVOID Buffer, UINT BufferSize) { PUCHAR Buf = (PUCHAR)Buffer; UINT Offset = 0; while (Offset < BufferSize) { PNATIVE_CODE_LINK Link = new NATIVE_CODE_LINK; Link->Flags = CODE_FLAG_IS_INST; UINT PossibleSize = min(15, BufferSize - Offset); XED_ERROR_ENUM DecodeError = XedDecode(&Link->XedInstruction, (Buf + Offset), PossibleSize); if (DecodeError != XED_ERROR_NONE) { printf("XedDecode failed with error %s\n", XedErrorEnumToString(DecodeError)); NcDeleteBlock(Block); delete Link; return FALSE; } Link->RawDataSize = XedDecodedInstGetLength(&Link->XedInstruction); Link->RawData = new UCHAR[Link->RawDataSize]; RtlCopyMemory(Link->RawData, (Buf + Offset), Link->RawDataSize); NcAppendToBlock(Block, Link); Offset += Link->RawDataSize; } NcCreateLabels(Block); return TRUE; } PVOID NcAssemble(PNATIVE_CODE_BLOCK Block, PUINT OutSize) { if (!NcFixRelJmps(Block)) return NULL; *OutSize = NcCalcBlockSizeInBytes(Block); PUCHAR Buffer = new UCHAR[*OutSize]; // (PUCHAR)malloc(*OutSize); if (!Buffer) return NULL; PUCHAR BufferOffset = Buffer; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) continue; RtlCopyMemory(BufferOffset, T->RawData, T->RawDataSize); if (T->Flags & CODE_FLAG_HAS_ASM_OP) { for (STDPAIR CONST& Op : T->AsmOperations) Op.first(T, BufferOffset, Op.second); } BufferOffset += T->RawDataSize; } return Buffer; } //assumes all jmps already promoted to 32 bit branch displacement size PVOID NcAssembleEx(PNATIVE_CODE_BLOCK Block, PUINT OutSize) { for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next;) { if (T->Flags & CODE_FLAG_IS_REL_JMP) { INT32 BranchDisp = 0; if (!NcGetDeltaToRandomLabel(T, &BranchDisp)) return FALSE; UINT DispWidth = XedDecodedInstGetBranchDisplacementWidth(&T->XedInstruction); switch (DispWidth) { case 1: *(PINT8)&T->RawData[T->RawDataSize - DispWidth] = (INT8)BranchDisp; break; case 2: *(PINT16)&T->RawData[T->RawDataSize - DispWidth] = (INT16)BranchDisp; break; case 4: *(PINT32)&T->RawData[T->RawDataSize - DispWidth] = (INT32)BranchDisp; break; } } T = T->Next; } *OutSize = NcCalcBlockSizeInBytes(Block); PUCHAR Buffer = new UCHAR[*OutSize]; if (!Buffer) return NULL; PUCHAR BufferOffset = Buffer; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) continue; RtlCopyMemory(BufferOffset, T->RawData, T->RawDataSize); if (T->Flags & CODE_FLAG_HAS_ASM_OP) { for (STDPAIR CONST& Op : T->AsmOperations) Op.first(T, BufferOffset, Op.second); } BufferOffset += T->RawDataSize; } return Buffer; } VOID NcDeleteBlock(PNATIVE_CODE_BLOCK Block) { if (!Block->Start || !Block->End) return; PNATIVE_CODE_LINK BlockEnding = Block->End->Next; for (PNATIVE_CODE_LINK T = Block->Start; T && T != BlockEnding;) { PNATIVE_CODE_LINK Next = T->Next; delete T; T = Next; } } VOID NcDebugPrint(PNATIVE_CODE_BLOCK Block) { HANDLE ConsoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); if (!ConsoleHandle) return; for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (T->Flags & CODE_FLAG_IS_LABEL) { SetConsoleTextAttribute(ConsoleHandle, FOREGROUND_GREEN | FOREGROUND_RED); printf("Label: %u\n", T->Label); } else { XED_ICLASS_ENUM IClass = XedDecodedInstGetIClass(&T->XedInstruction); if (T->Flags & CODE_FLAG_IS_REL_JMP) { SetConsoleTextAttribute(ConsoleHandle, FOREGROUND_GREEN | FOREGROUND_RED); printf("%s: %u\n", XedIClassEnumToString(IClass), T->Label); } else { SetConsoleTextAttribute(ConsoleHandle, FOREGROUND_GREEN | FOREGROUND_BLUE); printf("%s\n", XedIClassEnumToString(IClass)); } } } } VOID NcPrintBlockCode(PNATIVE_CODE_BLOCK Block) { for (PNATIVE_CODE_LINK T = Block->Start; T && T != Block->End->Next; T = T->Next) { if (!(T->Flags & CODE_FLAG_IS_LABEL)) { for (uint32_t i = 0; i < T->RawDataSize; i++) { std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)T->RawData[i] << ' '; } } } }