Lightweight Detours

On lunes, 27 de diciembre de 2010 0 comentarios

Lightweight Detours

While working on my toolkit library I did some work on detours and I think I made something interesting.

I made 2 'code patching' hook classes. One is a good old detour to hook functions. The other is far more interesting, it basically allows you to 'wiretap' any function any where in its body.

A quick review:
PHP Code:
#ifndef _TOOLKIT_DETOURS_HGUARD
#define _TOOLKIT_DETOURS_HGUARD

// ----------------------------------------------------------------
// Toolkit: Detours
// ----------------------------------------------------------------
// Classes to allow easy modifying of the target code in order to hook functions.
namespace toolkit {


    
// ------------------------------------------------
    // Class: Detour
    // ------------------------------------------------
    // Detours a function using the standard [jmp xxxx] method.
    
class Detour
    
{
    public:
        
Detourvoidfuncvoidhookuint bytes );
        ~
Detour();

        
void Unhook();
        
void Rehook();

        
// Call the original function ( without unhooking )
        
templatetypename Fn >
        
inline Fn Orig() { return reinterpret_cast<Fn>( _stub ); }

    private:
        
void*    _func;
        
void*    _hook;
        
void*    _stub;
        
uint    _bytes;
        
bool    _hooked;
    };



    
    
// ----------------------------------------------------------------
    // Class: Wiretap
    // ----------------------------------------------------------------
    // Run your hook code in the middle of a function.
    // It'll continue execution after your hook returns.
    //
    // The function will get redirected to a small stub which does this:
    //
    
class Wiretap
    
{
    public:
        
// Stores the result of a pushad instruction
        
struct context32_t
        
{
            
types::dword ediesiebpespebxedxecxeax;
            
// Get the arguments (as a struct) of a function with a stack frame
            
templatetypename T inline Targs() { return reinterpret_cast<T*>( ebp 0x8 ); }
            
// Get the return address
            
inline void*& retaddr() { return *reinterpret_cast<void**>( ebp 0x4 ); }
        };
        
typedef context32_t context_t;

        
// Access the function arguments manually trough esp or ebp.
        // Modifying this context will modify the ACTUAL registers when the function continues.
        
typedef void (__stdcall *HookFn)( context_tctx );
        
typedef void (__cdecl *HookFn2)( context_tctx );

        
WiretapvoidplaceHookFn hookuint bytes );
        
WiretapvoidplaceHookFn2 hookuint bytes );
        ~
Wiretap();

        
void Unhook();
        
void Rehook();


    private:
        
void*    _place;
        
void*    _stub;
        
uint    _bytes;
        
bool    _hooked;
    };

    
};
#endif // !_TOOLKIT_DETOURS_HGUARD 

















































 
The Wiretap stub looks like this, the context32_t struct basically mimics the structure of stack after a pushad
Code:
stub:
 pushad
 push esp
 call hook
 ;add esp, 0x4 ; When constructor is called where the hook has a __cdecl calling convention
 popad
 [ Overwritten bytes go here ]
 jmp continue


Comparison of my implementation of Detour and Wiretap:

Advantages of Detour:
  • Can hook any function and prevent it from running
  • Easily read and modify the stack arguments
Disadvantages of Detour:
  • Cannot hook in the middle of a function
  • Easy to detect if a function has been hooked (just check the first few bytes for a jmp or other known codes)
  • Can be annoying to read arguments passed via registers

Advantages of Wiretap:
  • Can hook in any function anywhere in its body
  • Easily read and modify the registers
  • Harder to detect (both will fail a checksum though)
Disadvantages of Wiretap:
  • Cannot prevent the original function from running
  • Reading and modifying arguments passed via the stack is more cumbersome, especially if the function doesn't have a stack frame (using ebp)



Example usage:

PHP Code:
    // ----------------------------------------------------------------
    // Testing Detours
    // ----------------------------------------------------------------
    

    // This function will be hooked with Detour
    
