Why do you use WriteProcessMemory when you're injected?
If you are talking about in the OnInject function, there is no reason in particular, other than I used that code from another project of mine where it was needed. You could use the other provided WriteBytes function if you wanted to though.
There are cases where you should use WPM if you are inside DLLMain and injected into a process rather than a memcpy since the CRT might not yet be initialized (for the DLL versions) and you get a nasty error when trying to start the program. Using a static CRT should avoid that issue, but it's easy to forget to change that. This issue is always avoided in my case since I call the exported function after DLLMain is finished, so all functions are safe to use.
If you are talking about in the OnInject function, there is no reason in particular, other than I used that code from another project of mine where it was needed. You could use the other provided WriteBytes function if you wanted to though.
There are cases where you should use WPM if you are inside DLLMain and injected into a process rather than a memcpy since the CRT might not yet be initialized (for the DLL versions) and you get a nasty error when trying to start the program. Using a static CRT should avoid that issue, but it's easy to forget to change that. This issue is always avoided in my case since I call the exported function after DLLMain is finished, so all functions are safe to use.
It's unecessary when you're injected. Just use a pointer. And I never got any errors with that.
I've made a little update for the new (unpacked) client. This version should be a lot easier to update in the future as opposed to the original version posted. This guides code is also combined with the other guides for extracting built packets. This is the simple version of the analyzer that just has the code for console logging and is ready for you to do with it what you want.
DLL.h
Code:
#pragma once
// Bin: 33 C0 89 46 10 89 46 14 C7 46 08 00 00 00 00 C7 46 04 00 00 00 00 8B C6 5E C2 04 00 CC
// First line in function
#define CC_ExtractSentPacket_Address 0x535BA0
// 56 57 8D 9B 00 00 00 00 8B 73 08 81 E6 FF 0F 00 80 8B FD
// MOV EBX,ECX
// MOV DWORD PTR SS:[ESP+10],EAX
#define CC_ExtractSentPacket_Address_1 0x504C4C
// 64 A1 00 00 00 00 50 B8 08 10 00 00
// MOV EAX, 1008
#define CC_ExtractSentPacket_Address_2 0x76F9DE
// 2nd call address under "SR_RESET_CLIENT"; Case 3369 of switch 007794AA
// CALL sro_clie.006DC6F0 <--
#define CC_ExtractPacket_Address_1 0x6DC6F0
// End of ReadBytes function
// POP ESI
// MOV EAX,EBX
// POP EBX
// RETN 8
#define CC_ExtractPacket_Address_2 0x4F312C
// First CMP above "MSGID:0x%04X Wait My Char Data !!!"
//CMP EAX, 3369
#define CC_ExtractPacket_Address_3 0x779B1A
// Line of second call below "SR_RESET_CLIENT"; Case 3369 of switch 007794AA
// --> 00779555 . E8 262BF6FF CALL 200_clie.006DC080
#define CC_ExtractPacket_Address_4 0x779BC5
DLL.cpp
Code:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include "../common/common.h"
#include "DLL.h"
//-------------------------------------------------------------------------
// Global instance handle to this DLL
HMODULE gInstance = NULL;
// Function prototype
void UserOnInject();
void UserOnDeinitialize();
//-------------------------------------------------------------------------
// 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);
}
else if(ulReason == DLL_PROCESS_DETACH)
{
UserOnDeinitialize();
}
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();
}
//-------------------------------------------------------------------------
namespace CC_ExtractSentPacket
{
DWORD currentOpcode;
LPBYTE currentBuffer;
DWORD currentSize;
void OnProcessDataStart()
{
printf("[ClientToServer][%X]\n", currentOpcode);
}
void ProcessData()
{
for(DWORD x = 0; x < currentSize; ++x)
{
printf("%.2X ", currentBuffer[x]);
if((x+1)%16 == 0)
printf("\n");
}
printf("\n");
}
void OnProcessDataEnd()
{
printf("\n\n");
}
void EnableParseHook();
void DisableParseHook();
DWORD codecave_SetOpcode_ReturnAddress = 0;
__declspec(naked) void codecave_SetOpcode()
{
__asm pop codecave_SetOpcode_ReturnAddress
__asm MOV AX,WORD PTR SS:[ESP + 0x04] // Original code
__asm mov currentOpcode, eax
__asm pushad
EnableParseHook(); // Start hooking writes
__asm popad
__asm push codecave_SetOpcode_ReturnAddress
__asm ret
}
DWORD codecave_WriteBytes_ReturnAddress = 0;
__declspec(naked) void codecave_WriteBytes()
{
__asm pop codecave_WriteBytes_ReturnAddress
__asm mov currentBuffer, eax
__asm mov currentSize, ebp
__asm pushad
ProcessData();
__asm popad
// Even though this comes first, we will use EBX and then fix it up
// afterwards. This is ok since ECX is not disturbed.
//__asm MOV EBX, ECX
// This line seems to give the client problems when compiled in VS,
// so we have to rewrite it a little. This might be one of the hardest
// things to figure out if you were doing this yourself and not
// too experienced.
//__asm MOV DWORD PTR SS:[ESP + 0x10],EAX
__asm
{
mov ebx, esp
add ebx, 0x10
mov [ebx], eax
}
// Now set our EBX value to what it should be
__asm MOV EBX, ECX
__asm push codecave_WriteBytes_ReturnAddress
__asm ret
}
DWORD codecave_PostProcessPacket1_ReturnAddress;
__declspec(naked) void codecave_PostProcessPacket1()
{
__asm pop codecave_PostProcessPacket1_ReturnAddress
// We choose this line because it is the easiest to codecave and won't likely change
__asm MOV EAX, 0x1008 // Remember #s have to be in HEX! 1008 != 0x1008
__asm pushad
DisableParseHook();
OnProcessDataEnd();
__asm popad
__asm push codecave_PostProcessPacket1_ReturnAddress
__asm ret
}
void EnableParseHook()
{
// A little trick I came up with to filter out the packets received
// that make use of this functionality. Not sure if it'll always work,
// but it "should" since the higher 4 bytes of any C->S packet in the
// executable will be 0 since they are hard coded into the client!
DWORD result = currentOpcode & 0xFFFF0000;
//printf("Result = %i\n", result); // Debugging
if(result == 0)
{
// Hook the byte writing function
edx::CreateCodeCave(CC_ExtractSentPacket_Address_1, 6, codecave_WriteBytes);
// Hook the post build processing function
edx::CreateCodeCave(CC_ExtractSentPacket_Address_2, 5, codecave_PostProcessPacket1);
// Let the user know we have an new packet being built
OnProcessDataStart();
}
}
void DisableParseHook()
{
// Restore the byte writing function code
static BYTE patch1[] = {0x8B, 0xD9, 0x89, 0x44, 0x24, 0x10};
edx::WriteBytes(CC_ExtractSentPacket_Address_1, patch1, 6);
// Restore the post build processing function code
static BYTE patch2[] = {0xB8, 0x08, 0x10, 0x00, 0x00};
edx::WriteBytes(CC_ExtractSentPacket_Address_2, patch2, 5);
}
void Setup()
{
edx::CreateCodeCave(CC_ExtractSentPacket_Address, 5, codecave_SetOpcode);
}
}
//-------------------------------------------------------------------------
namespace CC_ExtractPacket
{
FARPROC fpHandler = (FARPROC)CC_ExtractPacket_Address_1;
DWORD currentOpcode;
LPBYTE currentBuffer;
DWORD currentSize;
void OnProcessDataStart()
{
printf("[ServerToClient][%X]\n", currentOpcode);
}
void ProcessData()
{
for(DWORD x = 0; x < currentSize; ++x)
{
printf("%.2X ", currentBuffer[x]);
if((x+1)%16 == 0)
printf("\n");
}
printf("\n");
}
void OnProcessDataEnd()
{
printf("\n\n");
}
DWORD codecave_ExtractPacket_ReturnAddress = 0;
__declspec(naked) void codecave_ExtractPacket()
{
__asm pop codecave_ExtractPacket_ReturnAddress
__asm mov currentOpcode, eax
__asm pushad
OnProcessDataStart();
__asm popad
__asm CMP EAX, 0x3369 // Original code
__asm push codecave_ExtractPacket_ReturnAddress
__asm ret
}
DWORD codecave_ReadBytes_ReturnAddress = 0;
__declspec(naked) void codecave_ReadBytes()
{
__asm pop codecave_ReadBytes_ReturnAddress
__asm mov currentBuffer, eax
__asm mov currentSize, ebx
__asm pushad
ProcessData();
__asm popad
// Emulate the rest of the function since our codecave overlaps it all
__asm POP ESI
__asm MOV EAX,EBX
__asm POP EBX
__asm RET 8
}
void EnableParseHook()
{
edx::CreateCodeCave(CC_ExtractPacket_Address_2, 7, codecave_ReadBytes);
}
void DisableParseHook()
{
static BYTE patch[] = {0x5E, 0x8B, 0xC3, 0x5B, 0xC2, 0x08, 0x00};
edx::WriteBytes(CC_ExtractPacket_Address_2, patch, 7);
OnProcessDataEnd();
}
DWORD codecave_InvokeHandlers_ReturnAddress;
__declspec(naked) void codecave_InvokeHandlers()
{
__asm pop codecave_InvokeHandlers_ReturnAddress
__asm pushad
EnableParseHook();
__asm popad
// We have to use this trick as VS does not support calling direct memory addresses
__asm call fpHandler
__asm pushad
DisableParseHook();
__asm popad
__asm push codecave_InvokeHandlers_ReturnAddress
__asm ret
}
void Setup()
{
edx::CreateCodeCave(CC_ExtractPacket_Address_3, 5, codecave_ExtractPacket);
edx::CreateCodeCave(CC_ExtractPacket_Address_4, 5, codecave_InvokeHandlers);
}
}
//-------------------------------------------------------------------------
// The function where we place all our logic
void UserOnInject()
{
// Create a debugging console
edx::CreateConsole("edxAnalyzer Debugging Console");
// Mutex for the launcher, no patches required to start Silkroad now
CreateMutexA(0, 0, "Silkroad Online Launcher");
CreateMutexA(0, 0, "Ready");
CC_ExtractSentPacket::Setup();
CC_ExtractPacket::Setup();
}
//-------------------------------------------------------------------------
void UserOnDeinitialize()
{
}
//-------------------------------------------------------------------------
I've also attached a GUI version of the analyzer from edx33 so the packets log to the GUI window instead of the console. That version is a much nicer development tool, but it's just a temporary solution for now. For users of the 2008 EE, you can't compile the project because of the resource editing limitations in EE, so I've also attached a binary version. If it doesn't work for you, your unpacked client might be different from mine.
One important thing to note about this method is that some packets don't display correctly or some seem parsed weird. The reason for this has to do with how the client works. This method is not a 100% accurate way of how the client parses the packets because the client maintains packet states internally so it can discard some data.
That means some packets might parse unexpectedly or only show portions of their data payload rather than the full packet. An example is B505, where if you cast Grasswalk, you won't see 10 bytes but rather 5, but if you cast another buff, you'll see 10 bytes. if you look at the raw packets in edx33, you'd have seen both packets actually contained 10 bytes. Just keep that in mind before relying solely on this feature because it can have an effect on your resulting code.
I am rewriting a lot of my previous released code, so I'll try to get updates posted to the important guides when I can, but as you can imagine, I'm pretty busy
Sorry, but my unpacked client generates quite a few false positives (5 total). That's enough to where I'm not comfortable uploading the file. I would assume this is from something in the new 3mb worth of code that was added into the client, but I'd rather now upload something that has any false positives scans...
I will provide an updated code and analyzer after tomorrow's inspection. I came up with a new patch that I think fixes most of the bugs I mentioned about some opcodes not being parsed right or at all.
Here is the new Analyzer for 1.205. I fixed some bugs with the packet parsings so all packets should hopefully parse correctly now. Also attached is the complete project to use as a new base for future updates.
Here is the new source code as well:
DLL.h
Code:
#pragma once
// Bin: 33 C0 89 46 10 89 46 14 C7 46 08 00 00 00 00 C7 46 04 00 00 00 00 8B C6 5E C2 04 00 CC
// First line in function
#define CC_ExtractSentPacket_Address 0x535BF0
// 56 57 8D 9B 00 00 00 00 8B 73 08 81 E6 FF 0F 00 80 8B FD
// MOV EBX,ECX
// MOV DWORD PTR SS:[ESP+10],EAX
#define CC_ExtractSentPacket_Address_1 0x504C9C
// 64 A1 00 00 00 00 50 B8 08 10 00 00
// MOV EAX, 1008
#define CC_ExtractSentPacket_Address_2 0x76FBCE
// Find sequence
//push ebx
//call eax
//cmp eax, 1
#define CC_ExtractPacket_Address_1 0xA865C0
// End of ReadBytes function
// POP ESI
// MOV EAX,EBX
// POP EBX
// RETN 8
#define CC_ExtractPacket_Address_2 0x4F317C
DLL.cpp
Code:
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include "../common/common.h"
#include "DLL.h"
#include "Analyzer.h"
//-------------------------------------------------------------------------
// Global instance handle to this DLL
HMODULE gInstance = NULL;
// Function prototype
void UserOnInject();
void UserOnDeinitialize();
//-------------------------------------------------------------------------
// 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);
}
else if(ulReason == DLL_PROCESS_DETACH)
{
UserOnDeinitialize();
}
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();
}
//-------------------------------------------------------------------------
namespace CC_ExtractSentPacket
{
DWORD currentOpcode;
LPBYTE currentBuffer;
DWORD currentSize;
void OnProcessDataStart()
{
Analyzer_SetOpcode((WORD)currentOpcode, false);
}
void ProcessData()
{
Analyzer_StreamData(currentBuffer, currentSize);
}
void OnProcessDataEnd()
{
Analyzer_EndPacket();
}
void EnableParseHook();
void DisableParseHook();
DWORD codecave_SetOpcode_ReturnAddress = 0;
__declspec(naked) void codecave_SetOpcode()
{
__asm pop codecave_SetOpcode_ReturnAddress
__asm MOV AX,WORD PTR SS:[ESP + 0x04] // Original code
__asm mov currentOpcode, eax
__asm pushad
EnableParseHook(); // Start hooking writes
__asm popad
__asm push codecave_SetOpcode_ReturnAddress
__asm ret
}
DWORD codecave_WriteBytes_ReturnAddress = 0;
__declspec(naked) void codecave_WriteBytes()
{
__asm pop codecave_WriteBytes_ReturnAddress
__asm mov currentBuffer, eax
__asm mov currentSize, ebp
__asm pushad
ProcessData();
__asm popad
// Even though this comes first, we will use EBX and then fix it up
// afterwards. This is ok since ECX is not disturbed.
//__asm MOV EBX, ECX
// This line seems to give the client problems when compiled in VS,
// so we have to rewrite it a little. This might be one of the hardest
// things to figure out if you were doing this yourself and not
// too experienced.
//__asm MOV DWORD PTR SS:[ESP + 0x10],EAX
__asm
{
mov ebx, esp
add ebx, 0x10
mov [ebx], eax
}
// Now set our EBX value to what it should be
__asm MOV EBX, ECX
__asm push codecave_WriteBytes_ReturnAddress
__asm ret
}
DWORD codecave_PostProcessPacket1_ReturnAddress;
__declspec(naked) void codecave_PostProcessPacket1()
{
__asm pop codecave_PostProcessPacket1_ReturnAddress
__asm pushad
DisableParseHook();
OnProcessDataEnd();
__asm popad
// We choose this line because it is the easiest to codecave and won't likely change
__asm MOV EAX, 0x1008 // Remember #s have to be in HEX! 1008 != 0x1008
__asm push codecave_PostProcessPacket1_ReturnAddress
__asm ret
}
void EnableParseHook()
{
// A little trick I came up with to filter out the packets received
// that make use of this functionality. Not sure if it'll always work,
// but it "should" since the higher 4 bytes of any C->S packet in the
// executable will be 0 since they are hard coded into the client!
DWORD result = currentOpcode & 0xFFFF0000;
//printf("Result = %i\n", result); // Debugging
if(result == 0)
{
// Hook the byte writing function
edx::CreateCodeCave(CC_ExtractSentPacket_Address_1, 6, codecave_WriteBytes);
// Hook the post build processing function
edx::CreateCodeCave(CC_ExtractSentPacket_Address_2, 5, codecave_PostProcessPacket1);
// Let the user know we have an new packet being built
OnProcessDataStart();
}
}
void DisableParseHook()
{
// Restore the byte writing function code
static BYTE patch1[] = {0x8B, 0xD9, 0x89, 0x44, 0x24, 0x10};
edx::WriteBytes(CC_ExtractSentPacket_Address_1, patch1, 6);
// Restore the post build processing function code
static BYTE patch2[] = {0xB8, 0x08, 0x10, 0x00, 0x00};
edx::WriteBytes(CC_ExtractSentPacket_Address_2, patch2, 5);
}
void Setup()
{
edx::CreateCodeCave(CC_ExtractSentPacket_Address, 5, codecave_SetOpcode);
}
}
//-------------------------------------------------------------------------
namespace CC_ExtractPacket
{
DWORD currentOpcode;
LPBYTE currentBuffer;
DWORD currentSize;
void * packetPtr;
DWORD lastAddr = 0;
void OnProcessDataStart()
{
Analyzer_SetOpcode((WORD)currentOpcode, true);
}
void ProcessData()
{
Analyzer_StreamData(currentBuffer, currentSize);
}
void OnProcessDataEnd()
{
Analyzer_EndPacket();
}
void ProcessPacket()
{
LPBYTE lpBuffer = (LPBYTE)packetPtr;
DWORD packetData = *(LPDWORD)(lpBuffer + 0x18 - 4);
currentOpcode = *((LPWORD)(lpBuffer + 0x18));
WORD packetLength = *((LPWORD)(lpBuffer + 0x18 - 16));
if(lastAddr == packetData)
{
}
else
{
lastAddr = packetData;
OnProcessDataStart();
}
}
DWORD codecave_ReadBytes_ReturnAddress = 0;
__declspec(naked) void codecave_ReadBytes()
{
__asm pop codecave_ReadBytes_ReturnAddress
__asm mov currentBuffer, eax
__asm mov currentSize, ebx
__asm pushad
ProcessData();
__asm popad
// Emulate the rest of the function since our codecave overlaps it all
__asm POP ESI
__asm MOV EAX,EBX
__asm POP EBX
__asm RET 8
}
void EnableParseHook()
{
edx::CreateCodeCave(CC_ExtractPacket_Address_2, 7, codecave_ReadBytes);
}
void DisableParseHook()
{
static BYTE patch[] = {0x5E, 0x8B, 0xC3, 0x5B, 0xC2, 0x08, 0x00};
edx::WriteBytes(CC_ExtractPacket_Address_2, patch, 7);
OnProcessDataEnd();
}
DWORD codecave_HookPacketProcess_ReturnAddress;
__declspec(naked) void codecave_HookPacketProcess()
{
__asm pop codecave_HookPacketProcess_ReturnAddress
__asm mov packetPtr, ebx
__asm pushad
ProcessPacket();
EnableParseHook();
__asm popad
__asm PUSH EBX
__asm CALL NEAR EAX
__asm CMP EAX,1
__asm pushad
DisableParseHook();
__asm popad
__asm push codecave_HookPacketProcess_ReturnAddress
__asm ret
}
void Setup()
{
edx::CreateCodeCave(CC_ExtractPacket_Address_1, 6, codecave_HookPacketProcess);
}
}
//-------------------------------------------------------------------------
// The function where we place all our logic
void UserOnInject()
{
// Create a debugging console
//edx::CreateConsole("edxAnalyzer Debugging Console");
// Mutex for the launcher, no patches required to start Silkroad now
CreateMutexA(0, 0, "Silkroad Online Launcher");
CreateMutexA(0, 0, "Ready");
CC_ExtractSentPacket::Setup();
CC_ExtractPacket::Setup();
Analyzer_Setup();
}
//-------------------------------------------------------------------------
void UserOnDeinitialize()
{
Analyzer_Cleanup();
}
//-------------------------------------------------------------------------
I tried to update your code for the current tSRO-client. But im unable to compile it. THe error for the DLL is
Quote:
2>.\DLL.rc(10) : fatal error RC1015: cannot open include file 'afxres.h'.
All what i found out is, that i nned the ATL/MFC? But where can i get it? I tried diffrent things, but everything failed
Could it maybe be, that "Visual Studio 2008 Express" won't support it?
Could it maybe be, that "Visual Studio 2008 Express" won't support it?
Yes, Visual Studio 2008 Express does not come with any of the Gui editing tools, so only a higher version of Visual Studio 2008 can compile projects that come with a Gui.
You can also delete the Gui related stuff and output to a console, but that does not make for a good tool hehe. For the inject Gui, you can create the window yourself, but that adds a lot of extra code so that's why I use the Gui builder.
[Guide] Extracting Built Packets in Silkroad 11/08/2018 - SRO Guides & Templates - 18 Replies Extracting Built Packets in Silkroad
I. Purpose
This guide will provide a complement to the previous guide of extracting the parsed packets in Silkroad. This time, we will learn how to extract packets the client sends the serer as they are built. Because most of the concepts in and theory are the same, this guide will be much shorter than the previous so please refer back to that article for all the nitty-gritty details.
An article like this is also just as important to understand as...
Extracting sounds? 01/03/2010 - Grand Chase - 3 Replies is there a way to extract sounds from the kom files? or is that against the rules ?
[Help] Extracting .wdf files 09/02/2009 - CO2 Weapon, Armor, Effects & Interface edits - 3 Replies hello all!!
maybe it's not here i need to ask it but...
i need a .wdf extractor to make my edits in the login screen of the client...
someone can help me ... i will be gratefull
[Guide]How to analyse Packets on Flyff 04/27/2009 - Flyff Private Server - 8 Replies Hiho Elitepvper,
Sinn dieses Guides ist es euch zu zeigen wie ihr Packets snifft und wie ihr sie analysiert.
Download:
MEGAUPLOAD - The leading online storage and file delivery service
Credits:
aldimaster
Devinepunition
Extracting with WinRar 03/15/2008 - Conquer Online 2 - 8 Replies Hi guys
I wanted to download a Rar file such as archerbuddy1.0...
Windows is not letting me so i was told to download Winrar..
I did that.... and people told me to click the Extract button from the pag that
page that pops up...
I don't understand what to do after you download the file with WinRar, and how