Mein erstes Tutorial wird ein wenig beschreiben, wie man sich einen simplen kleinen Bot zum Automatisieren verschiedener Abläufe in C basteln kann.
Dies wird ein Pixel Bot. Also ein Programm, das nach vordefinierten Pixeln sucht, und auf bestimmte Ereignisse verschiedene Dinge vollbringt.
Wir nehmen als Beispiel einmal ein MMORPG.
Darin müssten verschiedene Kriterien erfüllt werden, um in den vollen Genuss eines funktionierenden Noob-Bots zu kommen:
- Die Monster müssen von etwas bestimmten geprägt sein, wie z.B. einer HP-Bar (Anzeige)
- Hotkeys für die Tastatur wären gut, um sich z.B. zu heilen oder um Items aufzuheben
- Es wäre gut, wenn beim Angreifen eines Monsters etwas spezifisches angezeigt wird, wie z.B. dass der Cursor rot wird
Leider habe ich keines dieser Art gefunden, deshalb kann ich nun nur die Funktionen posten und dazu etwas erklären und nicht beispielsweise Screenshots oder einen vollwertigen Bot mitliefern.
Jetzt fange ich mal langsam an.. (Jope, ich habe keine Ahnung von strukturierten Texten :err: )
Schritt 1: HWND
Als erstes müssen wir uns Zugriff auf das gewollte Fenster verschaffen.
Diesen bekommen wir, indem wir die API Funktion FindWindow benutzen. Ihr übergeben wir als zweiten Parameter den Fenster Titel und bekommen dafür ein Fensterhandle mit dem wir vorfahren. (Der erste Parameter hat uns hier nicht zu kümmern) Nun Können wir das Fenster in Originalgröße anzeigen, es in den Vordergrund holen und den Tastaturfokus drauf setzen. Das passiert alles mit dem Fensterhandle.
Code:
#include <windows.h> /*Um die Win32 API zu nutzen, müssen wir natürlich die Win Headerdatei inkludieren;-)*/
HWND game_hwnd; /*Eine globale Variable, da wir auch aus anderen Funktionen darauf zugreifen müssen.*/
int main( int argc, int *argv[]) {
game_hwnd = FindWindow(NULL, "Hier kommt nun der Fenstertitel hinein, welchen man in der Titelleiste des Fensters ablesen kann");
ShowWindow(game_hwnd, SW_RESTORE);
SetForegroundWindow(game_hwnd);
SetFocus(game_hwnd);
return 0;
}
Schritt 2: Pixel Suche
Nun müssen wir die gewünschten Informationen aus diesem Fenster bekommen.
Wie z.B. die Monsterjagd..
Über den Köpfen der Monster sind Anzeigebalken um die Hp derer zu verdeutlichen.
Jetzt machen wir uns einen Screenshot, und holen uns die Farbinformationen der Stelle, an der die HP-Anzeige ist, mit der Hilfe von Paint oder jemand anderem.
Wir wissen nun, nach welchem Pixel wir suchen müssen, nur wie stellen wir diese Suche nun an?
Der zum Fenster zugehörige Device Context (also die derzeitig angezeigte Bitmap) besteht aus einem Koordinatensystem, wir können deshalb jeden einzelnen Punkt abgehen und nachfragen, ob er der Punkt ist, den wir suchen.
Dazu müssen wir uns erst einmal einen Handle zu dem Device Context des Fensters holen, und zwar mit GetDC.
Auf die Bitmap können wir dann später mit GetPixel, welches uns das abfragen ermöglicht, zugreifen.
Folgender Code holt sich also den Device Context des Spiels, geht in einer Schleife, welche eine zweite Schleife enthält, jeden einzelnen Pixel der Bitmap ab (1024x768), und holt sich den RGB Wert des Pixels und vergleicht ihn mit dem gewünschten Pixel.
Code:
#define PIXEL_R 255 /*Präprozessorvariablen für den RGB Wert des Pixels*/
#define PIXEL_G 0
#define PIXEL_B 0
HDC dc; /*wieder global*/
BOOL find_pixel(void) {
int x, y;
COLORREF pixel;
for(x = 0; x < 1024; x++) {
for(y = 0; y < 768; y++) {
pixel = GetPixel(dc, x, y);
if(GetRValue(pixel) == PIXEL_R && GetGValue(pixel) == PIXEL_G && GetBValue(pixel) == PIXEL_B)
return TRUE; /*ein Pixel mit den gleichen Farbinformationen wurde gefunden*/
}
}
return FALSE; /*ein Pixel mit den gleichen Farbinformationen wurde NICHT gefunden*/
}
Wollen wir den Mauszeiger bewegen, brauchen wir wieder ein paar API Funktionen, doch bevor diese zum Einsatz kommen, brauchen wir Hintergrund Wissen:
Der Bildschirm besteht, unabhängig von der Größe, aus 65.535x65.535 Mickeys.
Wir müssen also die absoluten Bildschirmkoordinaten aus den zum Fenster relativen Pixelkoordinaten errechnen.
Dafür brauchen wir eine kleine Berechnung, die unter anderem unsere Auflösung enthält. (GetSystemMetrics)
Die Maus bewegen wir mit mouse_event und den Koordinaten. Ich habe das jetzt einmal alles in einem kleinen Makro zusammen gefasst.
Code:
#define move(x, y) mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, ((posx + x) * (65535 / GetSystemMetrics(SM_CYSCREEN))), ((posy + y) * (65535 / GetSystemMetrics(SM_CXSCREEN))), 0, 0); mouse_event(MOUSEEVENTF_LEFTDOWN, 0,0,0,0); mouse_event(MOUSEEVENTF_LEFTUP, 0,0,0,0);
Schritt 4: Erweiterung von main()
Nun erwitern wir die Funktion main und holen uns noch ein paar mehr Infos über das Fenster.
Vorerst der Code:
Code:
int main(int argc, char *argv[]) {
int i = 0;
BOOL ismonster, isattacking;
char bar[] = "/-\\|";
coprint(CL_WHITE, "Waiting for Game [ ]");
game_hwnd = NULL;
while(game_hwnd == NULL) {
cuprint(18, 0, "%c", bar[i]);
game_hwnd= FindWindow(NULL, "FensterTitel");
if(++i > 3) i = 0;
Sleep(100);
}
cuprint(0, 2, "");
coprint(CL_YELLOW, "Process found! Bot is initializing...\n");
ShowWindow(game_hwnd, SW_RESTORE);
SetForegroundWindow(game_hwnd);
SetFocus(game_hwnd);
get_dc();
Sleep(1500);
while(TRUE) {
/*MAIN LOOP*/
}
return 0;
}
Wie man sieht, wird nach dem Fenster gesucht, falls es nicht gefunden wird, bleibt das Programm solange in der Schleife, bis es gefunden wird. Außerdem wird, während es nicht gefunden wird, eine nette kleine "Ich.bin.am.arbeiten."-Anzeige gemalt. (mit cuprint s. oben)
Wenn es dann gefunden wird, wird es erst einmal in originaler Größe gezeigt (und in den Vordergrund/Tastaturfokus drauf) und dann werden Informationen über den Device Context des Fensters besorgt. Und zwar über die Funktion get_dc():
Code:
int posx, posy; /*global: x/y Position des Anfangs des Fensters*/
int width, height; /*global: Breite/Höhe des Fensters*/
int get_dc(void) {
RECT prect;
dc = GetDC(game_hwnd);
GetWindowRect(game_hwnd, &prect);
width = prect.right - prect.left; /*Hier müsste man eigentlich noch die Ränder an der Seite subtrahieren*/
height = prect.bottom - prect.top; /*Und hier wird auch die Titelleiste mitgezählt*/
posx = prect.left;
posy = prect.top;
return 0;
}
Schritt 5: Monster, wo?
Nun werden wir also nach einem Monster suchen, und über unser Maus-Makro den Cursor auf es setzen und außerdem noch angreifen.
Code:
int monx, mony; /* Global für andere Funktionen :-)*/
/*Dann noch die Pixel Farb-Präprozessorvariablen*/
BOOL ismonster(void) {
int x, y;
COLORREF pixel;
for(x = 0; x < width; x++) {
for(y = 0; y < height; y++) {
pixel = GetPixel(dc, x, y);
if(GetRValue(pixel) == PIXEL_R && GetGValue(pixel) == PIXEL_G && GetBValue(pixel) == PIXEL_B) {
monx = x;
mony = y;
return TRUE; /*ein Pixel mit den gleichen Farbinformationen wurde gefunden*/
}
}
}
return FALSE;
}
Jetzt können wir noch einbauen, dass man nach dem roten Cursor suchen soll, also dass das Programm guckt, ob man gerade ein Monster angreift, dass geht genauso:
Code:
BOOL isattacking(void) {
int x, y;
COLORREF pixel;
for(x = 0; x < width; x++) {
for(y = 0; y < height; y++) {
pixel = GetPixel(dc, x, y);
if(GetRValue(pixel) == CURSOR_R && GetGValue(pixel) == CURSOR_G && GetBValue(pixel) == CURSOR_B)
return TRUE; /*Wir sind gerade mit einem Monster am kämpfen*/
}
}
return FALSE; /*Wir kämpfen momentan nicht!*/
}
Schritt 6: Items plz!
Will man Items aufheben, oder benutzen, so kann man das mit Hotkeys machen, die man mit keybd_event einfach nur drückt. Man könnte auf diese Weise auf durchgehend die Tasten zum Itemsaufheben schicken, und somit hebt man dann alle Items, die einem begegnen, auf.
Code:
#define pickup(key) keybd_event(key, 0, 0, 0); keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
Code:
int shift(const BOOL mode) {
if(mode) keybd_event(VK_SHIFT, 0, 0, 0);
else keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
return 0;
}
z.B. pickup(VK_F1) oder pickup(VK_CONTROL)
Um alle Tastencodes zu sehen, drücke
.Beachte, dass die Werte in hexadezimal dargestellt sind, also für 'A' nicht pickup(41), sondern pickup(0x41).
Schritt 7: Der Main Loop
In der Hauptschleife wird nun alles geregelt, es wird geguckt, ob ein Monster in der Nähe ist, ob du gerade am Attackieren eines Monsters bist usw. Beim pickup brauchst du eigentlich keine Abfrage für irgendetwas, du kannst es genauso gut spammen.
Ach und, man könnte noch siene HP kontrollieren, wenn sie aus einem Balken besteht.
Du machst einen Screenshot, holst dir die Farbinformationen der Pixel und die Position, wo die Leiste anfängt und aufhört.
Beispiel, Farbe der Pixel von der Leiste: R > 100 und G < 20 (Max HP: 100, Leiste liegt bei y auf 50, und bei x auf 80 + 0(Anfang)-100(Ende)) :
Code:
int HP(void) {
int i, hp = 0;
COLORREF pixel;
for(i = 0; i < 100; i++) {
pixel = GetPixel(dc, 80 + i, 50);
if(GetRValue(pixel) < 100 && GetGValue(pixel) > 20) {
hp = 100 - i;
break;
}
}
return 100 - hp;
}
Code:
while(TRUE) {
ismonster = is_monster();
isattacking = is_attacking ();
if(ismonster && !isattacking) { /*Monster da, und man attackiert gerade keines, also los: Attacke!*/
coprint(CL_GREEN, "Monster on Position %d/%d\n", monx + 25, mony + 50); /*Ach.. die HP Anzeige ist ja über den Köpfen, deswegen addiert man noch etwas dazu, damit der Cursor auch auf das Monster trifft*/
move(monx + 25, mony + 50);
}
else if(!ismonster && !isattacking) { /*kein Monster und nicht am kämpfen? dann kann man ein bisschen suchen, s. etwas weiter unten*/
randmove();
Sleep(1500);
}
pickup(VK_F2);
if(GetAsyncKeyState(VK_F9)) /*Willst du das Programm schließen?*/
exit(0);
Sleep(500);
}
Code:
#define randmove srand(time(NULL)); move(rand()%width, rand()%height);
Dann noch einige wenige Tipps und darunter sind alle Funktionen zusammen gehauen.
1. Wenn sich der Title immer ändert, dann kannst du auch den Klassennamen benutzen. Den findest du z.B. mit Spy++ heraus. Dann also nicht FindWindow(NULL, "FensterTitel"), sondern FindWindow("KlassenName", NULL).
2. Je kleiner die Auflösung und der DC ist, umso schneller werden die Loops in der "Finde.das.Monster"-Funktion, man verzichtet zwar auf gute Grafik und Fullscreen, aber man spürt einen erheblichen Unterschied in der Schnelligkeit.
3. Die Pixel ändern sich je nach Grafikkarte. Du kannst die Pixelfarbinformation aber auch mit > oder < abfragen:
if(GetRValue(pixel) > 95 && GetGValue(pixel) < 30 && GetBValue(pixel) < 20)
4. cuprint und coprint sind Funktionen, die ich mir geschrieben habe, um das setzen des Cursors oder der Farbe in der Konsole zu verändern.
.::CODE::.
Die Code Snippets oben können Fehler enthalten und stimmen mit dem Code hier drunter nicht ganz über ein, das liegt daran, dass ich den ganzen Code noch einmal zusammen gefasst und kompiliert habe.
Die Codes oben sind aus dem Kopf heraus aufgeschrieben worden. Jetzt folgt halt noch einmal der ganze Code.
bot.c
Code:
#include <windows.h>
#include <time.h>
#include <stdio.h>
#include "showmsg.h"
#define move(x, y) mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, ((posx + x) * (65535 / GetSystemMetrics(SM_CYSCREEN))), ((posy + y) * (65535 / GetSystemMetrics(SM_CXSCREEN))), 0, 0); mouse_event(MOUSEEVENTF_LEFTDOWN, 0,0,0,0); mouse_event(MOUSEEVENTF_LEFTUP, 0,0,0,0);
#define pickup(key) keybd_event(key, 0, 0, 0); keybd_event(key, 0, KEYEVENTF_KEYUP, 0);
#define PIXEL_R 255
#define PIXEL_G 0
#define PIXEL_B 0
#define CURSOR_R 0
#define CURSOR_G 255
#define CURSOR_B 255
HWND game_hwnd;
HDC dc;
int posx, posy;
int width, height;
int monx, mony;
int shift(const BOOL mode) {
if(mode) keybd_event(VK_SHIFT, 0, 0, 0);
else keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
return 0;
}
int get_dc(void) {
RECT prect;
dc = GetDC(game_hwnd);
GetWindowRect(game_hwnd, &prect);
width = prect.right - prect.left;
height = prect.bottom - prect.top;
posx = prect.left;
posy = prect.top;
return 0;
}
int HP(void) {
int i, hp = 0;
COLORREF pixel;
for(i = 0; i < 100; i++) {
pixel = GetPixel(dc, 80 + i, 50);
if(GetRValue(pixel) < 100 && GetGValue(pixel) > 20) {
hp = 100 - i;
break;
}
}
return 100 - hp;
}
int randmove(void) {
srand(time(NULL));
move(rand()%width, rand()%height);
return 0;
}
BOOL is_monster(void) {
int x, y;
COLORREF pixel;
for(x = 0; x < width; x++) {
for(y = 0; y < height; y++) {
pixel = GetPixel(dc, x, y);
if(GetRValue(pixel) == PIXEL_R && GetGValue(pixel) == PIXEL_G && GetBValue(pixel) == PIXEL_B) {
monx = x;
mony = y;
return TRUE;
}
}
}
return FALSE;
}
BOOL is_attacking(void) {
int x, y;
COLORREF pixel;
for(x = 0; x < width; x++) {
for(y = 0; y < height; y++) {
pixel = GetPixel(dc, x, y);
if(GetRValue(pixel) == CURSOR_R && GetGValue(pixel) == CURSOR_G && GetBValue(pixel) == CURSOR_B)
return TRUE;
}
}
return FALSE;
}
int main(int argc, char *argv[]) {
int i = 0;
BOOL ismonster, isattacking;
char bar[] = "/-\\|";
coprint(CL_WHITE, "Waiting for Game [ ]");
game_hwnd = NULL;
while(game_hwnd == NULL) {
cuprint(18, 0, "%c", bar[i]);
game_hwnd= FindWindow(NULL, "Fenstertitel");
if(++i > 3) i = 0;
Sleep(100);
}
cuprint(0, 2, "");
coprint(CL_YELLOW, "Process found! Bot is initializing...\n");
ShowWindow(game_hwnd, SW_RESTORE);
SetForegroundWindow(game_hwnd);
SetFocus(game_hwnd);
get_dc();
Sleep(1500);
while(TRUE) {
ismonster = is_monster();
isattacking = is_attacking ();
if(ismonster && !isattacking) {
coprint(CL_GREEN, "Monster on Position %d/%d\n", monx + 25, mony + 50);
move(monx + 25, mony + 50);
}
else if(!ismonster && !isattacking) {
randmove();
Sleep(1500);
}
if(HP() < 30)
pickup(VK_F3);
pickup(VK_F2);
if(GetAsyncKeyState(VK_F9))
exit(0);
}
return 0;
}
showmsg.h
Code:
#define CL_BOLD 0x08 #define CL_BG_BOLD 0x80 #define CL_SPACE " " #define CL_SSPACE " " #define CL_LSPACE " " #define CL_BLACK 0x00 #define CL_BLUE 0x01 #define CL_GREEN 0x02 #define CL_CYAN 0x03 #define CL_RED 0x04 #define CL_MAGENTA 0x05 #define CL_YELLOW 0x06 #define CL_GRAY 0x07 #define CL_BT_BLACK 0x00 | CL_BOLD #define CL_BT_BLUE 0x01 | CL_BOLD #define CL_BT_GREEN 0x02 | CL_BOLD #define CL_BT_CYAN 0x03 | CL_BOLD #define CL_BT_RED 0x04 | CL_BOLD #define CL_BT_MAGENTA 0x05 | CL_BOLD #define CL_BT_YELLOW 0x06 | CL_BOLD #define CL_WHITE 0x07 | CL_BOLD /* CL_BT_GRAY */ #define CL_BG_BLACK 0x00 #define CL_BG_BLUE 0x10 #define CL_BG_GREEN 0x20 #define CL_BG_CYAN 0x30 #define CL_BG_RED 0x40 #define CL_BG_MAGENTA 0x50 #define CL_BG_YELLOW 0x60 #define CL_BG_GRAY 0x70 #define CL_BG_BT_BLACK 0x00 | CL_BG_BOLD #define CL_BG_BT_BLUE 0x10 | CL_BG_BOLD #define CL_BG_BT_GREEN 0x20 | CL_BG_BOLD #define CL_BG_BT_CYAN 0x30 | CL_BG_BOLD #define CL_BG_BT_RED 0x40 | CL_BG_BOLD #define CL_BG_BT_MAGENTA 0x50 | CL_BG_BOLD #define CL_BG_BT_YELLOW 0x60 | CL_BG_BOLD #define CL_BG_WHITE 0x70 | CL_BG_BOLD /* CL_BG_BT_GRAY */ #define cuprint ShowCCursor #define coprint ShowColor int ShowCCursor(const int x, const int y, const char *message, ...); int ShowColor(const WORD color, const char *message, ...);
Code:
int set_cursor(const char *message, va_list ap, const int x, const int y) {
CONSOLE_SCREEN_BUFFER_INFO info;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(handle, &info);
info.dwCursorPosition.X = x;
info.dwCursorPosition.Y = y;
SetConsoleCursorPosition(handle, info.dwCursorPosition);
vprintf(message, ap);
return 0;
}
int set_color(const char *message, va_list ap, const WORD color) {
CONSOLE_SCREEN_BUFFER_INFO info;
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(handle, &info);
info.wAttributes = color;
SetConsoleTextAttribute(handle, info.wAttributes);
vprintf(message, ap);
SetConsoleTextAttribute(handle, CL_GRAY);
return 0;
}
int ShowCCursor(const int x, const int y, const char *message, ...) {
va_list ap;
va_start(ap, message);
set_cursor(message, ap, x, y);
va_end(ap);
return 0;
}
int ShowColor(const WORD color, const char *message, ...) {
va_list ap;
va_start(ap, message);
set_color(message, ap, color);
va_end(ap);
return 0;
}
Es mag sein, dass ich nonsense Müll geschrieben habe und dafür muss ich mich mal eben rechtfertigen
1. Bin ich heute morgen um 8 Uhr ins Bett gegangen.
2. Bin ich heute um 15 Uhr aufgestanden. (Man kann anhand der Differenz der Zeiten nicht sagen, dass ich zu diesem Zeitpunkt hätte wach sein müssen)
3. Trafen 1. und 2. auf die letzte Woche so gut wie immer zu
4. Hör ich laut SOAD und singe mit
...Das lenkt ab O_o
Ich werde diesen Text irgendwann noch einmal auf Fehler überprüfen..
Mir ist gerade spontan die Idee gekommen... nur leider ein wenig spät :?
Wenn möglich bitte einmal durchlesen, und mich auf alle Fehler aufmerksam machen...
Ich wusste während des Schreibens nicht einmal mehr, was ich schreiben wollte