__declspec(noinline)
    
int Magicint x )
    {
        return 
x;
    }
    
// This function will be hooked with Wiretap
    
__declspec(noinline)
    
int Magic2int aint bint c )
    {
        return 
5;
    }


    
int __cdecl Hooked_Magicint x )
    {
        return -
x;
    }
    
typedef int (__cdeclMagicFn)( int );

    
void Wiretap_Magic2Wiretap::context_tctx )
    {
        
struct args_t
        
{
            
int a;
            
int b;
            
int c;
        };
        
args_tpArgs ctx->args<args_t>();
        
pArgs->+= 1;
        
pArgs->+= 2;
        
pArgs->+= 3;
    }

    
bool TestDetours()
    {
#ifdef NDEBUG
        // Does not work in Debug mode because the compiler adds an indirection to function calls. (Possible to allow edit&continue).
        // The operator& doesn't get the correct pointer.
        
Detour hook( &Magic, &Hooked_Magic);
        
Wiretap hook2make_ptr<void*>( (void*)&Magic20x3 ), &Wiretap_Magic2);

        
// Call them
        
if ( Magic) != -) return false;
        if ( 
hook.Orig<MagicFn>()( ) != ) return false;

        if ( 
Magic211) != ) return false; #endif // NDEBUG

        
return true;
    } 


























I apologize for the dependencies, It's just how I've organized my toolkit library.
The memory.h is something I quickly slapped together for pooling read/write/exec memory. I also experimented with generating stub functions using templates (but dropped it in favor of VirtualAlloc).

Atm it's pretty much Windows x86 only (because that's all I need atm). Get me the opcodes for x64 and I'll make it work for x64 (although I can't test it due to running Vista x86).



http://www.ziddu.com/download/13158196/detours.zip.html
Attached Files
Read more ...»

Punkbuster "Unknown API" Kick Bypass

On 0 comentarios

Punkbuster "Unknown API" Kick Bypass

Use this as you will, it's simply a proof of conept PB still sux IMO.

I see a lot of people having issues and here's something to get you started.

Basically I was doing some Breakpointing in PB and stumbled upon this lovely buffer outside the code area of pbcl. It seems they are still only using pbcl to do certain things and the service APPEARS to scan simply d3d and the game itself.

For those of you wanting to get rid of teh PB kick "Unknown API" here's the code how.

Simply setup a global buffer:

Code:
char    *Strings;
Setup the function Hook itself:
Code:
void ( *orig_API_Scanner )( );
void __declspec( naked ) API_Scanner( )
{
    _asm
    {
        //Grab Strings From The Stack
        mov Strings, eax

        //Preserve the stack
        pushad
    }

    Log( "API String: [ %s ]", Strings );

    _asm
    {
        //Preserve the stack
        popad

        //Push the strings back to PB now we've edited
        mov eax, Strings

        //Return the original :)
        jmp [ orig_API_Scanner ]
    }
}



You will need to hook inside pbcl.dll itself, so do it via a hwbp to avoid detection. As far as i'm aware PBCL DOESN'T scan outside it's own code area but that could change anytime So don't blame me if you get busted!

Hook with my fantastic pattern i've made you below ( Yes this works with ALL pb games tested to date )

Code:
DWORD dwApiScan  = FindPattern( ( DWORD )GetModuleHandle( "pbcl.dll" ), 0xFFFFFFF, ( BYTE* )"\x6A\x00\x50\x50\x33\xF1\xFF\x55\xC8\x59\x40\x50\xFF\x75\x08", "xxxxxxxxxxxxxxx" );

orig_API_Scanner = (void (__cdecl *)(void))DetourFunction((PBYTE)dwApiScan, (PBYTE)API_Scanner );
Now go ingame and log. Make sure there are no hooks but this one, it's very important as your need to log clean untouched values!! It may take several minutes before any log appears, this is normal. Once loged your see all the api's with original / clean MD5's. save this log file. below i'll give you an example of bypassing the scan itself.

