ProjectHax

ProjectHax General Programming

Closed Thread
 
Thread Tools Display Modes
  #11  
Old 05-02-2011
WeeMan WeeMan is offline
Administrator
 
Join Date: Apr 2011
Location: United States
Posts: 12,358
Default

Quote:
Originally Posted by atom0s
IsDebuggerPresent

What is it?
IsDebuggerPresent is a Win32 API found on Windows machines. This is one of the most common methods of preventing debugging, as well as the most bypassed method probably. This is a simple boolean returned function that checks the PEB stack of the process to determine if the process is currently being debugged or not. This function will only work on user-mode debuggers as kernel mode debuggers do not touch the PEB when attaching.


Syntax?
The syntax for IsDebuggerPresent is as follows:
Code:
BOOL WINAPI IsDebuggerPresent(void);

How Is It Used?
Well, there are many different things a developer can do when making their application use this function. Typically, the program will just close if this API returns true. But, being more creative, a developer can disable options in the program, cause wrong info to be displayed or something on that line, as well as many other things to avoid the debugger from obtaining useful info from the program. But in most cases the program will exit.


Bypassing?
As I said above there are many ways to bypass IsDebuggerPresent. One of the more commonly seen methods in programs such as olly is to simply hook the function and always return false. You can also rewrite the programs code thats calling this, such as completely remove the call, jump over it, or change the compare to always get the return you like. A simple C++ application to demonstrate with:

Code:
 #define _WIN32_WINNT 0x0500
 #include <windows.h>
 #include <conio.h>
  
 int main()
 {
     if( IsDebuggerPresent() == TRUE )
         return 1;
  
     _getch();
     return 0;
 }
In OllyDbg we will find this here:

