Reversing a Crackme Writeup 12/31/2019

Got a hold of this reverse me from the new crackme site that was released. It’s called “xordemo”, it was an interesting little crackme I reversed up and wanted to give a write up on.

Upon opening up xordemo inside of IDA you’ll noticed its an executable using the ELF64 format (AKA it runs on UNIX). I personally do not use Linux, and I didn’t want to go through the hassle of installing Linux, as I mostly just reverse to learn and toy around, so I just reversed it, documenting how it worked, and also writing my own program that handles decrypting.

Anyways, you’ll notice the main function is accessing program launch flags

.text:0000000000000825                 push    rbp
.text:0000000000000826                 mov     rbp, rsp
.text:0000000000000829                 sub     rsp, 20h
.text:000000000000082D                 mov     [rbp+var_14], edi
.text:0000000000000830                 mov     [rbp+var_20], rsi
.text:0000000000000834                 cmp     [rbp+var_14], 2 ; launch flags get accessed here and are compared to 2
.text:0000000000000838                 jz      short loc_84D
.text:000000000000083A                 lea     rdi, s          ; "Need exactly one argument."
.text:0000000000000841                 call    _puts
.text:0000000000000846                 mov     eax, 0FFFFFFFFh
.text:000000000000084B                 jmp     short locret_88D

Program flags get stored in var_14 which is accessed using the frame pointer, not surprising as the frame pointer has access to local variables and function parameters (in this main function).

Further down the start function you’ll see references to a subroutine

.text:000000000000084D loc_84D:                                ; CODE XREF: main+13j
.text:000000000000084D                 mov     rax, [rbp+var_20]
.text:0000000000000851                 add     rax, 8
.text:0000000000000855                 mov     rax, [rax]
.text:0000000000000858                 mov     rdi, rax
.text:000000000000085B                 call    key_encryptor
.text:0000000000000860                 mov     [rbp+var_1], al
.text:0000000000000863                 cmp     [rbp+var_1], 0
.text:0000000000000867                 jz      short loc_87C
.text:0000000000000869                 lea     rdi, aJackpot   ; "Jackpot"

I renamed the subroutine to key_encryptor, but you notice the string "jackpot" is referenced right after the call to this certain subroutine. Using basic logic and the process of elimination, its obvious that that this function has some sort of play with the key itself. Going further down into it key_encryptor, you’ll notice there is a string, "1feebdab", and an array of byte code is being stored in the rax register:

.text:0000000000000756                 mov     rax, '1feebdab' ; the string that gets encrypted
.text:0000000000000760                 mov     qword ptr [rbp+s], rax
.text:0000000000000764                 mov     [rbp+var_2E], 32h
.text:000000000000076A                 mov     rax, 10 14 0A 05 0C 17 0A 02 ; byte code that is store

"1feebdab" gets reversed later on due to endianness on x86, the littlest endian gets put up front, while the most significant gets put in the back, so I’ll just refer to it as "badbeef1", which is what it truly is, anyways.

Later on in the program there is this big chunk of disassembly:

.text:0000000000000787 loc_787:                                ; CODE XREF: key_encryptor+8Dj
.text:0000000000000787                 mov     eax, [rbp+var_3C]
.text:000000000000078A                 cdqe
.text:000000000000078C                 movzx   ecx, [rbp+rax+s]
.text:0000000000000791                 mov     eax, [rbp+var_3C]
.text:0000000000000794                 movsxd  rdx, eax
.text:0000000000000797                 mov     rax, [rbp+var_48]
.text:000000000000079B                 add     rax, rdx
.text:000000000000079E                 movzx   eax, byte ptr [rax]
.text:00000000000007A1                 xor     ecx, eax
.text:00000000000007A3                 mov     edx, ecx
.text:00000000000007A5                 mov     eax, [rbp+var_3C]
.text:00000000000007A8                 cdqe
.text:00000000000007AA                 mov     [rbp+rax+s1], dl
.text:00000000000007AE                 add     [rbp+var_3C], 1
.text:00000000000007B2
.text:00000000000007B2 loc_7B2:                                ; CODE XREF: key_encryptor+4Bj
.text:00000000000007B2                 mov     eax, [rbp+var_3C]
.text:00000000000007B5                 movsxd  rbx, eax
.text:00000000000007B8                 lea     rax, [rbp+s]
.text:00000000000007BC                 mov     rdi, rax        ; s
.text:00000000000007BF                 call    _strlen
.text:00000000000007C4                 cmp     rbx, rax
.text:00000000000007C7                 jbe     short loc_787