Code:
void ( *orig_API_Scanner )( );
void __declspec( naked ) API_Scanner( )
{
    _asm
    {
        //Grab Strings From The Stack
        mov Strings, eax

        //Preserve the stack
        pushad
    }

    //Bypass Direct3DCreate9 Hook
    if( strstr( Strings, "Bmd Direct3DCreate9 5" ) )
    {
        strcpy( Strings, "Bmd Direct3DCreate9 5 c9c9c9c9c9 8bff558bec81ec08010000a120c2f54f" );
    }

    _asm
    {
        //Preserve the stack
        popad

        //Push the strings back to PB now we've edited
        mov eax, Strings

        //Return the original :)
        jmp [ orig_API_Scanner ]
    }
}





Simple eh? Only check a little of the wording then give the cleanly logged string in return. Now you can hook that API and PB will pass you as clear . Each game differs in the MD5 value it checks for, so you will need to log each game seperately to bypass.

The main issue you might get is withe the size of the module in the pattern. I use a function I didn't post which returns the size of the module as they do vary. Either just change the size in the pattern of the module, or put a stupidly high search size like 0xFFFFFFFF.

Also the asm shouldn't matter as it's a naked hook with no stack code unless you do a HWWBP then you will need to add a single line to the asm. And also you could always add more to the pattern to guarentee the right one everytime.

Another thing is to also go to the offset and set some breakpoints on some other areas around it, you might find some other usefull things there
Read more ...»

Punkbuster Hardware Viewer

On 0 comentarios

Punkbuster Hardware Viewer

/*
Just a Simple Program written to load the pb driver externally (Got Debug?)
- PizzaPan / Game-Deception! (www.gamedeception.net)

Credits: Tetsuo, RunningBon, panzer, Xen, h1web, Sparten, Kosire, Google!
*/

/*

Remember These Simple Rules of Coding to Make the Source Code Work!
1.) Remember to rename any voids, and remove any traces of _asm!
2.) static const char *[1000] Couch = { (Couch = leet) }; YEAH BOI!
3.) No on one's own account releases of hacks released by eliteCoders!
4.) Use lots of 0xglDisable!

*/

For lack of a better Title, i called this Hardware Viewer, really all it does is load the driver standalone, and decrypt the result, showing your hardware hash's
i use this method to debug the driver, as its a lot easier.

It includes 2 Examples, BF2 and AAO (a v7, and a v71 Game)

MAKE SURE YOU FOLLOW DIRECTIONS

1.) CLICK LOAD DRIVER
2.) VIEW YOUR HARDWARE HASH'S
3.) CLOSE MESSAGE BOX
4.) CLICK UNLOAD DRIVER
5.) CLOSE THE PROGRAM

If you dont, expect a bsod.
Attached Files
Read more ...»

Punkbuster Debugger Detection Bypass

On 0 comentarios

Punkbuster Debugger Detection Bypass

/*
Anti-PB Plugin (Fixes ZwQueryObject Detection)

Credits:
CDetour: Tetsuo/LanceVorgin (also check CDetour.cpp)
Bits & Bats: RunningBon/panzer/Xen/Sparten/Kosire
Volcano Master: h1web
EngRish Lessons: Pansemuckl!


Remember These Simple Rules of Coding to Make the Source Code Work!
1.) Remember to rename any voids, and remove any traces of _asm!
2.) static const char *[1000] Couch = { (Couch = leet) }; YEAH BOI!
3.) No on one's own account releases of hacks released by eliteCoders!
4.) Use lots of 0xglDisable!!

*/


This is something i wrote up a while ago (Detect.zip) it is based from a unseen at the time debugger detection from Punkbuster, a lot of people at netcoders were wondering why their games crashed while debuggin, upon futher investigation by my self, it turned out to be a debugger check.

My friend Peter[Pan] later posted the info at a Reverse Engineering Board (www.exetools.com/forum) and thus we had a technical explination.

