[Guide] Hooking the Console Input and Output

On martes, 26 de abril de 2011 0 comentarios

http://www.elitepvpers.de/forum/sro-...ut-output.html

My next guide is more of a boring one, but it is necessary to show the concepts now since I will be using them in an upcoming guide (assuming I can get it done before any client updates)! There are no images to show really and I don't expect anyone to really spend a lot of time on this one now, but it will be a good reference for later on when I use the concepts.

Just remember all of the guides up to now are setting the stage for future guides (barring anything that might prevent me from doing more) so I have to show all these concepts in their own standalone form first. This guide really marks the last of the foundation guides that shows generic client specific techniques that I use.

After this guide, I will be working on a more practical guide series that I think a lot of people should find useful and like, but it might be a little controversial. There are only so many things that fit in that category, but I'll leave it to your imagination for now. :)

Hooking the Console Input and Output

I. Purpose

This guide will show an implementation of a console hook for input and output in Silkroad. This logic is based on some of the work I had done for Silkmod way back in the day. Silkmod was where chat block originated from before it was moved into Softmod when I stopped updating it.

This guide is an important development tool to understand because there will be times we want to be able to interact with the client without having to go through another GUI or external window. If we can use what we have in front of us, we can develop and test our code more rapidly and safely as we do not have to worry about thread safety issues.

The techniques for actually finding this stuff from scratch in the client are not going to be covered in depth. There’s no real way to teach that stuff so this guide is more of a code and explanation guide. Once you understand what is going on though, the information remains relevant for other tasks and you should be able to apply it in trying to find similar locations in other games.

II. Requirements

This guide is similar to the previous guides of extracting packets and integrating AntTweakBar. There is a mix of assembly logic with C++ programming. There are not really any new concepts presented though, we just use our codecaves again to steal data and make changes to logic!

In order to be able to follow along this guide, you will need:
• OllyDbg 1.10 (or equivalent)
• Visual Studio 2008 (or equivalent)

Before you continue this guide, you will also need to have already read and understood the previous guides! All of the guides build on one another in some shape or form, so it’s most beneficial to have read them all by now. Don’t forget to consult the assembly reference listed in the Loader guide if you need to look something up.

III. Theory

There is not much specialized theory in hooking the console input and output mechanisms in the Silkroad client. It’s really just a matter of finding the locations, saving the buffers, and using them as you need it. The way I went about tracking down this logic was by taking advantage of the swear filter in the game. If you type in a ‘bad word”, you will notice your text is not sent and you get a red error message in the console window.

If you find that error message in the PK2 files, you can find the associated string define for it in the client and start tracing from there. You will then be able to find the function that will show the error message as well as the logic for checking your input text. With that knowledge, it’s pretty easy to create your own swear filter bypass as well!

Like a lot of the previous guides’ concepts, it really just is a matter of getting lucky or spending enough time tracing through the client code until you find what you are looking for. Don’t expect to be able to open the client and just find this type of stuff without a bit of work! The code I’m showing in this guide probably took a good couple of days of tracing and testing to come up with when I first made it a while back.

With that said, and nothing really to cover, we can look at our specific code.

IV. Implementation

Basically we have 3 tasks to complete here. The first is to hook the input logic. This is the code that is called whenever we type something into the client. Silkroad has a special logic handler to where it will not display or send any text that has a / at the beginning of the line. We can use this to our advantage in implementing custom commands and arguments in our programs. For example, some of you might remember the Softmod command set, /sun, /rain, /snow, /min, /max, etc… As mentioned before, the chat input was found by finding where the swear filter took place and tracing backwards from that.

The second task we have is to hook the output logic. This was not too hard of a task if you use the approach I mentioned about finding the swear filter logic and then looking at the code that showed the error message if your text contained illegal words. In addition, there is a starting welcome message that you always see when you start the game that you could also track down in the client and base your function on. Either way, this task is not terribly hard for Silkroad!

The third and final task we have is to expose the function for displaying text in the console box. Since we are hooking the console output function, we can just call the function like the client does and voila! We are able to print to the console box. The method shown in this guide is the simple way to do it.

The last thing I should talk about is the additional logic used in the console output hook. I have added in additional code to check to see if the message should be displayed or not. While this is not required, it is a little extra feature I wanted to throw in because those spam bots in towns make for a very annoying development time if you are trying to output to the console! You will need to write a bit of code to implement a chat block, but it’s a lot easier having this code in place.

As I said in the beginning, this guide is more of a code and explanation guide rather than a how to from scratch. The binary snippet for the console out was from 2007, so it should be safe for a while. The console input method is best found using the string and then looking for nearly identical code. The actual line being hooked has changed in the past. Here is the complete DLL.cpp file with all of our concepts in place:


Código:
#define _CRT_SECURE_NO_WARNINGS
#include 
#include "../common/common.h"

// Global instance handle to this DLL
HMODULE gInstance = NULL;

// Function prototype
void UserOnInject();

// Main DLL entry point
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReason, LPVOID lpReserved)
{
 UNREFERENCED_PARAMETER(lpReserved);
 if(ulReason == DLL_PROCESS_ATTACH)
 {
  gInstance = hModule;
  // Do not notify this DLL of thread based events
  DisableThreadLibraryCalls(hModule);
 }
 return TRUE;
}

// This is the main function that is called when the DLL is injected into the process
extern "C" __declspec(dllexport) void OnInject(DWORD address, LPDWORD bytes)
{
 // Restore the original bytes at the OEP
 DWORD wrote = 0;
 WriteProcessMemory(GetCurrentProcess(), UlongToPtr(address), bytes, 6, &wrote);

 // Call our user function to keep this function clean
 UserOnInject();
}

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

