Diablo 2 hacking (packets, d2hackit etc.) guides
Packets Guide by EvilCheese
Packets Guide by After-Death
Diablo II Packet Lists & Info
Diablo II 1.10 Skills List
D2Hackitv2 Tutorial
Battlenet Packetlist v1.11 Compilant
D2Hackit v2.00 API Reference
D2HackIt |
D2HackIt Modules |
D2HackIt Bots
D2Hackit v2 Module Tutorial by Lemmin
This tutorial is designed to teach you how to create a module to be used with
d2h v2. This is NOT a c++ tutorial although you can very easily learn some by
reading this tutorial. I'm actually still learning c++ myself, so please correct
me if i say something that is completely wrong.
For this tutorial we will create a follow mod, why not?
first thing to know is that d2h modules are dynamic link libraries (dll)s. if
you use visual c you can create a new project for a dll and put the include
folder in the project folder. otherwise you can do it with notepad and boreland
compiler. (do people need a visual c tutorial too?)
I am going to go through all the segments of code and have the whole thing
posted at the bottom.
Code:
#include "includes\ClientCore.cpp"
First thing we need is the #include because we are calling definitions from d2h
files. if you are using d2 v.80 (or whatever) its just "ClientCore.cpp".
Code:
CLIENTINFO
(
0,1,
"LemminMan",
"lemminman.home.comcast.net",
"Lemmin Follower",
""
)
This functuion displays the information you put into it, as well as saving the
info for the help functions (i think). this will be general version information
about your module. you don't need to put this function in, but its a good way to
personalize it.
LemminMan = Your name!
lemminman.home.comcast.net = your website without the http protocal
Lemmin Follower = the name of your mod.
(i think the numbers are version numbers or sometihng, but i never cared about
them and i dont remember what that last string is supposed to be. Some tutorial
huh?)
Code:
bool on = false;
GAMEUNIT guTarget;
char chTargetName[24];
DWORD dTarget[4];
Next we declare all the variables:
1. first we have a booleen variable 'on' that is set to false if you want your
mod to require a command to turn it on at start, and true if you want a command
to turn it off at start.
2. And because this is a follow module, we will need a gameunit for the target
that we will follow (gameunit can only be used in d2h v2 (i think)).
3. We also want an array of chars to create a string for the name of the
character that we will follow. this is used for whatever reason, but in this
tutorial we will have a hud showing the target.
4. And last, we need a dword for the targets id when we grab it.
Code:
BOOL PRIVATE Seton(char** argv, int argc)
{
on = true;
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2ON");
return true;
}
BOOL PRIVATE Setoff(char** argv, int argc)
{
on = false;
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc1OFF");
return true;
}
These are two commands that can be executed in your module (they are implemented
in the next section of code). There are on and off, which are self explainatory.
whatever is put in these functions will execute whenever the respective command
is typed in.
Code:
MODULECOMMANDSTRUCT ModuleCommands[]=
{
{"on", Seton, "Turn Follower On"},
{"off", Setoff, "Turn Module Off"},
};
This struct sets up the commands you want to be in your module. for now we have
on and off. they corrispond to seton and setoff respectively.
Code:
VOID EXPORT OnGameJoin(THISGAMESTRUCT* thisgame)
{
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2Loaded!");
return;
}
This code is executed whenever you enter a game. it is also executed whenever
the module is loaded (which is kind of anoying for some reasons, but there are
ways around it).
Theres nothing here i want to do accept announce that its loaded.
Code:
DWORD EXPORT OnGamePacketBeforeSent(BYTE* aPacket, DWORD aLen)
{
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00)
{
memcpy(&dTarget[0], aPacket+3, 4);
guTarget.dwUnitID = dTarget[0];
server->GetUnitName(&guTarget, &chTargetName[0], 24);
if (server->VerifyUnit(&guTarget))
{
server->GameInfof("ÿc4Lemmin Follower ÿc3:: ÿc2Target setÿc5: ÿc3%s",
chTargetName);
}
else
{
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2Target to far away to get
name");
}
}
return aLen;
}
This code is executed just before a packet is sent, aPacket[] being the packet
in question. lots there, lets break it down by line.
Code:
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00){}
This is unclicking the lil ear button next to a player (the squelch inverse
thingy). so i am telling d2, if the packet starts with 5d and continues on with
02 and 00 (5d0200). basically, you click the ear button in the player menu twice
to select the character you want to target.
Code:
memcpy(&dTarget[0], aPacket+3, 4);
memcpy is the function we will use to copy one dwords adress to another. aPacket
to dTarget in this one.
i want all 4 bytes of the dTarget to be filled starting with the first one which
is [0] in an array.
then i want to start from the 4th byte in aPacket and copy 4 bytes total. its
plus 3 because it is starting at 1, so the 4th is plus 3 more.
and like i said, i want 4 bytes so the 4 at the end is the length.
Code:
guTarget.dwUnitID = dTarget[0];
and now those 4 bytes are in our gameunit struct and we can do all sorts of
stuff with it. (im sure theres a way to memcpy straight to the struct, but i
cant figure it out )
Code:
server->GetUnitName(&guTarget, &chTargetName[0], 24);
since we have a valid gameunit all set up, we can use all the neeto functions
d2h gives us. lets start by finding the name of the gameunit we just got. we put
it in the array of chars that we defined earlier. remember it was defined to
have 24 chars, so thats what the next item is, the length.
Code:
if (server->VerifyUnit(&guTarget)){}
heres another neet function. this will return a non-zero if the gameunit is
found in the game at the time it is called.
Code:
{
server->GameInfof("ÿc4Lemmin Follower ÿc3:: ÿc2Target setÿc5: ÿc3%s",
chTargetName);
}
else
{
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2Target to far away to get
name");
}
if its found we send a lil message to the game. infof can be used just as printf
in c in that you use a %[letter] as the format of the variable that is being
printed. %s is string.
if its not found we say its not found :p
Code:
return aLen;
everything executed? so go ahead and send that packet to the server.
Code:
DWORD EXPORT OnGamePacketBeforeReceived(BYTE* aPacket, DWORD aLen)
{
return aLen;
}
here is another useful function that is used the same as OnGamePacketBeforeSent.
this is not needed for this module though.
Code:
DWORD EXPORT OnGameTimerTick(void)
{
if (on == true )
{
me->MoveToUnit(&guTarget, false);
Sleep(500);
}
return 0;
}
this next lil d2function is called errrrrtime the game loops. so you should be
careful when putting code in here not to make stuff ultra spam.
anyway, if on is true, which means if you typed .mod on, than it will execute
the followed code. MoveToUnit is another one of those magic d2h v2 functions
that does just what its called, so we go to the target, yay! and false is to not
be put in a job queue, if its true, it wont go to the next movement untill it
reaches the spot from the previous MoveTo.
then so as not to spam the crap out of the d2 server with movement packets, we
put about a half second pause between moving. (i dont know what the return
changes, sorry )
Code:
VOID EXPORT OnDraw(CGameDC* pDC, LPCRECT lpScreenRect)
{
// Then we draw a transparent white rectangle outlines by a
// yellow frame.
pDC->SetPenColor(0xEA); // 0xA8 = yellow
pDC->SetPenWidth(1); // Frame width is 1
pDC->SetBrushColor(0x20); // 0x20 = white
pDC->SetBrushTransparency(1); // Nice-looking translucent.
RECT rect = { 325, 1, 475, 26};
pDC->Rectangle(&rect);
// Finally we draw some text at center of the rectangle, using
// a thin font.
pDC->SetFont(D2FONT_THIN);
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "ÿc3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("ÿc1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
}
And here is our hud! the drawing is confusing, so i just left the original
comments by the d2h creator.
The rect coords are left, top, bottom, right.
below i will explain the code that isnt commented:
Code:
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "ÿc3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("ÿc1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
here we check to see if the unit is found in the game just like before. if it is,
we do the 3 lines there.
we declare a char just like the chTargetName here, but with 3 extra chars for
the d2 color code. setting it equal to "ÿc3" fills up the first 3 chars with the
color code for blue.
now strcat, which is a fucntion similar to memcpy, that will append the string
from chTargetName to chColorName. we dont need to use +2 or anything because it
is appending, not copying.
now that we have a colorful string, we pop it into the DrawText function, put it
in rect, and center it. (thats about as well as i can explain that, i didnt read
up on the draw functions much.)
and if the unit isnt found, we just say no target.
And thats it!!
here is the code all together for you copy pasters:
Code:
#include "includes\ClientCore.cpp"
CLIENTINFO
(
0,1,
"LemminMan",
"lemminman.home.comcast.net",
"Lemmin Follower",
""
)
bool on = false;
GAMEUNIT guTarget;
char chTargetName[24];
DWORD dTarget[4];
BOOL PRIVATE Seton(char** argv, int argc)
{
on = true;
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2ON");
return true;
}
BOOL PRIVATE Setoff(char** argv, int argc)
{
on = false;
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc1OFF");
return true;
}
MODULECOMMANDSTRUCT ModuleCommands[]=
{
{"on", Seton, ""},
{"off", Setoff, ""},
};
VOID EXPORT OnGameJoin(THISGAMESTRUCT* thisgame)
{
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2Loaded!");
return;
}
DWORD EXPORT OnGamePacketBeforeSent(BYTE* aPacket, DWORD aLen)
{
if (aPacket[0] == 0x5d && aPacket[1] == 0x02 && aPacket[2] == 0x00)
{
memcpy(&dTarget[0], aPacket+3, 4);
guTarget.dwUnitID = dTarget[0];
guTarget.dwUnitType = UNIT_TYPE_PLAYER;
server->GetUnitName(&guTarget, &chTargetName[0], 24);
if (server->VerifyUnit(&guTarget))
{
server->GameInfof("ÿc4Lemmin Follower ÿc3:: ÿc2Target setÿc5: ÿc3%s",
chTargetName);
}
else
{
server->GamePrintInfo("ÿc4Lemmin Follower ÿc3:: ÿc2Target to far away to get
name");
}
}
return aLen;
}
DWORD EXPORT OnGameTimerTick(void)
{
if (on == true )
{
me->MoveToUnit(&guTarget, false);
Sleep(1000);
}
return 0;
}
/*DWORD EXPORT OnGamePacketBeforeReceived(BYTE* aPacket, DWORD aLen)
{
return aLen;
}*/
VOID EXPORT OnDraw(CGameDC* pDC, LPCRECT lpScreenRect)
{
// Then we draw a transparent white rectangle outlines by a
// yellow frame.
pDC->SetPenColor(0xEA); // 0xA8 = yellow
pDC->SetPenWidth(1); // Frame width is 1
pDC->SetBrushColor(0x20); // 0x20 = white
pDC->SetBrushTransparency(1); // Nice-looking translucent.
RECT rect = { 325, 1, 475, 26};
pDC->Rectangle(&rect);
// Finally we draw some text at center of the rectangle, using
// a thin font.
pDC->SetFont(D2FONT_THIN);
if (server->VerifyUnit(&guTarget))
{
char chColorName[27] = "ÿc3";
strcat(&chColorName[0], &chTargetName[0]);
pDC->DrawText(chColorName, &rect, DT_CENTER | DT_VCENTER);
}
else
{
pDC->DrawText("ÿc1NO TARGET", &rect, DT_CENTER | DT_VCENTER);
}
}
hope this helped.
Diablo 2 Newsletter
Questions, ideas, problems, wishes?
Be informed whenever something new comes up (or any important problems are fixed.).
You can unsubscribe from this newsletter at any time.
hiep
19 Nov 2008, 13:47
ai bao ho em cach hack diablo 2 caj em xin cam on rat' nhieu`
|