This whole chunk loops over string, and xor’s each character at X by bytecode at X which is iterated over. For better understanding:

b gets XOR’d by 0x10 a XOR 0x14 d XOR 0x0A b XOR 0x05 e XOR 0x0C e XOR 0x17 f XOR 0x0A 1 XOR 0x02

You’ll notice on this line

movzx   eax, byte ptr [rax]

the current indexed byte code gets stored in eax, which then gets XOR’d by ecx, which is the current character in the string,

movzx   ecx, [rbp+rax+s] ; brackets signify a dereference

Where the XOR’ing is happening:

.text:000000000000079E movzx eax, byte ptr [rax]
.text:00000000000007A1 xor ecx, eax
.text:00000000000007A3 mov edx, ecx

Anyways, despite me not being able to run the program on Windows, I decided to write my own little program which does all the work for you:

std::vector do_xor()
{
	// const char* base_key = "1feebdab";
	// the string gets reversed based on the assembly shown,
	// so it actually is:
	const char* base_key = "badbeef1";
	std::uint8_t byte_code[] =
	{ 0x10, 0x14, 0x0A, 0x05, 0x0C, 0x17, 0x0A, 0x02 };
	const std::int32_t size = std::strlen(base_key);
	std::vector data;

	for (std::int32_t i = 0; i < size; i++)
	{
		std::uint8_t xor_value = base_key[i] ^ byte_code[i];
		data.push_back(xor_value);
	}

	return data;
}

This (very bad) code will return the data back into an std::vector, which you can use to then print the key

Reversing a simple CTF, tips through conquering CTFs

Hello people, I decided to get back in to CTF competitions, and the one I’m currently competing in has a great reverse me thats perfect for anyone who wants to learn how to reverse.

