Exemplo n.º 1
0
/* Return true if frame appears to be a legitimate, readable stack frame. */
bool IsValidFrame( const StackFrame *frame )
{
	if( !IsReadableFrame( frame ) )
		return false;

	/* The frame link should only go upwards. */
	if( frame->link <= frame )
		return false;

	/* The link should be on the stack. */
	if( !IsOnStack( frame->link ) )
		return false;

	/* The return address should be in a readable, executable page. */
	if( !IsExecutableAddress( frame->return_address ) )
		return false;

	/* The return address should follow a CALL opcode. */
	if( !PointsToValidCall(frame->return_address) )
		return false;

	return true;
}
Exemplo n.º 2
0
	bool CreateTrampolineFunction(CREATE_TREMPOLINE_T& ct)
	{
		assert(("CreateTrampolineFunction", ct.pTarget != NULL));

#if defined _M_X64
		CALL_ABS call = { 0x15FF, 0x00000000 };
		JMP_ABS  jmp  = { 0x25FF, 0x00000000 };
		JCC_ABS  jcc  = { 0x70, 0x02, 0xEB, 0x06, 0x25FF, 0x00000000 };
#elif defined _M_IX86
		CALL_REL call = { 0xE8, 0x00000000 };
		JMP_REL  jmp  = { 0xE9, 0x00000000 };
		JCC_REL  jcc  = { 0x800F, 0x00000000 };
#endif

		size_t    oldPos = 0;
		size_t    newPos = 0;
		uintptr_t jmpDest = 0;		// 関数内ジャンプの飛び先アドレス(分岐中判定に使用)
		bool      finished = false;	// 関数終了フラグ
		while (!finished)
		{
			uint8_t *pInst = reinterpret_cast<uint8_t*>(ct.pTarget) + oldPos;
			hde_t hs;
			hde_disasm(pInst, &hs);
			if ((hs.flags & F_ERROR) == F_ERROR)
			{
				return false;
			}

			void*  pCopySrc = pInst;
			size_t copySize = hs.len;

			if (pInst - reinterpret_cast<uint8_t*>(ct.pTarget) >= sizeof(JMP_REL))
			{
				// ターゲット関数へのジャンプを書き込み、関数を終了
				AppendTempAddress(reinterpret_cast<uintptr_t>(pInst), newPos, jmp, ct);

				pCopySrc = &jmp;
				copySize = sizeof(jmp);

				finished = true;
			}
#if defined _M_X64
			// RIP相対アドレッシングを使用している命令 (ModR/M = 00???101B)
			else if ((hs.modrm & 0xC7) == 0x05)
			{
				// RIP相対アドレスのみ書き換え
				AppendRipRelativeAddress(pInst, newPos, hs, ct);

				// JMP (FF /4)なら関数を終了
				if (hs.opcode == 0xFF && hs.modrm_reg == 4)
				{
					finished = true;
				}
			}
#endif
			// 相対直接CALL
			else if (hs.opcode == 0xE8)
			{
				AppendTempAddress(GetRelativeBranchDestination(pInst, hs, false), newPos, call, ct);
				pCopySrc = &call;
				copySize = sizeof(call);
			}
			// 相対直接JMP (EB or E9)
			else if ((hs.opcode & 0xFD) == 0xE9)
			{
				uintptr_t dest = GetRelativeBranchDestination(pInst, hs, hs.opcode == 0xEB);

				// 関数内へのジャンプはそのままコピー(ジャンプ中は命令長が変わるような操作は不可)
				if (IsInternalJump(ct.pTarget, dest))
				{
					jmpDest = std::max<uintptr_t>(jmpDest, dest);
				}
				else
				{
					AppendTempAddress(dest, newPos, jmp, ct);
					pCopySrc = &jmp;
					copySize = sizeof(jmp);

					// 分岐中でなければ関数を終了
					finished = (reinterpret_cast<uintptr_t>(pInst) >= jmpDest);
				}
			}
			// 相対直接Jcc
			else if ((hs.opcode & 0xF0) == 0x70 || (hs.opcode & 0xFC) == 0xE0 || (hs.opcode2 & 0xF0) == 0x80)
			{
				uintptr_t dest = GetRelativeBranchDestination(pInst, hs, (hs.opcode & 0xF0) == 0x70 || (hs.opcode & 0xFC) == 0xE0);

				// 関数内へのジャンプはそのままコピー(分岐中は命令長が変わるような操作は不可)
				if (IsInternalJump(ct.pTarget, dest))
				{
					jmpDest = std::max<uintptr_t>(jmpDest, dest);
				}
				else if ((hs.opcode & 0xFC) == 0xE0) // 関数外へのJCXZ, JECXZ には対応しない
				{
					return false;
				}
				else
				{
					AppendTempAddress(dest, newPos, jcc, ct);
					SetJccOpcode(hs, jcc);
					pCopySrc = &jcc;
					copySize = sizeof(jcc);
				}
			}
			// RET (C2 or C3)
			else if ((hs.opcode & 0xFE) == 0xC2)
			{
				// 分岐中でなければトランポリン関数を終了
				finished = (reinterpret_cast<uintptr_t>(pInst) >= jmpDest);
			}

			// 分岐中は命令長が変わるような操作は不可
			if (reinterpret_cast<uintptr_t>(pInst) < jmpDest && copySize != hs.len)
			{
				return false;
			}

			ct.trampoline.resize(newPos + copySize);
			memcpy(&ct.trampoline[ newPos ], pCopySrc, copySize);

			ct.oldIPs.push_back(oldPos);
			oldPos += hs.len;
			ct.newIPs.push_back(newPos);
			newPos += copySize;
		}

		// Is there enough place for a long jump?
		if (oldPos < sizeof(JMP_REL) && !IsCodePadding(reinterpret_cast<uint8_t*>(ct.pTarget) + oldPos, sizeof(JMP_REL) - oldPos))
		{
			// Is there enough place for a short jump?
			if (oldPos < sizeof(JMP_REL_SHORT) && !IsCodePadding(reinterpret_cast<uint8_t*>(ct.pTarget) + oldPos, sizeof(JMP_REL_SHORT) - oldPos))
			{
				return false;
			}

			// Can we place the long jump above the function?
			if (!IsExecutableAddress(reinterpret_cast<uint8_t*>(ct.pTarget) - sizeof(JMP_REL)))
			{
				return false;
			}

			if (!IsCodePadding(reinterpret_cast<uint8_t*>(ct.pTarget) - sizeof(JMP_REL), sizeof(JMP_REL)))
			{
				return false;
			}

			ct.patchAbove = true;
		}

		return true;
	}