void OnConsoleInput(std::string input);

namespace CC_HookConsoleInput
{
 wchar_t * message;

 void HookInput()
 {
  char mbMessage[4096] = {0};
  wcstombs(mbMessage, message, 4095);
  OnConsoleInput(mbMessage);
 }

 // Look for "UIIT_MSG_CANT_CHATTING" and scroll down a lot, code looks like:
 /*
 006CCC4E  |.  8DBC24 FC000000    LEA EDI,DWORD PTR SS:[ESP+FC]
 006CCC55  |>  896C24 24          MOV DWORD PTR SS:[ESP+24],EBP
 006CCC59  |.  896C24 18          MOV DWORD PTR SS:[ESP+18],EBP
 006CCC5D  |.  896C24 1C          MOV DWORD PTR SS:[ESP+1C],EBP
 006CCC61  |.  896C24 20          MOV DWORD PTR SS:[ESP+20],EBP
 006CCC65  |.  392D 08B9D300      CMP DWORD PTR DS:[D3B908],EBP
 006CCC6B  |.  889C24 70040000    MOV BYTE PTR SS:[ESP+470],BL
 */
 DWORD codecave_HookInput_ReturnAddress = 0;
 __declspec(naked) void codecave_HookInput()
 {
  __asm pop codecave_HookInput_ReturnAddress
  __asm mov message, EDI
  __asm pushad
  __asm call HookInput
  __asm popad
  __asm MOV BYTE PTR SS:[ESP + 0x470], BL // Original code
  __asm push codecave_HookInput_ReturnAddress
  __asm ret
 }

 void Setup()
 {
  edx::CreateCodeCave(0x6CCC6B, 7, codecave_HookInput);
 }
}

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

bool OnConsoleOutput(int type, std::string output);

namespace CC_HookConsoleOutput
{
 BOOL chatblocked = FALSE;
 wchar_t * msgaddr = 0;
 DWORD msgtype = 0;

 void HookOutput()
 {
  char mbMessage[4096] = {0};
  wcstombs(mbMessage, msgaddr, 4095);
  chatblocked = OnConsoleOutput(msgtype, mbMessage);
 }

 // Bin: 8B 44 24 10 8B 54 24 08 50 8B 44 24 10 6A 00 52 8B 54 24 10 50 52 6A 01 6A 01
 DWORD codecave_HookOutput_ReturnAddress = 0;
 __declspec(naked) void codecave_HookOutput(void)
 {
  __asm
  {
   pop codecave_HookOutput_ReturnAddress
   MOV EAX, DWORD PTR SS:[ESP + 0x10]
   MOV EDX, DWORD PTR SS:[ESP + 0x8]
   mov msgtype, EAX
   mov msgaddr, EDX
   pushad
   CALL HookOutput
   cmp chatblocked, 0
   mov chatblocked, 0
   jne LABEL1
   popad
   ret 0x10
LABEL1:
   popad
   push codecave_HookOutput_ReturnAddress
   ret
  }
 }

 void Setup()
 {
  edx::CreateCodeCave(0x697DA0, 8, codecave_HookOutput);
 }
}

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

void DisplayText(DWORD color, std::string text)
{
 // Bin: 8B 44 24 10 8B 54 24 08 50 8B 44 24 10 6A 00 52 8B 54 24 10 50 52 6A 01 6A 01
 // (The address of the function we hook above!)
 FARPROC ConsoleOutFunc = (FARPROC)0x697DA0;
 wchar_t outputText[4096] = {0};
 wchar_t * pOutputText = outputText;
 mbstowcs(outputText, text.c_str(), 2048);
 color |= 0xFF000000; // Make sure alpha is set
 __asm
 {
  MOV ECX, DWORD PTR DS:[0xF5CA74]
  push 1
  push color
  push pOutputText
  push 1
  CALL ConsoleOutFunc
 }
}

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

// The function where we place all our logic
void UserOnInject()
{
 // Create a debugging console
 edx::CreateConsole("SilkroadFramework Debugging Console");

 // Mutex for the launcher, no patches required to start Silkroad now
 CreateMutexA(0, 0, "Silkroad Online Launcher");
 CreateMutexA(0, 0, "Ready");

 CC_HookConsoleInput::Setup();
 CC_HookConsoleOutput::Setup();
}

void OnConsoleInput(std::string input)
{
 printf("[INPUT] %s\n", input.c_str());
 DisplayText(0x00FF00, input); // Color is in RRGGBB format.
}

bool OnConsoleOutput(int type, std::string output)
{
 printf("[OUTPUT][%i] %s\n", type, output.c_str());
 return true; // Keep the output
}
Here is what an example output looks like:



V. Conclusion

By following this guide, we now have additional tools available at our disposal. Being able to hook the console input allows us to implement direct interactions with our client for additional flexibility in our programs. Hooking the console output helps with logging and being able to block certain text from being on the screen. Finally, the display function helps give in game debug printing capabilities without having to rely solely on our console.

With this guide concludes most of the main core functionality we should need for our own custom injected DLLs. So far, most of the guides in this series provide a solid foundation of functionality and techniques to use to accomplish various tasks. Now that this knowledge has been disseminated, future guides can make use of it all to provide a more in depth look at complex programs that make use of all of these things.

That warps up this guide. I hope you have enjoyed it! I will be soon utilizing all of the concepts presented in my guides in one larger guide that shows something worthwhile!

Drew “pushedx” Benton
edxLabs

0 comentarios:

Publicar un comentario