Quote Originally Posted by Opc0de
When you create/attach a program inside the debugger, the debug api will call a native function called "NtCreateDebugObject" that will create a DebugObject and set the EPROCESS->DebugPort = DebugObject.
Anyways its found in some games, i saw it in COD 1, ET, AAO and it seems to work best on WinXP Based OS'S, patching it was easy enough but this becomes boring to patch everytime manually, so i wrote a quick plugin to inject, which can easily fix using CDetour by Tetsuo & LanceVorgin.

Attached is Detect.zip (Standalone Detection) & Anti-PB.zip (DLL to be Injected to Fix the Detection)

-PizzaPan
Attached Files
Read more ...»

IDA plugin to grab encrypted debug strings.

On 0 comentarios

A plugin to grab encrypted debug strings.

Made tonight, cost me a bottle of coca'cola ^^ i hope u'll enjoy it.
Not really spent a lot of time to clean the code so :/ sorry ?
the plw is in attachment here : dcp.rar
Code:
/*
    IDA plugin for PnkBstr

    Copyright 2010 (ThiSpawn on gamedeception.com) - All Rights Reserved

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see .
*/

#include 

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 

#define MAX_FUNC_SIZE 0xFFFF

char g_func_buf[MAX_FUNC_SIZE + 1];
char g_masks[19];
bool g_fixed = 0;
bool g_errors = 0;

//----------------------------------------------
//  FUNCTION : get_crefs
//  fill vector with cref_to addresses
//----------------------------------------------
bool get_crefs(std::vector& vea, ea_t ea)
{
 xrefblk_t xb;
 func_t *last;

 if (!xb.first_to(ea, XREF_ALL))
 {
  msg("no ref\n");
  return false;
 }
 if (get_func(xb.from)){
  vea.push_back(xb.from);
  last = get_func(xb.from);
 }
 else
 {
  msg("please fix sub at ea : %08X\n", xb.from);
  g_errors = 1;
 }
 while (xb.next_to()) {
  if (get_func(xb.from) && last != get_func(xb.from))
  {
   vea.push_back(xb.from);
   last = get_func(xb.from);
  }
  else if (!get_func(xb.from))
  {
   msg("please fix sub at ea : %08X\n", xb.from);
   g_errors = 1;
  }
 }

 return true;
}

FILE* ask_out_file()
{
    char* filename;
    FILE* file;
    filename = askfile_c(1, "*.txt", "Enter the name of the out file:");
    file = qfopen(filename, "w");
    if ( !file ) {
        warning("Could not open %s for writing!\n", filename);
    } else {
        msg("Using: %s\n", filename);
    }
    return file;
}

char g_decrypted[0x100];

