Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
/* These assembly functions represent patterns that were already hooked by
* another application before our detour.
*/
#ifndef mozilla_AssemblyPayloads_h
#define mozilla_AssemblyPayloads_h
#include "mozilla/Attributes.h"
#include <cstdint>
#define PADDING_256_NOP \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;" \
"nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;nop;"
extern "C" {
#if defined(__clang__)
# if defined(_M_X64)
constexpr uintptr_t JumpDestination = 0x7fff00000000;
__declspec(dllexport) MOZ_NAKED void MovPushRet() {
asm volatile(
"mov %0, %%rax;"
"push %%rax;"
"ret;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void MovRaxJump() {
asm volatile(
"mov %0, %%rax;"
"jmpq *%%rax;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void DoubleJump() {
asm volatile(
"jmp label1;"
"label2:"
"mov %0, %%rax;"
"jmpq *%%rax;"
// 0x100 bytes padding to generate jmp rel32 instead of jmp rel8
PADDING_256_NOP
"label1:"
"jmp label2;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void NearJump() {
asm volatile(
"jae label3;"
"je label3;"
"jne label3;"
"label4:"
"mov %0, %%rax;"
"jmpq *%%rax;"
// 0x100 bytes padding to generate jae rel32 instead of jae rel8
PADDING_256_NOP
"label3:"
"jmp label4;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void OpcodeFF() {
// Skip PUSH (FF /6) because clang prefers Opcode 50+rd
// to translate PUSH r64 rather than Opcode FF.
asm volatile(
"incl %eax;"
"decl %ebx;"
"call *%rcx;"
"jmp *(%rip);" // Indirect jump to 0xcccccccc`cccccccc
"int $3;int $3;int $3;int $3;"
"int $3;int $3;int $3;int $3;");
}
__declspec(dllexport) MOZ_NAKED void IndirectCall() {
asm volatile(
"call *(%rip);" // Indirect call to 0x90909090`90909090
"nop;nop;nop;nop;nop;nop;nop;nop;"
"ret;");
}
__declspec(dllexport) MOZ_NAKED void MovImm64() {
asm volatile(
"mov $0x1234567812345678, %r10;"
"nop;nop;nop");
}
static unsigned char __attribute__((used)) gGlobalValue = 0;
__declspec(dllexport) MOZ_NAKED void RexCmpRipRelativeBytePtr() {
asm volatile(
"cmpb %sil, gGlobalValue(%rip);"
"nop;nop;nop;nop;nop;nop;nop;nop;");
}
// A valid function that uses "cmp byte ptr [rip + offset], sil". It returns
// true if and only if gGlobalValue is equal to aValue.
__declspec(dllexport) MOZ_NAKED bool IsEqualToGlobalValue(
unsigned char aValue) {
asm volatile(
"xorl %eax, %eax;"
"pushq %rsi;"
"pushq %rcx;"
"popq %rsi;"
"cmpb %sil, gGlobalValue(%rip);"
"nop;"
// end of 13 first bytes
"movq $1, %rsi;"
"cmoveq %rsi, %rax;"
"popq %rsi;"
"retq;");
}
// This code reproduces bug 1798787: it uses the same prologue, the same unwind
// info, and it has a call instruction that starts within the 13 first bytes.
MOZ_NAKED void DetouredCallCode(uintptr_t aCallee) {
asm volatile(
"subq $0x28, %rsp;"
"testq %rcx, %rcx;"
"jz exit;"
"callq *%rcx;"
"exit:"
"addq $0x28, %rsp;"
"retq;");
}
constexpr uint8_t gDetouredCallCodeSize = 16; // size of function in bytes
alignas(uint32_t) uint8_t gDetouredCallUnwindInfo[] = {
0x01, // Version (1), Flags (0)
0x04, // SizeOfProlog (4)
0x01, // CountOfUnwindCodes (1)
0x00, // FrameRegister (0), FrameOffset (0)
// UnwindCodes[0]
0x04, // .OffsetInProlog (4)
0x42, // .UnwindOpCode(UWOP_ALLOC_SMALL=2), .UnwindInfo (4)
};
// This points to the same code as DetouredCallCode, but dynamically generated
// so that it can have custom unwinding info. See TestDllInterceptor.cpp.
extern decltype(&DetouredCallCode) gDetouredCall;
// This is just a jumper: our hooking code will thus detour the jump target
// -- it will not detour DetouredCallJumper. We need to do this to point our
// hooking code to the dynamic code, because our hooking API works with an
// exported function name.
MOZ_NAKED __declspec(dllexport noinline) void DetouredCallJumper(
uintptr_t aCallee) {
// Ideally we would want this to be:
// jmp qword ptr [rip + offset gDetouredCall]
// Unfortunately, it is unclear how to do that with inline assembly, so we
// use a zero offset and patch it before the test.
asm volatile("jmpq *0(%rip)");
}
# elif defined(_M_IX86)
constexpr uintptr_t JumpDestination = 0x7fff0000;
__declspec(dllexport) MOZ_NAKED void PushRet() {
asm volatile(
"push %0;"
"ret;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void MovEaxJump() {
asm volatile(
"mov %0, %%eax;"
"jmp *%%eax;"
:
: "i"(JumpDestination));
}
__declspec(dllexport) MOZ_NAKED void Opcode83() {
asm volatile(
"xor $0x42, %eax;"
"cmpl $1, 0xc(%ebp);");
}
__declspec(dllexport) MOZ_NAKED void LockPrefix() {
// Test an instruction with a LOCK prefix (0xf0) at a non-zero offset
asm volatile(
"push $0x7c;"
"lock push $0x7c;");
}
__declspec(dllexport) MOZ_NAKED void LooksLikeLockPrefix() {
// This is for a regression scenario of bug 1625452, where we double-counted
// the offset in CountPrefixBytes. When we count prefix bytes in front of
// the 2nd PUSH located at offset 2, we mistakenly started counting from
// the byte 0xf0 at offset 4, which is considered as LOCK, thus we try to
// detour the next byte 0xcc and it fails.
//
// 0: 6a7c push 7Ch
// 2: 68ccf00000 push 0F0CCh
//
asm volatile(
"push $0x7c;"
"push $0x0000f0cc;");
}
__declspec(dllexport) MOZ_NAKED void DoubleJump() {
asm volatile(
"jmp label1;"
"label2:"
"mov %0, %%eax;"
"jmp *%%eax;"
// 0x100 bytes padding to generate jmp rel32 instead of jmp rel8
PADDING_256_NOP
"label1:"
"jmp label2;"
:
: "i"(JumpDestination));
}
# endif
# if !defined(_M_ARM64)
__declspec(dllexport) MOZ_NAKED void UnsupportedOp() {
asm volatile(
"ud2;"
"nop;nop;nop;nop;nop;nop;nop;nop;"
"nop;nop;nop;nop;nop;nop;nop;nop;");
}
// Make sure no instruction ends at 5 (for x86) or 13 (for x64) bytes
__declspec(dllexport) MOZ_NAKED void SpareBytesAfterDetour() {
asm volatile(
"incl %eax;" // 2 bytes on x64, 1 byte on x86
"mov $0x01234567, %eax;" // 5 bytes
"mov $0xfedcba98, %eax;" // 5 bytes
"mov $0x01234567, %eax;" // 5 bytes
"mov $0xfedcba98, %eax;"); // 5 bytes
}
// Make sure no instruction ends at 10 (for x64) bytes
// This is slightly different than SpareBytesAfterDetour so the compiler doesn't
// combine them, which would make the test that detours this one behave
// unexpectedly since it is already detoured.
__declspec(dllexport) MOZ_NAKED void SpareBytesAfterDetourFor10BytePatch() {
asm volatile(
"incl %eax;" // 2 bytes
"mov $0x01234567, %ecx;" // 5 bytes
"mov $0xfedcba98, %ebx;" // 5 bytes
"mov $0x01234567, %eax;" // 5 bytes
"mov $0xfedcba98, %edx;"); // 5 bytes
}
# endif // !defined(_M_ARM64)
#endif // defined(__clang__)
} // extern "C"
#endif // mozilla_AssemblyPayloads_h