First off, the program is ELF, which means it’s most likely compiled on Linux, this isn’t always true however. as ELF is on ALL Unix OS’s. If you’d like to learn more about ELF, there is a great article on Wikipedia (https://en.wikipedia.org/wiki/Executable_and_Linkable_Format).

So first off, I don’t know the rules against supplying the file, so I’m going to leave it to me, if you’d like it. Please, PM me, or comment. However, the file is called “Time”, the whole point is to capture a flag, normally looking like something similar to this flag{SOME_stup1d_L33T_c0d3}, some flags are easy as pie to get, others can be extremely difficult. The one I did was relatively easy, taking me about 5 – 10 minutes to solve, which is extremely short compared to most flags you find.

Before we begin though, if you plan on doing CTFs I’d like to supply a few tips you can keep with you, stuff I realized has helped me.

Tip 1: When in doubt, GOOGLE, this applies to weird memory addresses, encryption method, bitwise operations, and much more.
Tip 2: Don’t be scared to mess around, whether you’re going to patch the file, mess with the encryption, or just straight bruteforce it. Remember, the people who created the CTF can make mistakes to!
Tip 3 (last one): CTFs are no easy task, but they are extremely fun and you’ll get the best feeling once you solve one. Its like a puzzle, with many many pieces, and some pieces you can just 100% leave out. 😉

Alright, finally, we can now begin. Once I opened up the file in IDA, I immediately went into strings, and searched "flag", this is a common string in most CTF reverse mes. I found multiple. The most interesting being "Flag is". If reversed correctly, we can get the flag. So I xref’ed that string, and found 1 occurrence inside a function. I went to the function, and saw where it was at.

void __noreturn sub_8514()
{
  int v0; // [sp+0h] [bp-Ch]@1
  int i; // [sp+4h] [bp-8h]@2

  puts("Enter your Key");
  _isoc99_scanf("%ld", &v0);
  if ( dword_108D0 - dword_108DC == v0 )
  {
    puts("Seems you guessed it.");
    printf("Flag is ");
    for ( i = 0; i <= 33; ++i )
      putchar(dword_1084C[i] ^ 7);
    exit(0);
  }
  puts("Flag check failed");
  exit(-1);
}

You can clearly see stuff, that looks very odd. I’ll try to explain this as thoroughly as possible, from top, to bottom. First off, puts is a C/C++ function that prints to the console, sounds easy. _isoc99_scanf, is a C99 function, its equivalent to C99, and you might be wondering why scanf wasn’t used. I’m pretty sure its because %ld isn’t available in C standards. So _isoc99_scanf was used. Correct me if I’m wrong. That comparison is comparing if the key is correct, how do I know this? Because inside of the if statement, you see it printing out the flag, which is dword_1084C[i]. The rest is pretty self explanatory. So whats a summary of the flow? A short and sweet one is that this function checks your input, if wrong it prints Flag check failed and if right it loops over 33 chars (the chars of the string), then it sets the key to equal to each xor’d iteration by 7
(https://www.cprogramming.com/tutorial/xor.html).

This is pretty easy, now you have to rebuild the function. To speed things up, its pretty straightforward, make a function that does the same exact thing, then prints out the buffer (dword_104C). Here is my implementation (C++):

#include <iostream>
#include <cstdint>
	
#define MAX_KEY_LENGTH 33
	
void key(int32_t xor_base)
{
	char buf[MAX_KEY_LENGTH] = 
	{
		'n', 'i', 'd', 's', 'a', '|', 'S', 'o', '6', 't', 'X',
		'&', 't', 'X', 'm', 'R', 't', 's', 'X', 's', 'o', '4',
		'X', 'E', '4', '`', 'n', 'i', 'I', 'n', 'i', '`', 'z'
	};
	
	for (int32_t i = 0; i <= MAX_KEY_LENGTH; i++)
	{
		buf[i] = buf[i] ^ xor_base;
		std::printf("%i", buf[i]);
	}

	std::printf("\nFlag is: ");
	std::printf("\n");
	std::printf("%s", buf);
}	
	
int32_t main(int32_t argc, char* argv[])
{
	key(7);

	system("pause");
}

Where buf was the array of chars IDA recognized.
When compiled you’ll see that this prints out

10511099116102123841044911595331159510685115116951161045195665110310511078105110103125-53
Flag is:
inctf{Th1s_!s_jUst_th3_B3ginNing}╦╠╠╠╠╠╠Xrm╪·»

Thank you, I hope you learnt something. Please, comment what you thought of this blog post!

ZDoom Reversing Part 1

Hello everyone!

Thanks for coming back to my blog, I’ve been struggling with a bit of family issues, so I couldn’t post anything.

However, I’ve been reverse engineering ZDoom. ZDoom is a source port of DOOM, it mostly adds support for other games build by iD Software.

Alright, let’s start.

First off, we must know ZDoom is an x86 exe file. As I said before in my previous blog posts, I prefer to reverse x86 executables due to there being MS Detours support, and reversing x86 executables just tends to be easier, and more coder friendly. Also, before we continue. I would like to say that I reversed this game without looking at the source code whatsoever, and I really encourage you guys to do the same.

Anyways, I started off with trying to find the vtable ptr for player_t in ZDoom.

This was a relatively easy task, and I’ll explain how I managed to complete this task.

First off, open up the executable inside IDA, and let it disassemble. We’re going to use some logic here, whats a good string idea we could search up to find the vtable ptr to player_t?

Well, we could search up health…

Once you do that, after some playing around and xrefing the string “Health” (there is multiple), then you’ll find a function called

unsigned int __userpurge sub_4B2010

If you go through it and find the place where the string was referenced, you’ll see this

            if ( (unsigned __int8)sub_49C090("Health") )
            {
              sub_4A1070(v11);
              *(_DWORD *)(a2 + 188) = *(_DWORD *)&dword_64F2BC;
              goto LABEL_120;
            }

You’ll notice something…

a2 + 188

This right here means that a2 is definitely a struct, you can see the addition sign, which means its that far of an offset off a2. Please, name the struct something like APlayer, where 188 is the players health. Thats what I’m going to assume. For those who do not know how to create a struct in IDA, right click on the var / arg, and click on “Create new struct type”, and rename the struct to APlayer. If you do not see “Create new struct type” then go get this IDA plugin https://github.com/REhints/HexRaysCodeXplorer

Anyways, the code becomes much easier to read. We can now see that there is a struct and fields for the struct!

Lets go ahead and rename that field that was equal to

*(_DWORD *)&dword_64F2BC

to player. I can only assume this. I do not know if that truly is player, but for anyone reading this, how about you try to find out yourself as a little fun task? Reverse engineering is all about trial and error.

Anyways, go to the IDA Disassembly view, and look at the address

.text:004B23D7                             mov     [edi+0BCh], edx

I’d like to pause here, and let you think of what we should do next. If you do not know, don’t worry 🙂

Ready? Good, so what we’re going to do is find out what edi is, and to do that, we’re going to breakpoint on address 004B23D7 in x32dbg,/ OllyDbg / whatever, and see what happens. We can then find out what edi is by looking in the registers box. Lets give it a shot eh?

Go into x32dbg and set a breakpoint at that address with F2.

Play around in the game until it gets hit, and bam, go into the registers box and take edi. There is the address, then after that, you can go into Reclass, and reverse the player struct! Thanks! 🙂

Well… I kinda broke Quake 3: Arena

Hello guys, and girls! Welcome back

I did actually end up achieving what I wanted to, and that was to break Quake 3. I did this by overwriting convars in the game. And actually reversed an entire set of convars + a class that can contain these convars!

So lets get to what I did, and how it works, shall we? First off, go open up Quake 3 Arena in IDA and go ahead and generate the list of strings. Done? Good, now we’re going to be a little newbie here, and search up on google, list of convars in quake, but hey, who said using resources is newbie?

Once you’ve done that, find a convar, some of them were not implemented into quake 3, but do not worry. For me, I searched up cl_showSend, xref that string. And you should get a couple, to one result (hopefully one).

swag

You’ll notice this is a push. That means its getting pushed onto the stack. Probably meaning its getting pushed into a functions arguments. Thats how I remember it.

Now, we’re going to use common sense here, only 1 result for a string thats a convar? And its taken as arguments for a function? Whats this mean? Well, this means that function is probably a RegisterCVar function. I named mine CV_RegisterCVar. But you can name it what you’d like

Okay, so, lets get back to it.

Now, theres something VERY special about this function. It moves the eax register into B3A42C

This is great! IDA literally just gave us the address.

Throw that into reclass, and make a pointer @ B3A42C, then in that pointer, add about 2048 bytes, until you see a string called “r_showNormals”

This is actually a great way to make a simple wallhack! We’re going to show normals in the game, then be able to see enemies, and objects alike, through walls! 😀

Alright, we ran into a problem though. We don’t know what value to set. We’ve kinda messed up. What value do we set to change the value of that convar?

Okay, so if you go back into IDA. Compare that

.text:0040F020                 push    offset a0       ; "0"

with whats in reclass. See a zero? Good, you can compare other convars to. You’ll probably see lots of zeroes but don’t worry. We got this. Okay, so the one right before the next pointer is the convar. Value, I’ll show you what I mean.

412321

Alright, sweet, we found the value. How did I know this? Well because I compared multiple values in IDA versus the value in reclass. Sometimes you have to reload your map for convars to actually change, so if it doesn’t change automatically, try that. Also, float values are the second one before the next pointer.

Thanks guys!! 🙂

Quake 3: Arena Reversing

Hey guys, I’m back again with another blog post. This time, on Quake 3 Arena. I recently got back into reverse engineering. And decided to start off with a fun classic video game, known as Quake 3: Arena. Quake 3: Arena is a multiplayer online shooter. Created by id Games, it still is a popular game. It really stands the test of time!

Anyways, lets get to reversing. When I reverse. I like to pop the program open and just get straight to hacking away. I went into my quake console, and looked for an interesting string. Ahaha, found one! It says: WARNING: could not find %s - using default, where %s is actually a string for the sound it couldn’t find. Anyways, this is probably apart of some register sound function, quite obvious (and it is). I like to reverse without reading any bit of source. So, lets get straight to it! X-Ref that string, and you should come across three results. After close inspection of all three, you’ll notice the second one has something, odd. It looks like its being pushed into a printf function! Yes! Quake 3, has their own printf function for the console

 
LABEL_33:
  Con_PrintF(a3warningCouldN, (_BYTE)v8 + 0x18);
  return 0;

Here is how its being called. I have good knowledge of quake, so I know this is called Con_PrintF. Lets go ahead and name it that. You’ll also notice something very strange and odd in this call. Theres something called (_BYTE)v8 + 0x18), what is this?!?! Its actually, an offset of a value. So that gives us an exact idea as to what v8 is. Its a variable of type struct!! 🙂

The reason I say struct, is because there are classes in C++. And we know that. There are indeed, classes in C++, but not C.

Lets get back to coding. We can rename v8 to something like, sound_ptr because we know that prints out WARNING: could not find 'something.wav' - using default, and this is a sound. So, if you right click on that variable you’ll see something called reconstruct type (don’t panic if you don’t). If you do not see reconstruct type, you’re missing the plugin. You can find it over at: https://github.com/REhints/HexRaysCodeXplorer

Okay, once you have dl’ed that, you can now reconstruct the type. We can reconstruct the type of that variable, to some struct. In my case, I called the struct sound_t.

Lets call the function register sound. So your IDA pseudo code should look like:

int __cdecl S_RegisterSound(const char *a1)

Okay, so, now we can get to reversing and the juicy part!!

Once you reconstructed the type, you should get a new type called sound_t. IDA auto-detects the size of it. Goto the structures tab, and we should see the struct there.

00000000 sound_t           struc ; (sizeof=0x60, mappedto_68)
00000000 field_0         dd ?
00000004 bool_use        dd ?
00000008 field_8         dd ?
0000000C field_12        dd ?
00000010                 db ? ; undefined
00000011                 db ? ; undefined
00000012                 db ? ; undefined
00000013                 db ? ; undefined
00000014                 db ? ; undefined
00000015                 db ? ; undefined
00000016                 db ? ; undefined
00000017                 db ? ; undefined
00000018 sound_name      dd ?                    ; offset
0000001C                 db ? ; undefined
0000001D                 db ? ; undefined
0000001E                 db ? ; undefined
0000001F                 db ? ; undefined
00000020                 db ? ; undefined
00000021                 db ? ; undefined
00000022                 db ? ; undefined
00000023                 db ? ; undefined
00000024                 db ? ; undefined
00000025                 db ? ; undefined
00000026                 db ? ; undefined
00000027                 db ? ; undefined
00000028                 db ? ; undefined
00000029                 db ? ; undefined
0000002A                 db ? ; undefined
0000002B                 db ? ; undefined
0000002C                 db ? ; undefined
0000002D                 db ? ; undefined
0000002E                 db ? ; undefined
0000002F                 db ? ; undefined
00000030                 db ? ; undefined
00000031                 db ? ; undefined
00000032                 db ? ; undefined
00000033                 db ? ; undefined
00000034                 db ? ; undefined
00000035                 db ? ; undefined
00000036                 db ? ; undefined
00000037                 db ? ; undefined
00000038                 db ? ; undefined
00000039                 db ? ; undefined
0000003A                 db ? ; undefined
0000003B                 db ? ; undefined
0000003C                 db ? ; undefined
0000003D                 db ? ; undefined
0000003E                 db ? ; undefined
0000003F                 db ? ; undefined
00000040                 db ? ; undefined
00000041                 db ? ; undefined
00000042                 db ? ; undefined
00000043                 db ? ; undefined
00000044                 db ? ; undefined
00000045                 db ? ; undefined
00000046                 db ? ; undefined
00000047                 db ? ; undefined
00000048                 db ? ; undefined
00000049                 db ? ; undefined
0000004A                 db ? ; undefined
0000004B                 db ? ; undefined
0000004C                 db ? ; undefined
0000004D                 db ? ; undefined
0000004E                 db ? ; undefined
0000004F                 db ? ; undefined
00000050                 db ? ; undefined
00000051                 db ? ; undefined
00000052                 db ? ; undefined
00000053                 db ? ; undefined
00000054                 db ? ; undefined
00000055                 db ? ; undefined
00000056                 db ? ; undefined
00000057                 db ? ; undefined
00000058                 db ? ; undefined
00000059                 db ? ; undefined
0000005A                 db ? ; undefined
0000005B                 db ? ; undefined
0000005C field_92        dd ?
00000060 sound_t           ends

Heres what mine looks like. WOW a big struct for only 5 members. I already reversed sound_name, and we’ll get to that later. Now you might be asking, why is it 0x60 bytes long? Well, its 0x60 bytes long because field_92 is actually a type of another struct! I haven’t reversed this yet. But that is indeed why.

So, we can get back into IDA pseudo code view and rename this part of that function

strcpy((char *)&sound_to_play->sound_name, a1);

And set the name to sound_t name. And make sure to set that type to char* aswell

You can now hook this function using detours. And then call sounds via it! Great! You have now seen what IDA can do in this blog post. Now, go do something 🙂

Thanks! 🙂

Cube 2: Sauerbraten Reverse Engineering

Hello guys, welcome to my blog post about Cube 2: Sauerbraten reversing. I’ve recently been hacking this game, and would like to post the process of what I did. Lets get to it shall we?

First off, I fixed the game to run in x86 instead of x64. Which is a lot easier to reverse engineer, at least in my opinion due to the lesser hexadecimals and less instructions. To do this, we go into the .bat file, edit it, and paste the following code in:

@ECHO OFF

set SAUER_BIN=bin

IF /I "%PROCESSOR_ARCHITECTURE%" == "amd64" (
    set SAUER_BIN=bin
)
IF /I "%PROCESSOR_ARCHITEW6432%" == "amd64" (
    set SAUER_BIN=bin
)

start %SAUER_BIN%\sauerbraten.exe "-q$HOME\My Games\Sauerbraten" -glog.txt %*

So, when you open it up in IDA, go into the imports tab and we’re going to reverse a simple render loop. So, you should see it uses OpenGL and SDL, this is great! IDA’s already recognized that it uses these 2 libraries, now, a basic render loop in a video game normally has SDL_GL_SwapBuffers, which swaps the OpenGL framebuffers. So, lets quit out what we have open inside of IDA, and now we’ll open up bin/SDL.dll, go into the functions window and search up in the functions window, SDL_GL_SwapBuffers. Once you have found it, we can now make a simple render loop. Make a function pointer, here is mine:

typedef void(__cdecl* tSDL_SwapBuffers)();
tSDL_SwapBuffers oSDL_SwapBuffers;

Now, make a function called hkSDL_SwapBuffers, or whatever you’d like, and just have it CALL the function pointer, which is oSDL_SwapBuffers. Here is an example

void hkSDL_SwapBuffers()
{
	std::cout << "Hello" << std::endl;
	oSDL_SwapBuffers();
}

The reason for the std::cout << "Hello" << std::endl; is because it should spam this in console if we've successfully hooked the function. Now, call hkSDL_SwapBuffers in your DllMain and when you inject the x86 DLL you should see it spam "Hello" in console. This is a good thing! It means we've successfully hooked the function, now, lets get to calling a function like DrawString. Which will draw a string to the screen, which can be useful. So, go back into sauerbraten.exe in IDA and go into strings. Search up something that is being drawn to the screen. In my case, I searched up "SPECTATOR" and xrefed it to this function, sub_B2970. Also, if you haven't rebased your program, please do so, rebase it to 0x0, this will make it so we don't have RVA's (relative virtual addresses). Now, after that, we can see how sub_B2970 is calling DrawString. You can also make a function pointer for this, but I did it the "bad" way to learn x86 assembly more. Of course, you could just make a function pointer, but the whole process of programming is to try new things and learn, right? Here is what I did, compare it to the way its being called in disassembly mode inside of IDA:

drawStringAddy = (uintptr_t)(GetModuleHandleW(0)) + 0x00107750;
void DrawString(char* msg, int x, int y, int red, int green, int blue, int alpha, int format, int format2)
{
	__asm
	{
		push format2;
		push format;
		push alpha;
		push blue;
		push green;
		push red;
		push y;
		mov ecx, msg;
		push x;
		push ecx;
		call drawStringAddy; // call the function
		add esp, 0x24; // clean up stack
	}
}

Now, you can call that inside of you render loop, before you call the original function, and you should have something draw to the screen :D.

Okay, now that concludes my blog post for today, I’ll be posting quite a bit more later. Thanks for reading!! 😀

PS: Heres a nice player class I reversed in ReClass 😉

// Generated using ReClass 2016

#include "Vector.h" // you should already have a vector class, if you do not, go get one off the valves source sdk or smthing lol

class BasePlayer;
class Player;

class BasePlayer
{
public:
	Player* ply; //0x0000 
	char pad_0x0004[0x8A4]; //0x0004

}; //Size=0x08A8

class Player
{
public:
	Vector3 plyPos; //0x0000 
	char pad_0x000C[0x8]; //0x000C
	__int32 plyMode; //0x0014 not sure about this 
	char pad_0x0018[0x8]; //0x0018
	float plyJumpPow; //0x0020 
	char pad_0x0024[0xC]; //0x0024
	Vector3 plyPos_2; //0x0030 
	Vector2 plyViewAngles; //0x003C 
	char pad_0x0044[0x4]; //0x0044
	float plySpeed; //0x0048 
	__int32 plyJumpHeight; //0x004C 
	char pad_0x0050[0x28]; //0x0050
	__int32 UNK15; //0x0078 
	char pad_0x007C[0x2C]; //0x007C
	__int32 UNK14; //0x00A8 
	char pad_0x00AC[0x8]; //0x00AC
	float plyWalkMode; //0x00B4 
	char pad_0x00B8[0x8]; //0x00B8
	__int32 UNK13; //0x00C0 
	char pad_0x00C4[0x1C]; //0x00C4
	__int32 UNK18; //0x00E0 
	__int32 UNK11; //0x00E4 
	char pad_0x00E8[0x4]; //0x00E8
	float UNK12; //0x00EC 
	char pad_0x00F0[0x4]; //0x00F0
	__int32 UNK10; //0x00F4 
	__int32 UNK9; //0x00F8 
	__int32 UNK8; //0x00FC 
	char pad_0x0100[0x4]; //0x0100
	float UNK7; //0x0104 
	char pad_0x0108[0x28]; //0x0108
	__int32 UNK4; //0x0130 
	__int32 UNK5; //0x0134 
	char pad_0x0138[0x20]; //0x0138
	__int32 plyTicksPassed; //0x0158 possibly ticks?
	__int32 plyHealth; //0x015C 
	__int32 plyMaxHealth; //0x0160 
	__int32 plyArmor; //0x0164 
	char pad_0x0168[0x8]; //0x0168
	__int32 plyWeaponSlot; //0x0170 
	char pad_0x0174[0x4]; //0x0174
	__int32 UNK1; //0x0178 
	__int32 plyGrenades; //0x017C 
	char pad_0x0180[0x10]; //0x0180
	__int32 plyPistolRounds; //0x0190 
	char pad_0x0194[0x1C]; //0x0194
	__int32 UNK6; //0x01B0 
	char pad_0x01B4[0x38]; //0x01B4
	__int32 UNK2; //0x01EC 
	__int32 UNK3; //0x01F0 
	char pad_0x01F4[0x3C4]; //0x01F4
	__int32 UNK16; //0x05B8 
	char pad_0x05BC[0x1390]; //0x05BC
	__int32 UNK17; //0x194C 
	char pad_0x1950[0x8B4]; //0x1950
	__int32 inkyMaskCol; //0x2204 
	char pad_0x2208[0x98]; //0x2208
	__int32 inkyWingsMaskCol; //0x22A0 
	char pad_0x22A4[0x570]; //0x22A4

}; //Size=0x2814

PLAYING WITH QUAKE, A JOURNEY THROUGH REVERSING (PART TWO)

Hello! Welcome back to this series. Haven’t done much, however I managed to do some teleportation in Quake. So, plyPos (player position) is at the 0AD4 offset of the player pointer. Its a Vector3 type. So go there. Good, now in Visual Studio, make a DLL project, or whatever IDE you use. And copy the following code in.

#include <iostream>
#include <Windows.h>
#include "SDK.h"
#include "Vector.h"
 
void Init()
{ 
	AllocConsole(); // allocate a console

	freopen("CONIN$", "r", stdin); // open the stdin pipe (input)
	freopen("CONOUT$", "w", stdout); // open the stdout pipe (output)

	SetConsoleTitle("Quake"); // set the console window name, in our case its Quake

	base = reinterpret_cast<Quake::Base*>(0x00428C24); // set base = 0x00428C24 (base player pointer)

	std::cout << base << std::endl; // cout base, only did this to see if its right

	while (true)
	{
		std::cout << base->ply->plyPos.y << std::endl;
		base->ply->plyPos.y = FLT_MAX; // set plyPos.y to maximum float.
	}
}

BOOL WINAPI DllMain(HINSTANCE hInst, DWORD fdwReason, LPVOID lpVoid)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		CreateThread(0, 0, (LPTHREAD_START_ROUTINE)Init, 0, 0, 0);
		break;
	}

	return TRUE;
}

