// This file is distributed under a BSD license. See LICENSE.txt for details. #include "_types.hpp" #include "dis.hpp" #include "mapfile.hpp" #include #include /****************************************************************************/ /****************************************************************************/ // formats #define fNM 0x0 // no modrm #define fAM 0x1 // no modrm, address mode #define fMR 0x2 // modrm #define fMO 0x3 // modrm+xtra opcode #define fMODE 0x3 // mode mask #define fNI 0x0 // no immediate #define fBI 0x4 // byte immediate #define fDI 0x8 // dword immediate #define fWI 0xc // word immediate #define fTYPE 0xc // type mask #define fAD 0x0 // address #define fBR 0x4 // byte relative #define fDR 0xc // dword relative #define fERR 0x9 // error! /****************************************************************************/ static sU8 Table0[256] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI, // 0 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI, // 1 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI, // 2 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI, // 3 fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // 4 fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // 5 fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fDI,fMR|fDI,fNM|fBI,fMR|fBI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // 6 fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR, // 7 fMR|fBI,fMR|fDI,fMR|fBI,fMR|fBI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // 8 fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fERR ,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // 9 fAM|fAD,fAM|fAD,fAM|fAD,fAM|fAD,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fBI,fNM|fDI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // a fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fDI,fNM|fDI,fNM|fDI,fNM|fDI,fNM|fDI,fNM|fDI,fNM|fDI,fNM|fDI, // b fMR|fBI,fMR|fBI,fNM|fWI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fBI,fMR|fDI,fERR ,fNM|fNI,fNM|fWI,fNM|fNI,fNM|fNI,fNM|fBI,fERR ,fNM|fNI, // c fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fBI,fNM|fBI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // d fAM|fBR,fAM|fBR,fAM|fBR,fAM|fBR,fNM|fBI,fNM|fBI,fNM|fBI,fNM|fBI,fAM|fDR,fAM|fDR,fAM|fAD,fAM|fBR,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // e fNM|fNI,fERR ,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fMO|fNI,fMO|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fMO|fNI,fMO|fNI, // f }; /****************************************************************************/ static sU8 Table0f[256] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR , // 0 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR , // 1 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR , // 2 fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR , // 3 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // 4 fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fERR , // 5 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // 6 fMR|fBI,fMR|fBI,fMR|fBI,fMR|fBI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fNI,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fMR|fNI,fMR|fNI, // 7 fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR,fAM|fDR, // 8 fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // 9 fNM|fNI,fNM|fNI,fNM|fNI,fMR|fNI,fMR|fBI,fMR|fNI,fMR|fNI,fMR|fNI,fERR ,fERR ,fERR ,fMR|fNI,fMR|fBI,fMR|fNI,fERR ,fMR|fNI, // a fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fERR ,fERR ,fERR ,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // b fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI,fNM|fNI, // c fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // d fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // e fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fERR , // f }; /****************************************************************************/ static sU8 Tablefx[32] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f fMR|fBI,fERR ,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fBI,fERR ,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI,fMR|fNI, // 0 fMR|fNI,fMR|fNI,fERR ,fERR ,fERR ,fERR ,fERR ,fERR ,fMR|fNI,fMR|fNI,fMR|fNI,fERR ,fMR|fNI,fERR ,fMR|fNI,fERR , // 1 }; // caaaaareful with this table (match disdeck!) /****************************************************************************/ /****************************************************************************/ DataBuffer::DataBuffer() { Size = 0; Max = 16; Data = (sU8 *) malloc(Max); } DataBuffer::~DataBuffer() { free(Data); } void DataBuffer::Clear() { Size = 0; } void DataBuffer::Append(sU8 *data,sInt size) { if(Size+size>Max) { Max = (Max*2 < Size+size) ? Size+size : Max*2; sU8* newData = (sU8*) realloc(Data, Max); if (newData == NULL) { // realloc failed; memory pointed to by 'Data' didn't get free'd free(Data); sVERIFY(newData != NULL); } else { Data = newData; } } sCopyMem(Data+Size,data,size); Size += size; } void DataBuffer::PutValue(sU32 value,sInt size) { Append((sU8*) &value,size); } /****************************************************************************/ void CoreI::Clear() { Len = 0; Index = 255; sSetMem(Bytes,0,sizeof(Bytes)); Freq = 0; Next = 0; } /****************************************************************************/ CoreInstructionSet::CoreInstructionSet() { for(sInt i=0;iNext; delete current; } Bucket[i] = 0; } InstrCount = 0; if(InstrList) { delete[] InstrList; InstrList = 0; } } CoreI *CoreInstructionSet::FindInstr(const CoreI &instr) { // determine hash chain and try to find a match sInt bucket = Hash(instr); CoreI *match; for(match=Bucket[bucket];match;match=match->Next) if(match->Len == instr.Len && !sCmpMem(match->Bytes,instr.Bytes,instr.Len)) break; return match; } void CoreInstructionSet::CountInstr(const CoreI &instr) { CoreI *match = FindInstr(instr); if(match) // got one match->Freq++; else // no match, add a new instr { CoreI *instCopy = new CoreI(instr); sInt bucket = Hash(instr); instCopy->Freq = 1; instCopy->Index = 65535; instCopy->Next = Bucket[bucket]; Bucket[bucket] = instCopy; InstrCount++; } } void CoreInstructionSet::MakeInstrList() { // Make a list of instructions InstrList = new CoreI *[InstrCount]; sInt n = 0; for(sInt i=0;iNext) InstrList[n++] = current; sVERIFY(n == InstrCount); // Sort + assign indices std::sort(InstrList,InstrList + InstrCount,CompareInstrs); for(sInt i=0;i<254+256 && iIndex = i; } sInt CoreInstructionSet::Hash(const CoreI &instr) { // FNV-style hash. sU32 hash = 0x9dc5 + instr.Len; hash = (hash ^ instr.Bytes[0]) * 0x0193; hash = (hash ^ instr.Bytes[1]) * 0x0193; hash = (hash ^ instr.Bytes[2]) * 0x0193; return hash & BUCKET_MASK; } bool CoreInstructionSet::CompareInstrs(const CoreI *a,const CoreI *b) { if(a->Freq != b->Freq) return a->Freq > b->Freq; if(a->Len != b->Len) return a->Len < b->Len; return sCmpMem(a->Bytes,b->Bytes,a->Len) < 0; } /****************************************************************************/ sInt DisFilter::CountInstr(sU8 *instr) { sInt code,code2,modrm,sib,flags; sBool o16; CoreI corei; sU8 *start = instr; code = *instr++; if(code == 0x66) // operand size prefix { o16 = sTRUE; code = *instr++; } else o16 = sFALSE; if(code == 0x0f) // two-byte opcode { code2 = *instr++; flags = Table0f[code2]; } else flags = Table0[code]; if((flags & fMODE) == fMO) flags = Tablefx[((*instr & 0x38) >> 3) | ((code & 0x01) << 3) | ((code & 0x08) << 1)]; if(flags != fERR) { if(o16) corei.AddByte(0x66); corei.AddByte(code); if(code == 0x0f) corei.AddByte(code2); if(flags & fMR) // modrm { modrm = *instr++; corei.AddByte(modrm); // skip everything that may come after if((modrm & 0x07) == 4 && (modrm & 0xc0) != 0xc0) sib = *instr++; else sib = 0; if((modrm & 0xc0) == 0x40) // byte displacement instr++; if((modrm & 0xc0) == 0x80 || (modrm & 0xc7) == 0x05 || ((modrm & 0xc0) == 0 && (sib & 0x07) == 5)) instr += 4; } Instrs.CountInstr(corei); // instruction counted, skip immediates that may follow if((flags & fMODE) == fAM) { switch(flags & fTYPE) { case fAD: instr += 4; break; case fBR: instr += 1; break; case fDR: instr += 4; break; } } else { switch(flags & fTYPE) { case fBI: instr += 1; break; case fDI: instr += o16 ? 2 : 4; break; case fWI: instr += 2; break; } } // return size return instr - start; } else return 1; // error: just skip one byte and try again } sInt DisFilter::ProcessInstr(sU8 *instr,sU32 memory,sU32 VA) { sU8 *start; sU8 code,code2,modrm,sib,flags; sU32 val,tmp; sBool o16; sInt i; CoreI corei; o16 = sFALSE; modrm = 0; sib = 0; start = instr; code = *instr++; if(NextFunc && code != 0xcc) { FuncTable[FuncTablePos] = memory; if(++FuncTablePos == 255) FuncTablePos = 0; NextFunc = sFALSE; } if(code == 0x66) // operand size prefix { o16 = sTRUE; code = *instr++; } if(code == 0x0f) { code2 = *instr++; flags = Table0f[code2]; } else flags = Table0[code]; if(code == 0xc2 || code == 0xc3 || code == 0xcc) // return/int3 NextFunc = sTRUE; if((flags & fMODE) == fMO) flags = Tablefx[((*instr & 0x38) >> 3) | ((code & 0x01) << 3) | ((code & 0x08) << 1)]; if(flags != fERR) { if(o16) corei.AddByte(0x66); corei.AddByte(code); if(code == 0x0f) corei.AddByte(code2); } if((flags & fMODE) == fMR) { modrm = *instr++; corei.AddByte(modrm); if((modrm & 0x07) == 4 && (modrm & 0xc0) != 0xc0) { sib = *instr++; Buffer[21].PutValue(sib,1); } if((modrm & 0xc0) == 0x40) Buffer[(modrm & 0x07)+1].PutValue(*instr++,1); if((modrm & 0xc0) == 0x80 || (modrm & 0xc7) == 0x05 || ((modrm & 0xc0) == 0 && (sib & 0x07) == 5)) { val = *(sU32 *) instr; instr += 4; Buffer[(modrm & 0xc7) == 5 ? 14 : 13].PutValue(val & 0xffffff,3); Buffer[18].PutValue(val >> 24,1); } } // core instruction is completed by here, so find and encode if(flags != fERR) { CoreI *InstCode = Instrs.FindInstr(corei); sVERIFY(InstCode != 0); if(InstCode->Index < 255) Buffer[0].PutValue(InstCode->Index,1); else { Buffer[0].PutValue(255,1); // not in top 255, escape Buffer[0].Append(corei.Bytes,corei.Len); } } if((flags & fMODE) == fAM) { switch(flags & fTYPE) { case fAD: val = *(sU32 *) instr; instr += 4; Buffer[15].PutValue(val & 0xffffff,3); Buffer[19].PutValue(val >> 24,1); break; case fBR: Buffer[9].PutValue(*instr++,1); break; case fDR: val = *(sU32 *) instr; instr += 4; val += (instr - start) + memory; if(code != 0xe8) { i = val - LastJump; tmp = (i < 0) ? -i * 2 - 1 : i * 2; Buffer[17].PutValue(tmp & 0xffff,2); Buffer[20].PutValue(tmp >> 16,2); LastJump = val; } else { for(i=0;i<255;i++) if(FuncTable[i] == val) break; Buffer[16].PutValue(i+1,1); if(i == 255) { Buffer[16].PutValue(val & 0xffff,2); Buffer[20].PutValue(val >> 16,2); FuncTable[FuncTablePos] = val; if(++FuncTablePos == 255) FuncTablePos = 0; } } break; } } else { switch(flags & fTYPE) { case fBI: Buffer[10].PutValue(*instr++,1); break; case fDI: if(!o16) { Buffer[12].Append(instr,4); instr += 4; break; } // fall-through case fWI: Buffer[11].Append(instr,2); instr += 2; break; } } if(flags == fERR) { Buffer[0].PutValue(0xff,1); // corei escape Buffer[0].PutValue(0xce,1); // escape Buffer[0].PutValue(*start,1); // byte to encode return 1; } else return instr - start; } void DisFilter::Filter(sU8 *code,sInt size,sU32 VA,DebugInfo *info) { sU32 memory; sInt lastSize,i,processed,v; Instrs.Clear(); // first pass: count instructions and instruction types sU8 *curCode = code; sInt curSize = size; while(curSize > 0) { lastSize = curSize; processed = CountInstr(curCode); curCode += processed; curSize -= processed; } Instrs.MakeInstrList(); // second pass: actual encoding for(i=0;iBytes,Instrs.InstrList[i]->Len); while(curSize > 0) { processed = ProcessInstr(curCode,memory,VA); memory += processed; curCode += processed; curSize -= processed; } sVERIFY(curSize == 0); // encode the rest as escapes while(rest--) { Buffer[0].PutValue(0xff,1); // corei escape Buffer[0].PutValue(0xce,1); // escape Buffer[0].PutValue(*curCode++,1); // value } Output.Clear(); for(i=0;i> 4) == fERR) v &= 0x0f; Table[i/2] = v; } Instrs.Clear(); } /****************************************************************************/ void DisUnFilter(sU8 *packed,sU8 *dest,sU32 oldAddr,sU32 newAddr,ReorderBuffer &reord) { sU8 *buffer[NBUFFERS],*finish,*start,*opacked,*corei; sInt i,flags; sU8 code,code2,modrm,sib,coreind,corelen; sBool o16,nextFunc; sU32 val,lastJump,memory; sU32 funcTable[256]; sU8 *instTable[255]; sInt funcTablePos; sU8 *oldDest; oldDest = dest; opacked = packed; start = packed + NBUFFERS*4; for(i=0;i> 1); else val >>= 1; lastJump += val; val = lastJump; } val -= dest + 4 - start + memory; *(sU32 *) dest = val; dest += 4; break; } } else { switch(flags & fTYPE) { case fBI: reord.Add(newAddr + buffer[10] - opacked,1,dest - oldDest + oldAddr,1); *dest++ = *buffer[10]++; break; case fDI: if(!o16) { reord.Add(newAddr + buffer[12] - opacked,4,dest - oldDest + oldAddr,4); val = *(sU32 *) buffer[12]; *(sU32 *) dest = val; dest += 4; buffer[12] += 4; break; } // fall-through case fWI: reord.Add(newAddr + buffer[11] - opacked,2,dest - oldDest + oldAddr,2); *(sU16 *) dest = *(sU16 *) buffer[11]; dest += 2; buffer[11] += 2; break; } } } if(coreind == 255) buffer[0] = corei; memory += dest - start; } }