Exemplo n.º 3
0
/* This x86 backtracer attempts to walk the stack frames.  If we come to a
 * place that doesn't look like a valid frame, we'll look forward and try
 * to find one again. */
static void do_backtrace( const void **buf, size_t size, const BacktraceContext *ctx )
{
	/* Read /proc/pid/maps to find the address range of the stack. */
	get_readable_ranges( g_ReadableBegin, g_ReadableEnd, 1024 );
	get_readable_ranges( g_ExecutableBegin, g_ExecutableEnd, 1024, READABLE_ONLY|EXECUTABLE_ONLY );

	/* Find the stack memory blocks. */
	g_StackBlock1 = find_address( ctx->sp, g_ReadableBegin, g_ReadableEnd );
	g_StackBlock2 = find_address( SavedStackPointer, g_ReadableBegin, g_ReadableEnd );

	/* Put eip at the top of the backtrace. */
	/* XXX: We want EIP even if it's not valid, but we can't put NULL on the
	 * list, since it's NULL-terminated.  Hmm. */
	unsigned i=0;
	if( i < size-1 && ctx->ip ) // -1 for NULL
		buf[i++] = ctx->ip;

	/* If we did a CALL to an invalid address (eg. call a NULL callback), then
	 * we won't have a stack frame for the function that called it (since the
	 * stack frame is set up by the called function), but if esp hasn't been
	 * changed after the CALL, the return address will be esp[0].  Grab it. */
	if( IsOnStack( ctx->sp ) )
	{
		const void *p = ((const void **) ctx->sp)[0];
		if( IsExecutableAddress( p ) && PointsToValidCall( p ) && i < size-1 ) // -1 for NULL
			buf[i++] = p;
	}

#if 0
	/* ebp is usually the frame pointer. */
	const StackFrame *frame = (StackFrame *) ctx->bp;

	/* If ebp doesn't point to a valid stack frame, we're probably in
	 * -fomit-frame-pointer code.  Ignore it; use esp instead.  It probably
	 * won't point to a stack frame, but it should at least give us a starting
	 * point in the stack. */
	if( !IsValidFrame( frame ) )
		frame = (StackFrame *) ctx->sp;
#endif

	/* Actually, let's just use esp.  Even if ebp points to a valid stack frame, there might be
	 * -fomit-frame-pointer calls in front of it, and we want to get those. */
	const StackFrame *frame = (StackFrame *) ctx->sp;

	while( i < size-1 ) // -1 for NULL
	{
		/* Make sure that this frame address is readable, and is on the stack. */
		if( !IsReadableFrame( frame ) )
			break;

		if( !IsValidFrame( frame ) )
		{
			/* We've lost the frame.  We might have crashed while in a call in -fomit-frame-pointer
			 * code.  Iterate through the stack word by word.  If a word is possibly a valid return
			 * address, record it.  This is important; if we don't do this, we'll lose too many
			 * stack frames at the top of the trace.  This can have false positives, and introduce
			 * garbage into the trace, but we should eventually find a real stack frame. */
			void **p = (void **) frame;
			if( IsExecutableAddress( *p ) && PointsToValidCall( *p ) )
				buf[i++] = *p;

			/* The frame pointer is invalid.  Just move forward one word. */
			frame = (StackFrame *) (((char *)frame)+4);
			continue;
		}

		/* Valid frame.  Store the return address, and hop forward. */
		buf[i++] = frame->return_address;
		frame = frame->link;
	}

	buf[i] = NULL;
}
Exemplo n.º 4
0
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal)
{
    MH_STATUS status = MH_OK;

    EnterSpinLock();

    if (g_hHeap != NULL)
    {
        if ((g_Flags & MH_FLAGS_SKIP_EXEC_CHECK)
            || (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour))
            )
        {
            UINT pos = FindHookEntry(pTarget);
            if (pos == INVALID_HOOK_POS)
            {
                LPVOID pBuffer = AllocateBuffer(pTarget);
                if (pBuffer != NULL)
                {
                    TRAMPOLINE ct;

                    ct.pTarget = pTarget;
                    ct.pDetour = pDetour;
                    ct.pTrampoline = pBuffer;
                    if (CreateTrampolineFunction(&ct))
                    {
                        PHOOK_ENTRY pHook = AddHookEntry();
                        if (pHook != NULL)
                        {
                            pHook->pTarget     = ct.pTarget;
#ifdef _M_X64
                            pHook->pDetour     = ct.pRelay;
#else
                            pHook->pDetour     = ct.pDetour;
#endif
                            pHook->pTrampoline = ct.pTrampoline;
                            pHook->patchAbove  = ct.patchAbove;
                            pHook->isEnabled   = FALSE;
                            pHook->queueEnable = FALSE;
                            pHook->nIP         = ct.nIP;
                            memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs));
                            memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs));

                            // Back up the target function.

                            if (ct.patchAbove)
                            {
                                memcpy(
                                    pHook->backup,
                                    (LPBYTE)pTarget - sizeof(JMP_REL),
                                    sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
                            }
                            else
                            {
                                memcpy(pHook->backup, pTarget, sizeof(JMP_REL));
                            }

                            if (ppOriginal != NULL)
                                *ppOriginal = pHook->pTrampoline;
                        }
                        else
                        {
                            status = MH_ERROR_MEMORY_ALLOC;
                        }
                    }
                    else
                    {
                        status = MH_ERROR_UNSUPPORTED_FUNCTION;
                    }

                    if (status != MH_OK)
                    {
                        FreeBuffer(pBuffer);
                    }
                }
                else
                {
                    status = MH_ERROR_MEMORY_ALLOC;
                }
            }
            else
            {
                status = MH_ERROR_ALREADY_CREATED;
            }
        }
        else
        {
            status = MH_ERROR_NOT_EXECUTABLE;
        }
    }
    else
    {
        status = MH_ERROR_NOT_INITIALIZED;
    }

    LeaveSpinLock();

    return status;
}
Exemplo n.º 5
0
	MH_STATUS CreateHook(void* pTarget, void* const pDetour, void** ppOriginal)
	{
		CriticalSection::ScopedLock lock(*gCS);

		if (!gIsInitialized)
		{
			return MH_ERROR_NOT_INITIALIZED;
		}

		HOOK_ENTRY *pHook = FindHook(pTarget);
		if (pHook != NULL)
		{
			return MH_ERROR_ALREADY_CREATED;
		}

		if (!IsExecutableAddress(pTarget) || !IsExecutableAddress(pDetour))
		{
			return MH_ERROR_NOT_EXECUTABLE;
		}

		{
			bool committed = false;
			RollbackIfNotCommitted scopedRollback(&committed);

			// トランポリン関数を作成する
			CREATE_TREMPOLINE_T ct = { 0 };
			ct.pTarget = pTarget;
			if (!CreateTrampolineFunction(ct))
			{
				return MH_ERROR_UNSUPPORTED_FUNCTION;
			}

			void* pJmpPtr = pTarget;
			if (ct.patchAbove)
			{
				pJmpPtr = reinterpret_cast<char*>(pJmpPtr) - sizeof(JMP_REL);
			}

			void* pTrampoline = AllocateCodeBuffer(pJmpPtr, ct.trampoline.size());
			if (pTrampoline == NULL)
			{
				return MH_ERROR_MEMORY_ALLOC;
			}
#if defined _M_X64
			void* pTable = AllocateDataBuffer(pTrampoline, (ct.table.size() + 1) * sizeof(uintptr_t));
			if (pTable == NULL)
			{
				return MH_ERROR_MEMORY_ALLOC;
			}
#endif

			ct.pTrampoline = pTrampoline;
#if defined _M_X64
			ct.pTable = pTable;
#endif
			if (!ResolveTemporaryAddresses(ct))
			{
				return MH_ERROR_UNSUPPORTED_FUNCTION;
			}

			memcpy(pTrampoline, &ct.trampoline[ 0 ], ct.trampoline.size());
#if defined _M_X64
			if (ct.table.size() != 0)
			{
				memcpy(pTable, &ct.table[ 0 ], ct.table.size() * sizeof(uintptr_t));
			}
#endif

			// ターゲット関数のバックアップをとる
			size_t backupSize = sizeof(JMP_REL);
			if (ct.patchAbove)
			{
				backupSize += sizeof(JMP_REL_SHORT);
			}

			void* pBackup = AllocateDataBuffer(NULL, backupSize);
			if (pBackup == NULL)
			{
				return MH_ERROR_MEMORY_ALLOC;
			}

			memcpy(pBackup, pJmpPtr, backupSize);

			// 中継関数を作成する
#if defined _M_X64
			void* pRelay = AllocateCodeBuffer(pJmpPtr, sizeof(JMP_ABS));
			if (pRelay == NULL)
			{
				return MH_ERROR_MEMORY_ALLOC;
			}

			WriteAbsoluteJump(pRelay, pDetour, reinterpret_cast<uintptr_t*>(pTable) + ct.table.size());
#endif
			CommitBuffer();
			committed = true;

			// フック情報の登録
			HOOK_ENTRY hook = { 0 };
			hook.pTarget = pTarget;
			hook.pDetour = pDetour;
#if defined _M_X64
			hook.pTable  = pTable;
			hook.pRelay  = pRelay;
#endif
			hook.pTrampoline = pTrampoline;
			hook.pBackup = pBackup;
			hook.patchAbove = ct.patchAbove;
			hook.isEnabled = false;
			hook.queueEnable = false;
			hook.oldIPs = ct.oldIPs;
			hook.newIPs = ct.newIPs;

			std::vector<HOOK_ENTRY>::iterator i	= std::lower_bound(gHooks.begin(), gHooks.end(), hook);
			i = gHooks.insert(i, hook);
			pHook = &(*i);
		}

		// OUT引数の処理
		*ppOriginal = pHook->pTrampoline;

		return MH_OK;
	}