Code:
00401000  /$  FF15 00204000 CALL DWORD PTR  DS:[<&KERNEL32.IsDebugger>; [IsDebuggerPresent
 00401006      83F8 01       CMP EAX,1
 00401009  |.  75 01         JNZ SHORT asdf.0040100C
 0040100B  |.  C3            RET
 0040100C  |>  FF15 9C204000 CALL DWORD PTR  DS:[<&MSVCR80._getch>]    ; [_getch
 00401012  |.  33C0          XOR EAX,EAX
 00401014  \.  C3            RET
As you can see, the call is made and then compared to 1. If you set a breakpoint on the compare then run the program with no plugins hiding Olly from this API, EAX should contain 1, meaning the API returning true saying there is a debugger found. So if we find a debugger, eax is 1. Then we have:
CMP 1, 1

Which will return 0 meaning they are the same. (Most if not all functions that are comparing something will return 0 meaning successful or correct.) If EAX were to be 0, we'd be comparing 0 to 1 and CMP would return 1 instead meaning they are not the same and there was a difference.

Then the next line says:
JNZ SHORT asdf.0040100C

Which means, jump if not zero (the compare return value) to the given address 0040100C. So in theory you could just change the compare to compare to 0 instead to make it work while debugging, but if you do that the program wont run correctly if you are running it without a debugger. So a quick method of fixing this one is removing the compare and changing the jump to force it to bypass the ret so we would have this instead:

Code:
CALL DWORD PTR DS:[<&KERNEL32.IsDebuggerPresent>]
 NOP
 NOP
 NOP
 JMP SHORT asdf.0040100C
 RET
 CALL DWORD PTR DS:[<&MSVCR80._getch>]
 XOR EAX, EAX
 RET
And then run the program after modifying it directly to the exe and it should run while being debugged.




Another method we could do is using OllyScript, a plugin for OllyDbg. The OllyScript plugin has a function already built into it to let you do this, so create a new file name it HideMe.txt and paste this:

Code:
;
 ; Hide From IsDebuggerPresent
 ; Resets PEB.IsBeingDebugged Flag
 ;
  
     dbh
Load the program, run the script and then run the progra All is well.
...
__________________
New forum coming soon!
https://forum.projecthax.com/t/welcome/37
  #12  
Old 05-02-2011
WeeMan WeeMan is offline
Administrator
 
Join Date: Apr 2011
Location: United States
Posts: 12,358
Default

Quote:
Originally Posted by atom0s
IsDebuggerPresent -- Another Bypass

Forgot that I was working on this post lol.. anyway, another bypass is to overwrite the _PEB.BeingDebugged Flag to bypsas IsDebuggerPresent. This is all IsDebuggerPresent uses when checking if the process is being debugged. To explain this a little more:

Code:
#define _WIN32_WINNT 0x0400
 #include <windows.h>
 int main()
 {
     return IsDebuggerPresent();
 }
A quick bare minimum example project to call IsDebuggerPresent. Lets compile and look at the code in Olly.

Code:
00401000  - FF25 00204000   JMP DWORD PTR  DS:[<&KERNEL32.IsDebuggerP>; kernel32.IsDebuggerPresent
 00401006    3B0D 00304000   CMP ECX,DWORD PTR DS:[403000]
 0040100C    75 02           JNZ SHORT asdf.00401010
 0040100E    F3:             PREFIX REP:
 0040100F    C3              RET
The first call is a jmp to the IsDebuggerPresent API, lets follow it and see the code for this API.

Code:
7C813093 >  64:A1 18000000  MOV EAX,DWORD PTR FS:[18]
 7C813099    8B40 30         MOV EAX,DWORD PTR DS:[EAX+30]
 7C81309C    0FB640 02       MOVZX EAX,BYTE PTR DS:[EAX+2]
 7C8130A0    C3              RET
The first part of this instruction is:
Code:
MOV EAX,DWORD PTR FS:[18]
This obtains the _TEB pointer for the current process that is calling the function and moves it into EAX. TEB stands for Thread Environment Block. This block holds the current info of the main thread in the current process. You can obtain the TEB block information using a kernel command line debugger, or check out Intels website and it shows this as an example:

Code:
kd> !teb
 TEB at 7FFDE000
     ExceptionList:    12ffb0
     Stack Base:       130000
     Stack Limit:      12d000
     SubSystemTib:     0
     FiberData:        1e00
     ArbitraryUser:    0
     Self:             7ffde000
     EnvironmentPtr:   0
     ClientId:         490.458
     Real ClientId:    490.458
     RpcHandle:        0
     Tls Storage:      0
     PEB Address:      7ffdf000
     LastErrorValue:   0
     LastStatusValue:  0
     Count Owned Locks:0
     HardErrorsMode:   0
As you can see the PEB pointer is located in this block which we need. The PEB, or Process Environment Block, holds the information of the current process as a whole. Now, the PEB usually NEVER changes for any process, this is usually hard coded for the given OS, but you can obtain it easily using this method yourself as well. Anyway, the next instruction line grabs the PEB pointer and moves it into EAX:

Code:
7C813099    8B40 30         MOV EAX,DWORD PTR DS:[EAX+30]
EAX already had contained the TEB block pointer, which then +0x30 is the pointer to the PEB. So EAX now contains the PEB pointer.

The next line:
Code:
7C81309C    0FB640 02       MOVZX EAX,BYTE PTR DS:[EAX+2]
Gets the BeingDebugged flag from the PEB block and moves it into EAX. PEB+0x02 is the offset in the PEB block that holds the flag if it is being debugged or not. MOVZX moves the higher byte of the 16bit register into the 32bit register. Meaning the high byte of BYTE PTR DS:[eax+2] is moved into EAX.

Then EAX is returned via RET to the calling code. So we have learned that IsDebuggerPresent simply reads from the PEB block of the process which can be overwritten by us in usermode

There are a few different methods you can use to overwrite this. Easily enough, you can reverse the above ASM and write back to the flag, heres an example hook that creates a thread to constantly write the flag to ensure that it doesn't reset.

Code:
#pragma comment( linker, "/ENTRY:DllMain" )
 #include <windows.h>
  
 BOOL bWantsExit = FALSE;
  
 DWORD SetDebugFlag()
 {
     while( !bWantsExit )
     {
         _asm
         {
         //////////////////////////////////////////////////////////////
         //
         // IsDebuggerPresent Resetter
         //
         //////////////////////////////////////////////////////////////
             mov eax, dword ptr fs:[0x18]        // Obtain _TEB Pointer
             mov eax, dword ptr ds:[eax+0x30]    // Obtain _PEB Pointer
             mov byte ptr ds:[eax+0x2], 0x0        // Overwrite BeingDebugged  Flag
         }
         Sleep( 10 );
     }
     return 0;
 }
  
 BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved  )
 {
     switch( dwReason )
     {
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls( hModule );
         CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)SetDebugFlag, 0,  NULL, NULL );
         return TRUE;
     case DLL_PROCESS_DETACH:
         bWantsExit = TRUE;
         return TRUE;
     }
     return FALSE;
 }
...
__________________
New forum coming soon!
https://forum.projecthax.com/t/welcome/37
  #13  
Old 05-02-2011
WeeMan WeeMan is offline
Administrator
 
Join Date: Apr 2011
Location: United States
Posts: 12,358
Default

Quote:
Originally Posted by atom0s
CheckRemoteDebuggerPresent

What is it?
CheckRemoteDebuggerPresent is a Win32 API found on Windows machines. This API requires you to be on a machine that is either Windows XP Home or above. This API will not work on machines lower then this. Just like IsDebuggerPresent, this is another commonly used method to check if a process is being debugged. However, unlike IsDebuggerPresent, CheckRemoteDebuggerPresent determines if a process is being debugged based on the debug port of the process.

A debug port is just like a port for the internet. It allows a debugger to connect to it and be attached while debugging. When a debug port is filled, no other debugger can connect. However, a application can have more then one debug port setup. This allows for remote debugging and such. You can have a debugger attach to a computer on a specified port and it will connect to the given application with that port open at the current moment.

Also, this uses an offset in the _EPROCESS struct of the current process. This memory space is handled by the kernel therefore cannot be simply overwritten like IsDebuggerPresent's flag was. I personally don't know how to overwrite the debugport, I know it is possible in usermode, as two things I have can do it. I cannot find any example though myself


Syntax?
The syntax for CheckRemoteDebuggerPresent is as follows:
Code:
BOOL WINAPI CheckRemoteDebuggerPresent(
   __in     HANDLE hProcess,
   __inout  PBOOL pbDebuggerPresent
 );

How to use it?
A developer would make a call on their current process to determine if a remote debugger has connected to the application to debug it. A debug port does not have to be established for a debugger to attach to the process. When a debugger attaches to a process, the debug port is overwritten with -1 meaning it is being debugged. Along with this the BeingDebugged flag is set in the _PEB struct. The first parameter to this API is a handle to the process you wish to determine if it is being debugged or not. Typically you will either see -1 or GetCurrentProcess() here.

The second parameter is a pointer to a BOOL variable. This variable is overwritten by the API with a TRUE/FALSE value if the process is being debugged or not. The return value of the API is also TRUE if succeeded and FALSE if it fails.

A quick example app calling this API to determine if it is being debugged or not would be:

Code:
#define _WIN32_WINNT 0x0501
 #include <windows.h>
 #include <iostream>
 int main()
 {
     BOOL bDebugged = FALSE;
     for( ; ; )
     {
         CheckRemoteDebuggerPresent( GetCurrentProcess(), &bDebugged  );
         if( bDebugged )
             std::cout << "Being debugged!" << std::endl;
         else
             std::cout << "Not being debugged!" << std::endl;
         Sleep( 1000 );
     }
     return 0;
 }

A little more detail.
Unlike IsDebuggerPresent, this API does not directly call things from the data segments of the process. Instead it makes a call to an Nt function. The function called is NtQueryInformationProcess. This API tells the system to obtain a piece of info about the process from the kernel. To see how this works lets take a look at the full code:

Code:
7C859B1E >  8BFF            MOV EDI,EDI
 7C859B20    55              PUSH EBP
 7C859B21    8BEC            MOV EBP,ESP
 7C859B23    837D 08 00      CMP DWORD PTR SS:[EBP+8],0
 7C859B27    56              PUSH ESI
 7C859B28    74 35           JE SHORT kernel32.7C859B5F
 7C859B2A    8B75 0C         MOV ESI,DWORD PTR SS:[EBP+C]
 7C859B2D    85F6            TEST ESI,ESI
 7C859B2F    74 2E           JE SHORT kernel32.7C859B5F
 7C859B31    6A 00           PUSH 0
 7C859B33    6A 04           PUSH 4
 7C859B35    8D45 08         LEA EAX,DWORD PTR SS:[EBP+8]
 7C859B38    50              PUSH EAX
 7C859B39    6A 07           PUSH 7
 7C859B3B    FF75 08         PUSH DWORD PTR SS:[EBP+8]
 7C859B3E    FF15 AC10807C   CALL DWORD PTR  DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
 7C859B44    85C0            TEST EAX,EAX
 7C859B46    7D 08           JGE SHORT kernel32.7C859B50
 7C859B48    50              PUSH EAX
 7C859B49    E8 1DF8FAFF     CALL kernel32.7C80936B
 7C859B4E    EB 16           JMP SHORT kernel32.7C859B66
 7C859B50    33C0            XOR EAX,EAX
 7C859B52    3945 08         CMP DWORD PTR SS:[EBP+8],EAX
 7C859B55    0F95C0          SETNE AL
 7C859B58    8906            MOV DWORD PTR DS:[ESI],EAX
 7C859B5A    33C0            XOR EAX,EAX
 7C859B5C    40              INC EAX
 7C859B5D    EB 09           JMP SHORT kernel32.7C859B68
 7C859B5F    6A 57           PUSH 57
 7C859B61    E8 4AF7FAFF     CALL kernel32.7C8092B0
 7C859B66    33C0            XOR EAX,EAX
 7C859B68    5E              POP ESI
 7C859B69    5D              POP EBP
 7C859B6A    C2 0800         RET 8
Ok so this might look a bit confusing but its not that bad. Our call to NtQueryInformationProcess is:

Code:
7C859B31    6A 00           PUSH 0
 7C859B33    6A 04           PUSH 4
 7C859B35    8D45 08         LEA EAX,DWORD PTR SS:[EBP+8]
 7C859B38    50              PUSH EAX
 7C859B39    6A 07           PUSH 7
 7C859B3B    FF75 08         PUSH DWORD PTR SS:[EBP+8]
 7C859B3E    FF15 AC10807C   CALL DWORD PTR  DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
This function, ZwQueryInformationProcess is as follows:
Code:
NTSTATUS WINAPI ZwQueryInformationProcess(
   __in       HANDLE ProcessHandle,
   __in       PROCESSINFOCLASS ProcessInformationClass,
   __out      PVOID ProcessInformation,
   __in       ULONG ProcessInformationLength,
   __out_opt  PULONG ReturnLength
 );
The push 7 is the ProcessInformationClass which is ProcessDebugPort, we are requesting the debugport value from this call. The debugport is a handle, which converts to 4 bytes, hence the push 4 as well. The last push is the return length which is NULL since it doesn't really matter if we have that or not. Mostly used for error checking.

After the call, we check if the function was successful by testing eax, the return value. This will only not jump if the value is less then 0. NT_SUCCESS, or the successful return of an NT function is Value >= 0. So anything 0 and above is good. (In theory that is, it can still fail even if it works.)

So if the function worked, we jump and goto:
Code:
7C859B50    33C0            XOR EAX,EAX
 7C859B52    3945 08         CMP DWORD PTR SS:[EBP+8],EAX
 7C859B55    0F95C0          SETNE AL
 7C859B58    8906            MOV DWORD PTR DS:[ESI],EAX
 7C859B5A    33C0            XOR EAX,EAX
 7C859B5C    40              INC EAX
Here we xor eax out against itself. This makes EAX 0 then is compared to our return buffer value. If the debug port is set, this value is -1 or FFFFFFFF in hex. So if the port is set we would be doing:

cmp FFFFFFFF, 0

The next line says, set negitive if true. Which sets the low word of EAX to 1 making eax equal 00000001

After this we store EAX back into the buffer for pBool if it is being debugged or not, xor out EAX, pop our stored registers and return.


Bypassing Method :: Hooking
At the moment this is the only fessible method that I know of as I don't know how to write to kernel memory in usermode. There are ways, but I have yet to figure it out myself and there is very little documentation for the Nt functions and stuff like this.

So we will hook the API instead In my case, I am using Detours since well.. yea it works and its easy.

You will need Detours 1.5 for this since the new versions have removed some used functions.

We will create a DLL with the following code:


Code:
#define _WIN32_WINNT 0x0501
 #include <windows.h>
 #include "Detours/detours.h"
 #pragma comment(lib, "Detours/detours.lib")
  
 DETOUR_TRAMPOLINE( BOOL WINAPI Real_CheckRemoteDebuggerPresent(HANDLE,PBOOL),  CheckRemoteDebuggerPresent );
 BOOL Mine_CheckRemoteDebuggerPresent(HANDLE hHandle, PBOOL pBool)
 {
     _asm mov dword ptr ss:[pBool], 0x0
     return TRUE;
 }
  
 DWORD DllSetup()
 {
     DetourFunctionWithTrampoline( (PBYTE)Real_CheckRemoteDebuggerPresent,  (PBYTE)Mine_CheckRemoteDebuggerPresent );
     return 0;
 }
  
 BOOL APIENTRY DllMain( HMODULE hModule, DWORD dwReason, LPVOID lpReserved  )
 {
     switch( dwReason )
     {
     case DLL_PROCESS_ATTACH:
         DisableThreadLibraryCalls( hModule );
         CreateThread( NULL, NULL, (LPTHREAD_START_ROUTINE)DllSetup, NULL,  NULL, NULL );
         return TRUE;
     case DLL_PROCESS_DETACH:
         DetourRemove( (PBYTE)Real_CheckRemoteDebuggerPresent,  (PBYTE)Mine_CheckRemoteDebuggerPresent );
         return TRUE;
     }
     return FALSE;
 }
To explain the code some, we first create a thread to handle the hooking. Why? Well if you inject into a process chances are you are going to need to set it to SUSPEND mode to pause it while injecting. If you do not use a new thread, your injection will fail as the DLL will attempt to be handled in the processes main thread. This will just not work at all since the thread is suspended, nothing will happen.

Instead, you create a new thread and handle the hooking inside that.

Inside our thread, we simply tell Detours to create a callback to CheckRemoteDebuggerPresent and have it trampoline from my function to the original. Meaning we have access to both the hooked and normal functions.

Now inside our callback function which is called EVERYTIME CheckRemoteDebuggerPresent is called, we are going to completely ignore the original function. We do not need to call it at all. Instead, we want to simply overwrite the PBOOL param and then return TRUE.

So we use: _asm mov dword ptr ss:[pBool], 0x0

This says move 0 into the pointer of pBool which sets pBool to FALSE. Then the function returns TRUE as if it was really called.
...
__________________
New forum coming soon!
https://forum.projecthax.com/t/welcome/37
  #14  
Old 05-02-2011
WeeMan WeeMan is offline
Administrator
 
Join Date: Apr 2011
Location: United States
Posts: 12,358
Default

Quote:
Originally Posted by atom0s
Well, Ksbunker brought this one up to my attention over on GameDeception, so I researched it a bit and yea, Ollydbg by default sets ESI to -1 (FFFFFFFF) when it loads at the entry point. With that you can implement a simple check to detect Olly that way, for example:

Code:
#pragma comment( linker, "/ENTRY:main" )
 #include <windows.h>
  
 char *szString = "Olly Found!";
  
 int main()
 {
     _asm
     {
         cmp esi, -1
         je OllyFound
         jmp NotFound
 OllyFound:
         push 0
         push szString
         push szString
         push 0
         call dword ptr [MessageBoxA]
 NotFound:
         // Just return after..
     }
     return 0;
 }
Other then being easily patched, you can also setup a plugin method to bypass this with Olly itself. I coded a plugin, for learning experiences on how to code Olly plugins just now to accomplish this. Although I highly doubt this is the best method possible of doing this, here ya go:

Code:
#include <windows.h>
 #include "plugin.h"
  
 HINSTANCE    m_hInstance;
 HINSTANCE    m_hDllInstance;
 HWND        m_hWnd;
 BOOL        bInitializePause;
  
 //
 // Main Entry Point
 //
 BOOL WINAPI DllEntryPoint( HINSTANCE hInstance, DWORD dwReason, LPVOID  lpReserved )
 {
     if( dwReason == DLL_PROCESS_ATTACH )
         m_hInstance = hInstance;
     return 1;
 }
  
 //
 // Plugin Name / Data
 //
 extc int _export cdecl ODBG_Plugindata(char shortname[32])
  
 {
     strcpy(shortname,"EsiCheck");
     return PLUGIN_VERSION;
 };
  
 //
 // Initialization
 //
 extc int _export cdecl ODBG_Plugininit(int ollydbgversion,HWND hw,ulong  *features)
 {
     if( ollydbgversion < PLUGIN_VERSION )
         return -1;
  
     m_hWnd = hw;
  
     Addtolist( 0,0,"EsiCheck Plugin v1.0" );
     Addtolist( 0,1,"Coded by: Wiccaan" );
     return 0;
 }
  
 //
 // Menu Setup
 //
 extc int _export cdecl ODBG_Pluginmenu(int origin,char data[4096],void  *item)
 {
     switch( origin )
     {
     case PM_MAIN:
         strcpy( data, "0 &About" );
         return 1;
     default:
         break;
     }
     return 0;
 }
  
 //
 // Main Plugin Callback
 //
 extc void _export cdecl ODBG_Pluginaction(int origin,int action,void  *item)
 {
     if( origin == PM_MAIN )
     {
         switch( action )
         {
         case 0:
             MessageBox( m_hWnd, "Ollydbg EsiCheck v1.0\nCoded by: Wiccaan",  "EsiCheck", MB_OK|MB_ICONINFORMATION );
             break;
         default:
             break;
         }
     }
  
 }
  
 //
 // Close Plugin
 //
 extc int _export cdecl ODBG_Pluginclose(void)
 {
     return 0;
 }
  
 //
 // Plugin Reset Reset Initialize Pause Flag
 //
 extc void _export cdecl ODBG_Pluginreset(void)
 {
     bInitializePause = false;
 }
  
 //
 // Olly Pause If First Pause Reset ESI
 //
 extc int _export cdecl ODBG_Paused(int reason,t_reg *reg)
 {
     if( !bInitializePause )
     {
         reg->r[ 6 ] = 0;
         bInitializePause = true;
     }
     return 0;
 }
What this does is when Olly pauses, it checks if the initialize boolean 'bInitializePause' has been set yet. If not, then this is the first time Olly is paused, and usually means the OEP break that Olly does by default. With that, it will reset the register ESI to 0 and then set the boolean to true meaning we have called this once already and do not need it anymore.

When the program is reloaded through Olly, the plugin will be called to reset the flag and do it again.

Enjoy.
...
__________________
New forum coming soon!
https://forum.projecthax.com/t/welcome/37
  #15  
Old 05-02-2011
WeeMan WeeMan is offline
Administrator
 
Join Date: Apr 2011
Location: United States
Posts: 12,358
Default

Quote:
Originally Posted by atom0s
Made this for a project I was working on at work. Just a semi-quick toss together as I am learning the language. Probably some better methods of doing things in this, but, like I said, I'm still learning C# :P so I don't know the best of the best for each thing yet.

Currently this is limited to doing basic command handling meaning you cannot have command names with spaces, or, command arguments with spaces in them. The class splits the raw command based on the number of spaces entered in the full command and uses that as the argument count to determine if the minimum amount of arguments is met.

The command delegate to handle the command if found is also static to void and does uses 1 param (string) as the argument list. Meaning each of your commands must parse the arguments and handle them accordingly yourself.

The class strips the slash and command name for you and only passes the arguments as a single full string.

Code:
    /// <summary>
     /// CommandHandler - A basic command handler example by Wiccaan.
     /// Copyright 2008 (c) - Wiccaan ([email protected])
     /// 
     /// This example demonstrates how to dynamically create a command  handler
     /// that can handle stored commands based on user input. This class can  be
     /// useful for applications that allow user input to use commands.
     /// 
     /// This class is limited in the sense that it does not know how to  parse
     /// for arguments other then looking for the number of spaces in the  line
     /// given so command names and single arguments cannot contain  spaces.
     /// 
     /// The current delegate to handle calling the function is static to  void
     /// and simply handle the raw argument line. This can be extended to  handle
     /// different types of commands and such if you wish to edit it.
     /// </summary>
     public class CommandHandler
     {
         /// <summary>
         /// Command delegate to handle calling the function set to the  specific command.
         /// </summary>
         /// <param name="strArguments"></param>
         public delegate void HandleCommand(string strArguments);
  
         /// <summary>
         /// Command structur entry that holds the commands information.
         /// </summary>
         private struct _CommandEntry
         {
             public string _CmdName;     // Command Name
             public string _CmdDesc;     // Command Description
             public int _CmdArgCount;    // Number Or Arguments For The  Command
             public HandleCommand _Cmd;  // The Function To Call For This  Command
         }
  
         /// <summary>
         /// Command hashtable that holds each command entry.
         /// </summary>
         private Hashtable _hashCommands;
  
         /// <summary>
         /// The original full command passed to the command handler.
         /// </summary>
         private string _strRawCommand;
  
         /// <summary>
         /// CommandHandler Class Construction
         /// Creates the new hashtable and holds any other preuse  initialization
         /// commands and such if needed.
         /// </summary>
         public CommandHandler()
         {
             _hashCommands = new Hashtable();
         }
  
         /// <summary>
         /// Adds a command to the command hashtable to be used when parsing  the given command.
         /// </summary>
         /// <param name="cmdname">The command name used when parsing  the given text.</param>
         /// <param name="cmddesc">The command description used to  return information on the entered commands.</param>
         /// <param name="argcount">The number of arguments (minimum)  for the command to work.</param>
         /// <param name="cmdhandler">The function to be called to  handle the command.</param>
         /// <returns>Boolean on success of adding the  command.</returns>
         public bool AddCommand(string cmdname, string cmddesc, int argcount,  HandleCommand cmdhandler)
         {
             // Determine If This Command Exists Already
             Hashtable _CmdList = (Hashtable)_hashCommands.Clone();
             foreach (DictionaryEntry _Cmd in _CmdList)
             {
                 if (((_CommandEntry)_Cmd.Value)._CmdName.ToLower() ==  cmdname.ToLower())
                 {
                     // Command Already Exists Do Not Add
                     return false;
                 }
             }
  
             // All Looks Fine Create The Command And Add It To The  Hashtable
             _CommandEntry cCommand = new _CommandEntry();
             cCommand._CmdName = cmdname;
             cCommand._CmdDesc = cmddesc;
             cCommand._CmdArgCount = argcount;
             cCommand._Cmd = cmdhandler;
  
             // The Command Name Is The Key Used To Pull The Command Entry  From The Hashtable!
             _hashCommands.Add(cmdname, cCommand);
             return true;
         }
  
         /// <summary>
         /// Attempts to parse and handle the command passed to the  CommandHandler.
         /// </summary>
         /// <param name="command">The full raw command entered by the  user.</param>
         /// <returns>Boolean on success of parsing and handling the  command.</returns>
         public bool ParseCommand(string command)
         {
             // Store Full Raw Command For Later Use
             _strRawCommand = command;
  
             try
             {
                 // Check For Valid Command (Look For / At Start Of  String)
                 if (command.Substring(0, 1) != "/")
                 {
                     // Invalid Command Passed Don't Handle
                     return false;
                 }
  
                 // Remove The Slash From The Command To Leave Raw  Arguments
                 string commandTemp = command.Remove(0, 1);
  
                 // Pull Information From Command
                 string commandName = commandTemp.Substring(0,  commandTemp.IndexOf(" "));
                 string commandArgs = commandTemp.Remove(0,  commandTemp.IndexOf(" ") + 1);
                 int iArgCount = commandArgs.Split(' ').Length;
  
                 // Locate The Command And Attempt To Handle
                 Hashtable _CmdList = (Hashtable)_hashCommands.Clone();
                 foreach (DictionaryEntry _Cmd in _CmdList)
                 {
                     if (((_CommandEntry)_Cmd.Value)._CmdName.ToLower() ==  commandName.ToLower())
                     {
                         // Command Found Check Argument Count
                         if (((_CommandEntry)_Cmd.Value)._CmdArgCount <=  iArgCount)
                         {
                             // Argument Count Was Valid Attempt To Handle  Command
                              ((_CommandEntry)_Cmd.Value)._Cmd(commandArgs);
                             return true;
                         }
                         return false;
                     }
                 }
             }
             catch (Exception ex)
             {
                 System.Diagnostics.Debug.Print("{0}", ex.ToString());
                 return false;
             }
  
             // Command Wasnt Found So We Couldn't Handle It
             return false;
         }
     }
An example of using this:

Create a basic Windows Form application:
- Add a textbox (textBox1) and set it to allow multiple lines.
- Add a textbox (textBox2), this will be the user input bar.

Our form class code will look like this:

Code:
namespace CommandTest
 {
     public partial class Form1 : Form
     {
         CommandHandler cmdHandler;
         public Form1()
         {
             InitializeComponent();
             cmdHandler = new CommandHandler();
             cmdHandler.AddCommand("echo", "Echos the user input back into the  example textbox.", 1, HandleEchoCommand);
         }
  
         private void HandleEchoCommand(string strArguments)
         {
             textBox1.Text += strArguments;
             textBox1.Text += Environment.NewLine;
         }
  
         private void textBox2_KeyDown(object sender, KeyEventArgs e)
         {
             if (e.KeyCode == Keys.Enter)
             {
                 cmdHandler.ParseCommand(textBox2.Text);
                 textBox2.Text = "";
             }
         }
     }
 }
This sets up a single command to be handled which is 'echo'. Run the program, type anything into the box and hit enter. The parser will fail to parse the command as it didn't start with /

Then, type something like:

Code:
/echo Hello there!
And it will handle the command accordingly by calling HandleEchoCommand and print out the arguments parameter which contains the message you entered.

You are free to do what ever with this, edit it, copy paste it, etc. Just give me some form of credit if you use it.

Enjoy.
...
__________________
New forum coming soon!
https://forum.projecthax.com/t/welcome/37
Closed Thread

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

Similar Threads
Thread Thread Starter Forum Replies Last Post
SREmu Backup WeeMan Programming 0 05-02-2011 04:26 AM


All times are GMT. The time now is 08:12 PM.
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2018, vBulletin Solutions Inc.

Google+ Facebook Twitter