char* decrypt(func_t *func)
{
 func_tail_iterator_t tail_iter(func);
 asize_t size = 0;
 ea_t ea, max_ea;
 char *pcur = g_func_buf;
 bool first_done1 = false;
 bool first_done2 = false;
 uval_t buf_adr;
 uval_t msk_adr;
 char *p_center_mask = &g_masks[10];

 do
 {
  size += tail_iter.chunk().size();
 } while (tail_iter.next());
 
 if (size > MAX_FUNC_SIZE)
 {
  msg("function too long\n");
  return g_decrypted;
 }

 tail_iter.main();
 do
 {
  ea = tail_iter.chunk().startEA;
  max_ea = tail_iter.chunk().endEA;
  while (ea < max_ea)
  {
   decode_insn(ea);
   asize_t cmd_size = cmd.size;
   get_many_bytes(ea, pcur, cmd_size);
   // leave func if other things than string decryption
   if ((cmd.itype != NN_push) &&
    (cmd.itype != NN_pop) &&
    (cmd.itype != NN_leave) &&
    (cmd.itype != NN_retn) &&
    (cmd.itype != NN_mov) &&
    (cmd.itype != NN_xor))
   {
    msg("sub complex at ea : %08X\n", func->startEA);
    return g_decrypted;
   }
   //-------------------------------
   // to replace buf adresses ------
   if ((cmd.itype == NN_mov) && (cmd.Operands[0].type == o_mem))
   {
    uval_t val = cmd.Operands[0].addr;
    //msg("ea:%08X type:%d\n", ea, cmd.Operands[0].type);
    if (!first_done1)
    {
     first_done1 = true;
     buf_adr = val;
     //msg("haaaaaa : %08x\n", buf_adr);
    }
    *(DWORD*)(pcur + cmd.Operands[0].offb) = (DWORD)((uval_t)g_decrypted + val - buf_adr);
   }
   //-----------------------------
   // to replace used values ------
   if ((cmd.itype == NN_mov) && (cmd.Operands[0].type == o_reg) && (cmd.Operands[1].type == o_mem))
   {
    uval_t val = cmd.Operands[1].addr;
    //msg("ea:%08X offb:%d\n", ea, cmd.Operands[1].offb);
    if (!first_done2)
    {
     first_done2 = true;
     msk_adr = val;
     //msg("hoooooo : %08x\n", msk_adr);
    }
    *(DWORD*)(pcur + cmd.Operands[1].offb) = (DWORD)(p_center_mask + val - msk_adr);
    *(char*)(p_center_mask + val - msk_adr) = get_byte(val);
   }
   //----------------------------
   ea += cmd_size;
   pcur += cmd_size;
  }
 } while (tail_iter.next());

 //it's time to execute it
 DWORD pfaddr = (DWORD)g_func_buf ;
 ((char* (__cdecl*)())pfaddr)();
 //msg("%s\n", g_decrypted);
 if (g_fixed)
  add_long_cmt(func->startEA, 0, "%s", g_decrypted);

 return g_decrypted;
}

void decrypt_all() // modify addr of the masks base
{
 std::vector callers;
 std::vector::iterator it;
 ea_t ea;
 
 if (askbuttons_c(NULL,NULL,"format",0,"Have you selected the first byte mask for decryption ?\n If NOT please clic \"no\" then find a decrypt function\n then jump to one of the bytes used as masks.\nFinally select the first one. I coded the plugin to use refs on the five firsts.") < 1)
  return;

 FILE *out_file = ask_out_file();
 if (!out_file)
  return;

 DWORD base = get_screen_ea();

 for (int i = 0; i < 5; i++)
 {
  qfprintf(out_file, "mask %d :\n", i + 1);
  msg("mask %d :\n", i + 1);
  get_crefs(callers, base + i);
  for (it = callers.begin(); it < callers.end(); it++)
  {
   ea = *it;
   func_t *func = get_func(ea);
   qfprintf(out_file, "%08X : %s\n", func->startEA, decrypt(func));
  }
  callers.clear();
 }
}

//----------------------------------------------
//  PLUG-IN FUNCTIONS
//----------------------------------------------

int IDAP_init()
{
 if (inf.filetype != f_PE)
  return PLUGIN_SKIP;

 return PLUGIN_KEEP;
}

void IDAP_term()
{
 // nothing to clear
}

void IDAP_run(int arg)
{ 
 //func_t *func = get_func(get_screen_ea());
 //decrypt(func);
 info("comments will be added to function during analysis if\n you've done a prior analysis WITHOUT ANY MORE fix to do !!!");
 g_errors = 0;
 decrypt_all();
 if (!g_errors)
  g_fixed = 1;
}

char IDAP_comment[] = "PnkBstr hidden string DeCrypter";
char IDAP_help[] = "";
char IDAP_name[] = "PB DeCrypter";
char IDAP_hotkey[] = "Alt-W";

plugin_t PLUGIN =
{
 IDP_INTERFACE_VERSION, 
 0,
 IDAP_init,
 IDAP_term,
 IDAP_run,
 IDAP_comment,
 IDAP_help,
 IDAP_name,
 IDAP_hotkey
};

//----------------------------------------------

Read more ...»