Vector3 class is something you gotta make yourself, you can just use the source-sdk-2013 one which is public (what Valve uses). As for the classes for the game, here is something you can use.

#include "Vector.h"

class Player;
class Model;
class Base;
class Map;
class TextBase;
class ConCommand;
class Console;
class ConsoleManipulation;

namespace Quake
{
	class Player
	{
	public:
		__int32 plyHealth; //0x0000 
		char pad_0x0004[0x8]; //0x0004
		__int32 plyCurAmmo; //0x000C 
		char pad_0x0010[0x8]; //0x0010
		__int32 plyShotgunShells; //0x0018 
		__int32 plyNumNails; //0x001C 
		char pad_0x0020[0x8]; //0x0020
		__int32 plyWeaponSlot; //0x0028 probably weapon slot, no idea  
		char pad_0x002C[0x340]; //0x002C
		Model* plyPlayerMdl; //0x036C 
		Model* plyEyeMdl; //0x0370 
		Model* plyHPlayerMdl; //0x0374 
		Model* plyGib1Mdl; //0x0378 
		Model* plyGib2Mdl; //0x037C 
		Model* plyGib3Mdl; //0x0380 
		Model* plyBubbleMdl; //0x0384 
		Model* plyExplodMdl; //0x0388 
		Model* plyAxeMdl; //0x038C 
		Model* plyShotMdl; //0x0390 
		Model* plyNailMdl; //0x0394 
		Model* plyRockMdl; //0x0398 
		Model* plyShot2Mdl; //0x039C 
		Model* plyNail2Mdl; //0x03A0 
		Model* plyRock2Mdl; //0x03A4 
		Model* plyBoltMdl; //0x03A8 
		Model* plyBolt2Mdl; //0x03AC 
		Model* plyBolt3Mdl; //0x03B0 
		Model* plyLavaballMdl; //0x03B4 
		Model* plyMissleMdl; //0x03B8 
		Model* plyGrenadeMdl; //0x03BC 
		Model* plySpikeMdl; //0x03C0 
		Model* plySSpikeMdl; //0x03C4 
		Model* plyBackPackMdl; //0x03C8 
		Model* plyZombieGibMdl; //0x03CC 
		Model* plyLightMdl; //0x03D0 
		Map* plyBH25Bsp; //0x03D4 
		Model* plyArmorMdl; //0x03D8 
		Map* plyEXBOX2Bsp; //0x03DC 
		Map* plyBSHellBsp; //0x03E0 
		Map* plyNail1Bsp; //0x03E4 
		Map* plyNail0Bsp; //0x03E8 
		Map* plyRock1Bsp; //0x03EC 
		Map* plyBSHell1Bsp; //0x03F0 
		Map* plyBBH10Bsp; //0x03F4 
		Map* plyBBH100Bsp; //0x03F8 
		Model* plyGShotMdl; //0x03FC 
		Model* plyGRockMdl; //0x0400 
		Model* plYBGKeyMdl; //0x0404 
		Model* plyEnforcerMdl; //0x0408 
		Model* plyHMegaMdl; //0x040C 
		Model* plyLaserMdl; //0x0410 
		Model* plySoldierMdl; //0x0414 
		Model* plyHGuardMdl; //0x0418 
		Model* plyHDogMdl; //0x041C 
		Model* plyDogMdl; //0x0420 
		Model* plyGNailMdl; //0x0424 
		Model* plySuitMdl; //0x0428 
		Model* plyInvulnerableMdl; //0x042C 
		Model* plyQuadDamageMdl; //0x0430 
		Model* plyBSKeyMdl; //0x0434 
		Model* plyInvisibleMdl; //0x0438 
		Map* plyBHB10Bsp; //0x043C 
		Map* plyBNail1Bsp; //0x0440 
		char pad_0x0444[0x628]; //0x0444
		char* plyCurMap; //0x0A6C 
		char pad_0x0A70[0x64]; //0x0A70
		Vector3 plyPos; //0x0AD4 players pos.
		char pad_0x0AE0[0x18]; //0x0AE0
		Vector2 plyAngle; //0x0AF8 players view angles. only in 2d. 
		char pad_0x0B00[0x474]; //0x0B00
		Model* plyFlameMdl; //0x0F74 
		char pad_0x0F78[0x800]; //0x0F78

	}; //Size=0x1778

