BOOL Dasm_GetInstructionSize (BYTE * pInst, UINT * pnSize, UINT * pnSizeIfMoved) { int nSize = 0 ; int nSizeIfMoved = 0 ; ASSERT (pInst!=NULL) ; switch( pInst[0] ) { case 0x71: case 0x72: // JB rel8 case 0x73: // JAE rel8 case 0x74: case 0x75: case 0x76: case 0x77: // JA rel8 case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F: // JG rel8 case 0xE3: // JECXZ rel8 nSize = 2 ; nSizeIfMoved = 5 ; break ; case 0x0F: switch( pInst[1] ) { case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F: nSize = 6 ; break ; } break ; case 0x48: // DEC EAX case 0x49: // DEC ECX case 0x4A: // DEC EDX case 0x4B: // DEC EBX case 0x4C: // DEC ESP case 0x4D: // DEC EBP case 0x4E: // DEC ESI case 0x4F: // DEC EDI case 0x50: // PUSH EAX case 0x51: // PUSH ECX case 0x52: // PUSH EDX case 0x53: // PUSH EBX case 0x54: // PUSH ESP case 0x55: // PUSH EBP case 0x56: // PUSH ESI case 0x57: // PUSH EDI case 0x58: // POP EAX case 0x59: // POP ECX case 0x5A: // POP EDX case 0x5B: // POP EBX case 0x5C: // POP ESP case 0x5D: // POP EBP case 0x5E: // POP ESI case 0x5F: // POP EDI case 0x60: // PUSHAD case 0x61: // PUSHAD case 0x90: // NOP case 0x91: // XCHG EAX,ECX case 0x92: // XCHG EAX,EDX case 0x93: // XCHG EAX,EBX case 0x94: // XCHG EAX,ESP case 0x95: // XCHG EAX,EBP case 0x96: // XCHG EAX,ESI case 0x97: // XCHG EAX,EDI case 0xC3: // RET (near) case 0xCB: // RET (far) case 0xCC: // INT 3 case 0xCE: // INTO nSize = 1 ; break ; case 0x0C: // OR AL,imm8 case 0x3F: // CMP AL,imm8 case 0x6A: // PUSH imm8 case 0xA0: // MOV AL,moffs8 case 0xA2: // MOV moffs8,AL case 0xB0: // MOV AL, imm8 case 0xB1: // MOV CL, imm8 case 0xB2: // MOV DL, imm8 case 0xB3: // MOV BL, imm8 case 0xB4: // MOV AH, imm8 case 0xB5: // MOV CH, imm8 case 0xB6: // MOV DH, imm8 case 0xB7: // MOV BH, imm32 case 0xCD: // INT imm8 nSize = 2 ; break ; case 0xC2: // RET imm16 (near) case 0xCA: // RET imm16 (far) nSize = 3 ; break ; case 0x0D: // OR EAX, imm32 case 0x3D: // CMP EAX,imm32 case 0x68: // PUSH imm32 case 0xA1: // MOV EAX,moffs32 case 0xA3: // MOV moffs32,EAX case 0xB8: // MOV EAX, imm32 case 0xB9: // MOV ECX, imm32 case 0xBA: // MOV EDX, imm32 case 0xBB: // MOV EBX, imm32 case 0xBC: // MOV ESP, imm32 case 0xBD: // MOV EBP, imm32 case 0xBE: // MOV ESI, imm32 case 0xBF: // MOV EDI, imm32 case 0xE8: // CALL rel32 case 0xE9: // JMP rel32 nSize = 5 ; break ; case 0x00: // ADD r/m8,r8 case 0x01: // ADD r/m32,r32 case 0x02: // ADD r8,r/m8 case 0x03: // ADD r32,r/m32 case 0x08: // OR r/m8,r8 case 0x09: // OR r/m16,r16 case 0x0A: // OR r8,r/m8 case 0x0B: // OR r32,m/r32 case 0x33: // XOR r32,r/m32 case 0x38: // CMP r/m8,r8 case 0x39: // CMP r/m32,r32 case 0x3A: // CMP r8,r/m8 case 0x3B: // CMP r32,r/m32 case 0x85: // TEST r/m32,r32 case 0x88: // MOV r/m8,r8 case 0x89: // MOV r/m32,r32 case 0x8A: // MOV r8,r/m8 case 0x8B: // MOV r32,r/m32 case 0x8C: // MOV r/m16,Sreg case 0x8D: // LEA r32,m case 0x8E: // MOV Sreg,r/m16 nSize = _Dasm_ReadModRmAndSid (1, pInst) ; break ; case 0x64: // FS instruction prefix if( Dasm_GetInstructionSize(pInst+1,&nSize,&nSizeIfMoved) ) { nSize += 1 ; nSizeIfMoved += 1 ; } break ; case 0xC6: switch( OPCODE(pInst[1]) ) { case 0: // MOV r/m8,imm8 nSize = _Dasm_ReadModRmAndSid (1, pInst) + 1 ; break ; } break ; case 0xC7: switch( OPCODE(pInst[1]) ) { case 0: // MOV r/m32,imm32 nSize = _Dasm_ReadModRmAndSid (1, pInst) + 4 ; break ; } break ; case 0x80: switch( OPCODE(pInst[1]) ) { case 1: // OR r/m8,imm8 case 7: // CMP r/m8,imm8 nSize = _Dasm_ReadModRmAndSid (1, pInst) + 1 ; break ; } break ; case 0x81: switch( OPCODE(pInst[1]) ) { case 1: // OR r/m32,imm32 case 5: // SUB r/m32,imm32 case 7: // CMP r/m32,imm32 nSize = _Dasm_ReadModRmAndSid (1, pInst) + 4 ; break ; } break ; case 0x83: switch( OPCODE(pInst[1]) ) { case 1: // OR r/m32,imm8 case 5: // SUB r/m32,imm8 case 7: // CMP r/m32,imm8 nSize = _Dasm_ReadModRmAndSid (1, pInst) + 1 ; break ; } break ; case 0xFF: switch( OPCODE(pInst[1]) ) { case 6: // PUSH r/m32 nSize = _Dasm_ReadModRmAndSid (1, pInst) ; break ; } break ; default: TRACE_ERROR (TEXT("Unknown instruction 0x%02X\n"), pInst[0]) ; break ; } if( nSizeIfMoved==0 ) nSizeIfMoved = nSize ; if( pnSize ) *pnSize = nSize ; if( pnSizeIfMoved ) *pnSizeIfMoved = nSizeIfMoved ; return nSize != 0 ; }
BOOL Dasm_MoveProgram (BYTE * pSrc, UINT nSrcSize, BYTE * pDst, UINT * pnDstSize, UINT nDstMax) { int nReadBytes = 0 ; int nWritBytes = 0 ; int nReadInstSize ; int nWritInstSize ; BYTE *pReadInst, *pWritInst ; DWORD dwAddress ; BOOL bSuccess ; if( nDstMax < nSrcSize ) { TRACE_ERROR (TEXT("Destination buffer too small\n")) ; return FALSE ; } while( nReadBytes < nSrcSize ) { pReadInst = pSrc + nReadBytes ; pWritInst = pDst + nWritBytes ; bSuccess = Dasm_GetInstructionSize (pReadInst, &nReadInstSize, &nWritInstSize) ; if( ! bSuccess ) { TRACE_ERROR (TEXT("GetInstructionSize failed\n")) ; return FALSE ; } if( nReadBytes+nReadInstSize > nSrcSize ) { TRACE_ERROR (TEXT("Invalid source size\n")) ; return FALSE ; } if( nWritBytes+nWritInstSize > nDstMax ) { TRACE_ERROR (TEXT("Insufficient destination size\n")) ; return FALSE ; } if( pReadInst[0]==0xE8 || pReadInst[0]==0xE9 ) { // CALL rel32 // JMP rel32 ASSERT (nReadInstSize==nWritInstSize) ; dwAddress = *(DWORD*)(pReadInst+1) ; // get source relative dwAddress += (DWORD)pReadInst ; // convert to absolute dwAddress -= (DWORD)pWritInst ; // convert to relative pWritInst[0] = pReadInst[0] ; memcpy (pWritInst+1, &dwAddress, sizeof(DWORD)) ; TRACE_WARNING (TEXT("JMP rel32 or CALL rel32 instruction patched (0x%02X)\n"), pReadInst[0]) ; } else if( ( pReadInst[0]==0x0F && pReadInst[1]>=0x81 && pReadInst[1]<=0x8F ) || pReadInst[0]==0xE3 ) { // Jxx rel32 ASSERT (nReadInstSize==nWritInstSize) ; dwAddress = *(DWORD*)(pReadInst+2) ; // get source relative dwAddress += (DWORD)pReadInst ; // convert to absolute dwAddress -= (DWORD)pWritInst ; // convert to relative pWritInst[0] = pReadInst[0] ; pWritInst[1] = pReadInst[1] ; memcpy (pWritInst+2, &dwAddress, sizeof(DWORD)) ; TRACE_WARNING (TEXT("Jxx rel32 instruction patched (0x%02X)\n"), pReadInst[0]) ; } else if( pReadInst[0]>=0x71 && pReadInst[0]<=0x7F ) { // Jxx rel8 ASSERT (nReadInstSize+3==nWritInstSize) ; dwAddress = *(BYTE*)(pReadInst+1) ; // get source relative dwAddress += (DWORD)pReadInst ; // convert to absolute dwAddress -= (DWORD)pWritInst ; // convert to relative pWritInst[0] = 0x0F ; pWritInst[1] = pReadInst[0] + 0x10 ; memcpy (pWritInst+2, &dwAddress, sizeof(DWORD)) ; TRACE_WARNING (TEXT("Jxx rel8 instruction patched (0x%02X)\n"), pReadInst[0]) ; } else { // Any other instruction ASSERT (nReadInstSize==nWritInstSize) ; memcpy (pWritInst, pReadInst, nReadInstSize) ; } nReadBytes += nReadInstSize ; nWritBytes += nWritInstSize ; } ASSERT (nReadBytes==nSrcSize) ; *pnDstSize = nWritBytes ; return TRUE ; }
NTSTATUS Hook_InstallHook (HOOKSTRUCT * pHook) { DWORD dwJmpDst ; int nInstSize, nInstSizeIfMoved ; int i ; DWORD nOldCr0, nNewCr0 ; BOOL bSuccess ; ASSERT (pHook!=NULL) ; ASSERT (pHook->pTargetFunc!=NULL) ; // // Mesure head size and stub size // ------------------------------ TRACE_INFO (TEXT("Measure head size (target=0x%08X)\n"), pHook->pTargetFunc) ; pHook->nHeadSize = 0 ; pHook->nStubSize = 5 ; // == sizeof(JMP rel32) while( pHook->nHeadSize < 5 ) // need at least 5 char to path { bSuccess = Dasm_GetInstructionSize (pHook->pTargetFunc+pHook->nHeadSize, &nInstSize, &nInstSizeIfMoved) ; if( ! bSuccess ) { TRACE_ERROR (TEXT("GetInstructionSize failed (target=0x%08X)\n"), pHook->pTargetFunc) ; return STATUS_UNSUCCESSFUL ; } pHook->nHeadSize += nInstSize ; pHook->nStubSize += nInstSizeIfMoved ; if( pHook->nStubSize >= MAX_STUB_SIZE ) { TRACE_ERROR (TEXT("Stub too big (target=0x%08X)\n"), pHook->pTargetFunc) ; return STATUS_UNSUCCESSFUL ; } } TRACE_INFO (TEXT("Head size = %d\n"), pHook->nHeadSize) ; TRACE_INFO (TEXT("Stub size = %d\n"), pHook->nStubSize) ; // // Backup the function head // ------------------------ TRACE_INFO (TEXT("Backup function head\n")) ; memcpy (pHook->pHead, pHook->pTargetFunc, pHook->nHeadSize) ; // // Create stub // ----------- TRACE_INFO (TEXT("Create stub (stub=0x%08X)\n"), pHook->pStub) ; // create stub bSuccess = Dasm_MoveProgram (pHook->pTargetFunc, pHook->nHeadSize, pHook->pStub, &pHook->nStubSize, MAX_STUB_SIZE) ; if( ! bSuccess ) { TRACE_ERROR (TEXT("MoveProgram failed (target=0x%08X)\n"), pHook->pTargetFunc) ; return STATUS_UNSUCCESSFUL ; } // add jmp at the end of the stub dwJmpDst = (DWORD)pHook->pTargetFunc - (DWORD)pHook->pStub - pHook->nStubSize ; pHook->pStub[pHook->nStubSize] = 0xE9 ; // JMP rel32 memcpy (&pHook->pStub[pHook->nStubSize+1], &dwJmpDst, 4) ; pHook->nStubSize += 5 ; TRACE_INFO (TEXT("Stub created (stub=0x%08X, size=%u)\n"), pHook->pStub, pHook->nStubSize) ; // // Remove target protection // ------------------------ TRACE_INFO (TEXT("Removing write protection\n")) ; GET_CR0 (nOldCr0) ; nNewCr0 = nOldCr0 & ~0x10000 ; SET_CR0 (nNewCr0) ; // // Patch target // ------------ TRACE_INFO (TEXT("Patching target\n")) ; // overwrite head with a jmp to spy function pHook->pTargetFunc[0] = 0xE9 ; // JMP rel32 dwJmpDst = (DWORD)pHook->pHookFunc - (DWORD)pHook->pTargetFunc - 5 ; // 5==sizeof(JMP rel32) memcpy (pHook->pTargetFunc+1, &dwJmpDst, 4) ; // complete head with NOPs for( i=5 ; i<pHook->nHeadSize ; i++ ) pHook->pTargetFunc[i] = 0x90 ; // NOP // // Restore target protection // ------------------------- TRACE_INFO (TEXT("Restoring write protection\n")) ; SET_CR0 (nOldCr0) ; return STATUS_SUCCESS ; }