ZLUDA/ext/detours/samples/disas/disas.cpp

702 lines
20 KiB
C++
Vendored

/////////////////////////////////////////////////////////////////////////////
//
// Module: disas.cpp (disas.exe - Detours Test Program)
//
// Microsoft Research Detours Package
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#define DETOURS_INTERNAL
#include <detours.h>
#include <stdio.h>
#include <stdlib.h>
///////////////////////////////////////////////////////////////////////// ARM.
//
#ifdef DETOURS_ARM
extern "C" BYTE TestCodes[];
void DumpMemoryFragment(PBYTE pbData, ULONG cbData, ULONG cbSpace)
{
ULONG n = 0;
if (cbData >= 4) {
printf("%04x%04x ", ((PUSHORT)pbData)[0], ((PUSHORT)pbData)[1]);
n += 4;
}
else if (cbData >= 2) {
printf("%04x ", *((PUSHORT)pbData));
n += 2;
}
for (; n < cbSpace; n++) {
if (n < cbData) {
printf("%02x", pbData[n]);
}
else {
printf(" ");
}
}
if (n < cbData) {
printf(".");
}
else {
printf(" ");
}
}
inline ULONG fetch_thumb_opcode(PBYTE pbCode)
{
ULONG Opcode = *(UINT16 *)&pbCode[0];
if (Opcode >= 0xe800) {
Opcode = (Opcode << 16) | *(UINT16 *)&pbCode[2];
}
return Opcode;
}
BOOL IsTerminate(PBYTE pbSrc)
{
ULONG opcode = fetch_thumb_opcode(pbSrc);
if ((opcode & 0xff87) == 0x4700) {
// bx r
return TRUE;
}
#if 0
if ((opcode & 0xfbf08f00) == 0xf2400c00) { // movw r12,#xxxx
return TRUE;
}
if ((opcode == 0xf8dcf000) { // ldr pc,[r12]
ULONG Immediate = ((opcode2 << 12) & 0xf7000000) |
((opcode2 << 1) & 0x08000000) |
((opcode2 << 16) & 0x00ff0000) |
((opcode >> 4) & 0x0000f700) |
((opcode >> 15) & 0x00000800) |
((opcode >> 0) & 0x000000ff);
PBYTE pbTarget = *(PBYTE *)Immediate;
if (detour_is_imported(pbCode, pbTarget)) {
PBYTE pbNew = *(PBYTE *)pbTarget;
DETOUR_TRACE(("%p->%p: skipped over import table.\n", pbCode, pbNew));
return pbNew;
}
}
}
}
#endif
return FALSE;
}
#endif // DETOURS_ARM
///////////////////////////////////////////////////////////////// X86 and X64.
//
#if defined(DETOURS_X86) || defined(DETOURS_X64)
extern "C" BYTE TestCodes[];
void DumpMemoryFragment(PBYTE pbData, ULONG cbData, ULONG cbSpace)
{
ULONG n = 0;
for (; n < cbSpace; n++) {
if (n < cbData) {
printf("%02x", pbData[n]);
}
else {
printf(" ");
}
}
if (n < cbData) {
printf(".");
}
else {
printf(" ");
}
}
BOOL IsTerminate(PBYTE pbSrc)
{
if ((0xC3 == pbSrc[0] && 0x00 == pbSrc[1]) || // bx lr
0xCB == pbSrc[0] || // RETF
0xC2 == pbSrc[0] || // RET dw
0xCA == pbSrc[0] || // RETF dw
0xEB == pbSrc[0] || // JMP ob
0xE9 == pbSrc[0] || // JMP ol
0xEA == pbSrc[0]) { // JMP ol
return TRUE;
}
if (0xff == pbSrc[0] && 0x25 == pbSrc[1]) // JMP [addr]
return TRUE;
return FALSE;
}
#endif // DETOURS_X86 || DETOURS_X64
/////////////////////////////////////////////////////////// X86, X64, and ARM.
//
#if defined(DETOURS_X86) || defined(DETOURS_X64) || defined(DETOURS_ARM)
struct BasicBlockLink
{
public:
BasicBlockLink * m_pNext;
PBYTE m_pbEntry;
PCHAR m_pszName;
public:
BasicBlockLink(PBYTE pbEntry, PCHAR pszName = NULL)
{
m_pNext = NULL;
m_pbEntry = pbEntry;
m_pszName = pszName;
*s_ppTail = this;
s_ppTail = &m_pNext;
}
BasicBlockLink * Next()
{
return m_pNext;
}
static BasicBlockLink * GetListHead()
{
return s_pHead;
}
protected:
static BasicBlockLink * s_pHead;
static BasicBlockLink ** s_ppTail;
};
BasicBlockLink * BasicBlockLink::s_pHead = NULL;
BasicBlockLink ** BasicBlockLink::s_ppTail = &BasicBlockLink::s_pHead;
static PBYTE s_pbBegin = NULL;
static PBYTE s_pbLimit = NULL;
int TestDetourCopyInstruction(PBYTE pbSrcInstruction, PCHAR pszFunction)
{
PBYTE pbSrc = pbSrcInstruction;
ULONG nIns = 0;
if (pszFunction) {
printf("%s:\n", pszFunction);
}
for (; nIns < 4096; nIns++) {
BYTE rbDst[128];
PVOID pbDstPool = (PVOID)(rbDst + sizeof(rbDst));
LONG lExtra = 0;
PVOID pbTarget = NULL;
ULONG cbStep = (ULONG)((PBYTE)DetourCopyInstruction(rbDst, &pbDstPool, pbSrc,
&pbTarget, &lExtra) - pbSrc);
printf(" %p:", pbSrc);
DumpMemoryFragment(rbDst, cbStep, 10);
printf(" ");
DumpMemoryFragment(rbDst, cbStep, 10);
if (pbTarget) {
if (pbTarget == DETOUR_INSTRUCTION_TARGET_DYNAMIC) {
printf(" Dynamic\n");
}
else {
printf(" %p%c\n", pbTarget,
(pbTarget >= s_pbBegin && pbTarget < s_pbLimit) ? ' ' : '!');
}
}
else {
printf("\n");
}
if (pbTarget && pbTarget != DETOUR_INSTRUCTION_TARGET_DYNAMIC) {
if (pbTarget > pbSrc &&
pbTarget >= s_pbBegin &&
pbTarget < s_pbLimit
) {
(void) new BasicBlockLink((PBYTE)pbTarget, NULL);
}
}
if (IsTerminate(pbSrc)) {
break;
}
pbSrc += cbStep;
}
return nIns;
}
BOOL CALLBACK ExportCallback(_In_opt_ PVOID pContext,
_In_ ULONG nOrdinal,
_In_opt_ LPCSTR pszName,
_In_opt_ PVOID pCode)
{
(void)pContext;
(void)nOrdinal;
(void)pCode;
(VOID) new BasicBlockLink((PBYTE)pCode, pszName ? pszName : "[NO NAME]");
return TRUE;
}
#endif // DETOURS_X86 || DETOURS_X64
//////////////////////////////////////////////////////////////////////// IA64.
//
#ifdef DETOURS_IA64
#pragma warning(disable: 4201) // ignore warning about unnamed sturcture in union.
void DumpHi(PBYTE pbData, ULONG cbData, ULONG cbSpace)
{
ULONG n = 0;
for (; n < cbSpace; n++) {
if (n < cbData) {
printf("%02x", pbData[(cbData - 1) - n]);
}
else {
printf(" ");
}
}
printf("\n");
}
struct DETOUR_IA64_BUNDLE_DISASSEMBLE : public DETOUR_IA64_BUNDLE
{
public:
void SetBrx(UINT64 raw)
{
SetBrl();
SetBrlImm(raw);
}
void Dis()
{
const char szUnitNames[17] = "?aimbflx?AIMBFLX";
printf("%p: ", data);
BYTE nTemplate = GetTemplate();
BYTE nInst0 = GetInst0();
BYTE nInst1 = GetInst1();
BYTE nInst2 = GetInst2();
BYTE nUnit0 = GetUnit0();
BYTE nUnit1 = GetUnit1();
BYTE nUnit2 = GetUnit2();
if (nUnit1 == L_UNIT) { // MLX instruction
UINT64 d2 = (
// 0x0000000000fffff0
((wide[1] & 0x00fffff000000000) >> 32) |
// 0x000000ffff000000
((wide[0] & 0xffff000000000000) >> 24) |
// 0x7fffff0000000000
((wide[1] & 0x00000000007fffff) << 40) |
// 0x8000000000000000
((wide[1] & 0x0800000000000000) << 4)
);
printf("%02x %c%01x %010I64lx %c%01x %016I64lx",
nTemplate,
szUnitNames[nUnit0], nInst0, GetData0(),
szUnitNames[nUnit2], nInst2, d2);
}
else {
printf("%02x %c%01x %010I64lx %c%01x %010I64lx %c%01x %010I64lx",
nTemplate,
szUnitNames[nUnit0], nInst0, GetData0(),
szUnitNames[nUnit1], nInst1, GetData1(),
szUnitNames[nUnit2], nInst2, GetData2());
}
if (IsBrl()) {
printf(" brl %p", GetBrlTarget());
}
else if (IsMovlGp()) {
printf(" movl gp=%p", GetMovlGp());
}
if ((wide[0] & 0xfffffc000603ffff) == 0x002024000200100b &&
wide[1] == 0x0004000000203008) {
ULONG64 offset =
((wide[0] & 0x0000000001fc0000) >> 18) | // imm7b
((wide[0] & 0x000001ff00000000) >> 25) | // imm9d
((wide[0] & 0x00000000f8000000) >> 11); // imm5c
if (wide[0] & 0x0000020000000000) {
offset |= 0xffffffffffe00000;
}
printf(" imm=%016I64lx", offset);
}
printf("\n");
}
};
//////////////////////////////////////////////////////////////////////////////
//
BOOL CALLBACK ExportCallbackIA64(_In_opt_ PVOID pContext,
_In_ ULONG nOrdinal,
_In_opt_ LPCSTR pszName,
_In_opt_ PVOID pCode)
{
(void)pContext;
(void)nOrdinal;
DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = *(DETOUR_IA64_BUNDLE_DISASSEMBLE **)pCode;
DETOUR_IA64_BUNDLE temp;
if (!pb[0].Copy(&temp)) {
printf("%s:\n ", pszName ? pszName : "[NO NAME]");
pb[0].Dis();
}
return TRUE;
}
#if 0
void TestBoth()
{
LPVOID pvBase = VirtualAlloc((PBYTE)0x800000000, 0x10000,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
DETOUR_IA64_BUNDLE *pbBase = (DETOUR_IA64_BUNDLE *)pvBase;
DETOUR_IA64_BUNDLE *pb = pbBase;
printf("TestBoth:\n");
for (UINT64 i = 0x10; i < 0x8000000000000000; i <<= 1) {
pb->SetMovlGp(i);
if (pb->GetMovlGp() != i) {
printf("Error in MovlGp!\n");
return;
}
pb++;
pb->SetBrl(i);
if (pb->GetBrlEip() != i) {
printf("Error in Brl!\n");
return;
}
pb++;
}
for (UINT64 i = (UINT64)(INT64)-0x10; i > 0; i <<= 1) {
pb->SetMovlGp(i);
if (pb->GetMovlGp() != i) {
printf("Error in MovlGp!\n");
return;
}
pb++;
pb->SetBrl(i);
if (pb->GetBrlEip() != i) {
printf("Error in Brl!\n");
return;
}
pb++;
}
printf("u %p %p\n", pbBase, pb);
}
#endif
#endif // DETOURS_IA64
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR lpszCmdLine, int nCmdShow)
{
(void)hprev;
(void)hinst;
(void)lpszCmdLine;
(void)nCmdShow;
// Bug report, but it works here.
// 07ff8`4b783054 49ba 70b3d93a d40fb998 mov r10,98B90FD43AD9B370h
//
{
static const UCHAR mov_r10_imm64[] = {0x49, 0xba, 1, 2, 3, 4, 5, 6, 7, 8 };
PVOID const after = DetourCopyInstructionX64(0, 0, const_cast<PUCHAR>(mov_r10_imm64), 0, 0);
if (after != &mov_r10_imm64 + 1)
{
printf("mov_r10_imm64 failed, expected:%p vs. got:%p\n", &mov_r10_imm64 + 1, after);
if (IsDebuggerPresent())
{
__debugbreak();
DetourCopyInstructionX64(0, 0, const_cast<PUCHAR>(mov_r10_imm64), 0, 0);
}
return 1;
}
}
#ifdef DETOURS_IA64
// First we check the pre-canned TestCodes from disasm.asm
//
PBYTE pbTest = *(PBYTE*)WinMain;
for (;; pbTest += 16) {
DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = (DETOUR_IA64_BUNDLE_DISASSEMBLE *)pbTest;
pb->Dis();
if (pbTest[0] == 0xff) {
break;
}
DumpHi(pbTest, 16, 16);
}
#if 0
printf("\n\n");
DETOUR_IA64_BUNDLE_DISASSEMBLE *pb = (DETOUR_IA64_BUNDLE_DISASSEMBLE *)pbTest;
DETOUR_IA64_BUNDLE_DISASSEMBLE *pbBeg = pb;
DWORD dwOld;
VirtualProtect(pb, 0x2000, PAGE_EXECUTE_READWRITE, &dwOld);
printf("%p: (%d)\n", pb, sizeof(pb));
pb++;
printf("%p: (%d)\n", pb, sizeof(pb));
pb++; pb->SetBrx(0);
pb++; pb->SetBrx(0);
pb++; pb->SetBrx(0);
pb++; pb->SetBrx(0xffffffffffffffff);
pb++; pb->SetBrx(0x0fffffffffffffff);
pb++; pb->SetBrx(0x00ffffffffffffff);
pb++; pb->SetBrx(0x000fffffffffffff);
pb++; pb->SetBrx(0x0000ffffffffffff);
pb++; pb->SetBrx(0x00000fffffffffff);
pb++; pb->SetBrx(0x000000ffffffffff);
pb++; pb->SetBrx(0x0000000fffffffff);
pb++; pb->SetBrx(0x00000000ffffffff);
pb++; pb->SetBrx(0x000000000fffffff);
pb++; pb->SetBrx(0x0000000000ffffff);
pb++; pb->SetBrx(0x00000000000fffff);
pb++; pb->SetBrx(0x000000000000ffff);
pb++; pb->SetBrx(0x0000000000000fff);
pb++; pb->SetBrx(0x00000000000000ff);
pb++; pb->SetBrx(0x000000000000000f);
pb++; pb->SetBrx(0x0000000000000000);
pb++; pb->SetBrx(0xffffffffffffffff);
pb++; pb->SetBrx(0xffffffffffffffff);
pb->SetInst0(0xff);
pb->SetData0(0xffffffffffffffff);
printf("%p:\n", pb);
DETOUR_IA64_BUNDLE_DISASSEMBLE *pbEnd = pb;
for (pb = pbBeg; pb < pbEnd; pb++) {
printf(" %p: ", pb);
DumpHi((BYTE*)pb, 16, 16);
}
#endif
#if 1
{
// Then we check all of the code we can find in user32.dll
//
printf("\n");
HINSTANCE hInst = LoadLibraryA("user32.dll");
printf("Loaded: user32.dll: %p\n", hInst);
PBYTE pbEntry = (PBYTE)DetourGetEntryPoint(hInst);
printf("Entry: %p\n", pbEntry);
ExportCallbackIA64(NULL, 0, "[Entry]", pbEntry);
DetourEnumerateExports(hInst, NULL, ExportCallbackIA64);
}
{
// Then we check all of the code we can find in opengl32.dll
//
printf("\n");
HINSTANCE hInst = LoadLibraryA("opengl32.dll");
printf("Loaded: opengl32.dll: %p\n", hInst);
PBYTE pbEntry = (PBYTE)DetourGetEntryPoint(hInst);
printf("Entry: %p\n", pbEntry);
ExportCallbackIA64(NULL, 0, "[Entry]", pbEntry);
DetourEnumerateExports(hInst, NULL, ExportCallbackIA64);
}
printf("\n");
for (HINSTANCE hInst = NULL; (hInst = DetourEnumerateModules(hInst)) != NULL;) {
CHAR szModuleName[512];
GetModuleFileNameA(hInst, szModuleName,
sizeof(szModuleName)/sizeof(szModuleName[0]));
printf("%p : %s\n", hInst, szModuleName);
DetourEnumerateExports(hInst, NULL, ExportCallbackIA64);
}
printf("\n");
#endif
#if 0
TestBoth();
#endif
#endif // DETOURS_IA64
#if defined(DETOURS_X64) || defined(DETOURS_X86)
// First we check the pre-canned TestCodes from disasm.asm
//
PBYTE pbBegin = (PBYTE)DetourCodeFromPointer(TestCodes, NULL);
printf("%p:\n", pbBegin);
for (PBYTE pbTest = pbBegin;;) {
if (pbTest[0] != 0xcc) { // int 3
printf("%08lx ", (ULONG)(pbTest - pbBegin));
DumpMemoryFragment(pbTest, 8, 8);
printf("\n");
printf("failed on last.\n");
return 1;
}
pbTest++;
if (pbTest[0] == 0x70 || pbTest[0] == 0x71) {
printf("[%p]:\n", pbTest);
}
BYTE rbDst[128];
PVOID pbDstPool = (PVOID)(rbDst + sizeof(rbDst));
LONG lExtra = 0;
PVOID pbTarget = NULL;
PBYTE pbNext = (PBYTE)DetourCopyInstruction(rbDst, &pbDstPool, pbTest,
&pbTarget, &lExtra);
LONG cbTest = (LONG)(pbNext - pbTest);
printf("%08lx ", (ULONG)(pbTest - pbBegin));
DumpMemoryFragment(pbTest, cbTest, 12);
printf("[%16p] ", pbTarget);
DumpMemoryFragment(rbDst, cbTest + lExtra, 11);
printf("\n");
if (pbTest[cbTest] != 0xcc) {
printf("failed!\n");
return 1;
}
pbTest += cbTest;
if (pbTest[0] == 0xcc && pbTest[1] == 0xcc) {
break;
}
}
#if 0
// Then we check all of the code we can find in user32.dll
//
HINSTANCE hInst = LoadLibraryA("user32.dll");
printf("Loaded: user32.dll: %p\n", hInst);
s_pbBegin = (PBYTE)hInst;
s_pbLimit = s_pbBegin + DetourGetModuleSize(hInst);
PBYTE pbEntry = DetourGetEntryPoint(hInst);
(VOID) new BasicBlockLink(pbEntry, "user32.dll");
DetourEnumerateExports(hInst, NULL, ExportCallback);
ULONG nIns = 0;
for (BasicBlockLink *pLink = BasicBlockLink::GetListHead();
pLink; pLink = pLink->Next()) {
nIns += TestDetourCopyInstruction(pLink->m_pbEntry, pLink->m_pszName);
if (nIns > 100000) {
break;
}
}
printf("Disassembled %d instructions.\n", nIns);
#endif
#endif // DETOURS_X86 || DETOURS_X64
#ifdef DETOURS_ARM
// Create an output buffer and fill it with debugbreaks.
//
PBYTE pbBuffer
= (PBYTE)VirtualAlloc(NULL, 0x400, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
for (PBYTE pbOut = pbBuffer; pbOut < pbBuffer + 0x400;) {
*pbOut++ = 0xfe;
*pbOut++ = 0xde;
}
PBYTE pbDst = pbBuffer;
PVOID pvDstPool = (PVOID)(pbBuffer + 0x400);
// First we check the pre-canned TestCodes from disasm.asm
//
PBYTE pbBegin = (PBYTE)DetourCodeFromPointer(TestCodes, NULL);
printf("%p: (TestCodes %p) => %p\n", pbBegin, TestCodes, pbBuffer);
for (PBYTE pbSrc = pbBegin;;) {
if (pbSrc[0] != 0xfe && pbSrc[1] != 0xde) { // BREAK
printf("%08x ", pbSrc - pbBegin);
DumpMemoryFragment(pbSrc, 8, 8);
printf("\n");
printf("failed on last.\n");
return 1;
}
pbSrc += 2;
*pbDst++ = 0xfe;
*pbDst++ = 0xde;
if ((pbSrc[0] == 0x00 && pbSrc[1] == 0xbf) && // NOP
(pbSrc[2] != 0xfe && pbSrc[3] != 0xde)) { // BREAK
// Skip over a single NOP so we can test alignment.
pbSrc += 2;
}
if ((pbSrc[0] == 0x00 && pbSrc[1] == 0xbf) && // NOP
(pbSrc[2] != 0xfe && pbSrc[3] != 0xde)) { // BREAK
// If there is a second NOP, then we insert alignment.
pbSrc += 2;
*pbDst++ = 0x00;
*pbDst++ = 0xbf;
}
LONG lExtra = 0;
PVOID pbTarget = NULL;
PBYTE pbNext = (PBYTE)DetourCopyInstruction(pbDst, &pvDstPool, pbSrc, &pbTarget, &lExtra);
LONG cbTest = (LONG)(pbNext - pbSrc);
printf("%08x ", pbSrc - pbBegin);
DumpMemoryFragment(pbSrc, cbTest, 4);
printf("[%8p] ", pbTarget);
DumpMemoryFragment(pbDst, cbTest + lExtra, 16);
printf("\n");
if (pbSrc[cbTest] != 0xfe || pbSrc[cbTest+1] != 0xde) {
printf("%p: failed! (pbSrc[n]=%02x, pbSrc[n+1]=%02x\n",
pbSrc,
pbSrc[cbTest], pbSrc[cbTest+1]);
__debugbreak();
pbNext = (PBYTE)DetourCopyInstruction(pbDst, &pvDstPool, pbSrc, &pbTarget, &lExtra);
cbTest = (LONG)(pbNext - pbSrc);
return 1;
}
pbDst += cbTest + lExtra;
pbSrc += cbTest;
if (pbSrc[0] == 0xfe && pbSrc[1] == 0xde &&
pbSrc[2] == 0xfe && pbSrc[3] == 0xde) {
break;
}
}
#if 0
// Then we check all of the code we can find in user32.dll
//
HINSTANCE hInst = LoadLibraryA("user32.dll");
printf("Loaded: user32.dll: %p\n", hInst);
s_pbBegin = (PBYTE)hInst;
s_pbLimit = s_pbBegin + DetourGetModuleSize(hInst);
PBYTE pbEntry = DetourGetEntryPoint(hInst);
(VOID) new BasicBlockLink(pbEntry, "user32.dll");
DetourEnumerateExports(hInst, NULL, ExportCallback);
ULONG nIns = 0;
for (BasicBlockLink *pLink = BasicBlockLink::GetListHead();
pLink; pLink = pLink->Next()) {
nIns += TestDetourCopyInstruction(pLink->m_pbEntry, pLink->m_pszName);
if (nIns > 100000) {
break;
}
}
printf("Disassembled %d instructions.\n", nIns);
#endif
#endif // DETOURS_ARM
return 0;
}
//
///////////////////////////////////////////////////////////////// End of File.