	class Model
	{
	public:
		char pad_0x0000[0x440]; //0x0000

	}; //Size=0x0440

	class Base
	{
	public:
		Player* ply; //0x0000 
		char pad_0x0004[0x3C]; //0x0004

	}; //Size=0x0040

	class Map
	{
	public:
		char pad_0x0000[0x40]; //0x0000

	}; //Size=0x0040

	class TextBase
	{
	public:
		char pad_0x0000[0x10C]; //0x0000
		Console* console; //0x010C 
		char pad_0x0110[0x7A0]; //0x0110

	}; //Size=0x08B0

	class ConCommand
	{
	public:
		char pad_0x0000[0x1810]; //0x0000

	}; //Size=0x1810

	class Console
	{
	public:
		char pad_0x0000[0x10]; //0x0000
		ConsoleManipulation* consoleManipulation; //0x0010 
		char pad_0x0014[0x20]; //0x0014

	}; //Size=0x0034

	class ConsoleManipulation
	{
	public:
		ConCommand* conCmd; //0x0000 
		char pad_0x0004[0x3C]; //0x0004

	}; //Size=0x004
}

Quake::Base* base;

If you don’t have Vector3 or Vector3. Just go ahead and print health ;).

If you don’t know how to print health. Change these lines

	while (true)
	{
		std::cout << base->ply->plyPos.y << std::endl;
		base->ply->plyPos.y = FLT_MAX; // set plyPos.y to maximum float.
	}

to

	while (true)
	{
		std::cout << base->ply->plyHealth << std::endl;
	}

Thanks for reading! Happy hacking. More blogs to come later 🙂