Jump to content

Introduction to modding/hacking


peterthepigeon

Recommended Posts

So you want to mod/hack C&C95, or perhaps RA1, or another game?

 

You've been studying those C/C++ tutorials and you think you have a pretty good grasp of the material right?  You know how to overload operators, why binary_search is so cool, making use of templated classes, and those pesky pointers and references.  All of that is second nature to you right?  I also hope you're at least familiar with algebra.

 

However, you have no clue where to start.  Well this tutorial is designed to get your feet wet.

 

First I must stress that before we start disassembling streams of instructions, whacking those modrms and sibs into submission, relocating code blocks, enlarging sections, prepending prefixes we have to start with a basic understanding of terminology.

 

We'll start with the lowly bit.

 

A bit is defined as a single number, either 1 or 0.  The basis of binary.

 

Bitwise operations operate on bits obviously.  Remember your bitwise operators from C/C++?  I hope so.  Because we're going to need them when extracting registers from the modrm/sib.  Among other things including the scaling and what type of operation it is, whether it's a register or memory operation.  Hence the name MODRM and SIB ( scaled index byte )

 

Moving on we move to the next division, called the nibble.

 

A nibble is defined as 4 bits.  Each additional bit allows us to represent more numbers.

 

So whereas a single bit represents 1 number, 0 or 1, a nibble can represent 16 numbers, specifically 0-15.  Remember, when we're in computer land we always start at 0.

 

So for instance:

 

0101, represents the decimal number 5.  Why?

 

Because converting from binary to decimal requires us to represent each place as a power of 2.

 

8 4 2 1

 

0 1 0 1

 

Clear I hope.

 

Next, the absolute lowest level division in terms of memory/code representation computers is the byte.  Granted using bitwise operations we can manipulate the nibbles and individual bits, but that's for another time.  This is the smallest representation in a modern computer.  Think those bools in your C/C++ program are bits?  Wrong, they're actually bytes.

 

A byte is composed of 8 bits, or 2 nibbles and can effectively represent 0-255.

 

After that we move to the word, or 16 bits, or 4 nibbles or 2 bytes, which can represent 0-65,535.

 

Then the double word, 32 bits, 8 nibbles, 4 bytes, 2 words, which can represent 0-4,294,967,295.

 

Then the quad word, 64 bits, 16 nibbles, 8 bytes, 2 dwords, 4 words.

 

We can change the sign of a byte, word, dword, or qword ( oword - 128 bits, 32 nibbles, 16 bytes, 4 dwords, 8 words ) by adjusting the most significant bit.  How do we do that?  We set the most significant bit to either 0 or 1.

 

The most significant bit is the left most bit, and we thus set it to 1.  This changes our sign to negative.  Normally when you compile code if you want it to be interpreted as unsigned you must specify in the variable's declaration.  ( I really hope you remembered that and sang along with me )

 

In the case of a byte it would be bit 8 ( counting right from left ), or 128 ( 2^7 ) in this case.

 

128 64 32 16 8 4 2 1

 

1      0    0  0 0 0 0 1

 

Which would be -127.

 

128 64 32 16 8 4 2 1

 

0      1    1  1 1 1 1 1

 

Which would be 127.

 

Notice a pattern?  We have to use invert all the bits and add 1 to get the negative representation of a positive number and likewise.

 

Thus I must conclude it here for the time being. 

 

Link to comment
Share on other sites

  • Replies 63
  • Created
  • Last Reply

Top Posters In This Topic

Continuing on:

 

Now we at least have some understanding right?  Do you know your fundamental binary logic gates?

 

There are 7 types of logic gates which produce meaningful usable output, the remainder, 9, are essentially gibberish.

 

They are as follows:

 

AND, OR, NOT, XOR, NAND, NOR, XNOR.

 

NAND and NOR are considered universal logic gates and can be used to construct all the other logic gates.

 

Why in the world am I talking about logic gates?  Simple answer, bitwise operations and of course logical operators.

 

Your bitwise operators are as follows in C/C++:

 

NOT: ~

OR: |

AND: &

XOR: ^

 

Your logical operators are:

 

NOT: !

OR: ||

AND: &&

XOR: !=

 

To construct the NAND, NOR and XNOR we must mix up our operators and use parenthesis where necessary.

 

Bitwise:

 

NAND: ~( a & b )

NOR ~( a | b )

XNOR ~( a ^ b )

 

Logical:

 

NAND: !( a && b )

NOR: !( a || b )

XNOR !( a != b ), alternatively... ( a == b )

 

Next we'll go over our truth tables, a nice introduction to digital logic.

 

NOT:

0 -> 1

1 -> 0

 

AND

0 AND 0 -> 0

1 AND 0 -> 0

0 AND 1 -> 0

1 AND 1 -> 1

 

OR

0 OR 0 -> 0

1 OR 0 -> 1

0 OR 1 -> 1

1 OR 1 -> 1

 

XOR

0 XOR 0 -> 0

1 XOR 0 -> 1

0 XOR 1 -> 1

1 XOR 1 -> 0

 

NAND

0 NAND 0 -> 1

1 NAND 0 -> 1

0 NAND 1 -> 1

1 NAND 1 -> 0

 

NOR

0 OR 0 -> 1

1 OR 0 -> 0

0 OR 1 -> 0

1 OR 1 -> 0

 

XNOR

0 XNOR 0 -> 1

1 XNOR 0 -> 0

0 XNOR 1 -> 0

1 XNOR 1 -> 1

 

A little sidenote, the XOR operation is used to implement addition for all binary adders, which arguably is the most important operation in all math.  You can't do subtraction efficiently without it, or division or multiplication.  The AND gate is used to check for a carry.

 

Our next topic will cover the basics of disassembly, namely understanding what you see in some of the debugger panes.  This is precisely why we covered these topics.

 

 

Link to comment
Share on other sites

Now that we have covered some of the basics we can go on.

 

Let us briefly touch on the memory mode of 32 bit and some basic understanding of OS stuff.

 

We call 32 bit memory model flat memory mode, but technically it is still segmented.  In the last posts we touched on the size of a double word which gives you an idea of how much memory you can address in total.  When I speak of segmentation you have to understand that I am referring to the seperation between usermode and kernel mode.  Usermode resides in the lower half of the 32 bit model.  Specifically 0 -> 0x7FFFFFFF, whereas kernel mode resides 0x80000000 -> 0xFFFFFFFF.  16 bit segmentation allows you to address more than 64K of memory.  If you still want to know about 16 bit segmentation wikipedia has a good article about it.

 

In windows the allocation granularity is 64k.  Generally when you allocate memory it is preferable to use the heap over calling VirtualAlloc for small allocations.

 

I won't touch on 64 bit here, suffice to say for now we're limited to a 48 bit representation as of right now ( if I recall correctly, if this is wrong someone correct me ), which eerily mirrors the issue of 16 bit mode and the need to have more than 1 megabyte of addressable memory.

 

Programs which run in usermode are isolated from one other, and if you modify a core component say kernel32.dll or another, a copy on write mechanism creates a unique copy for that particular process.  What does that mean?  Typically every process has the same copy of kernel32.dll, it's mapped globally in the same area and shared across all processes typically unless ASLR is used, in which case you can... well that's an advanced topic, CR3 that is and page tables, not for here sadly.

 

At anyrate, if you modify it as I said before a copy on write mechanism creates a unique copy for that process and every other process happily chugs along with the mapped copy shared globally.  This is a solution to the issues suffered from windows 98 and below in which you modified kernel32.dll in one process it affected all processes thus allowing for some very nasty usermode viruses.

 

There are some of the memory protection modes available:

 

PAGE_READONLY

PAGE_READWRITE

PAGE_EXECUTE_READ

PAGE_EXECUTE_READWRITE

PAGE_GUARD

PAGE_NOACCESS

PAGE_EXECUTE_WRITECOPY

PAEG_WRITECOPY

 

For further information please reference:

 

http://msdn.microsoft.com/en-us/library/windows/desktop/aa366786(v=vs.85).aspx

 

Some of these protection constants can only be used by treating the allocation as a section.  ( NtCreateSection )

 

Most debuggers make use of PAGE_GUARD/PAGE_NOACCESS to place memory breakpoints.  This works by making an entire PAGE ( 4kb -> 32/64 bit or for more precisely:  4KB (all modes), 4MB (32-bit non-PAE), 2MB (32/64-bit PAE), 1GB (64-bit, always PAE), look up your processor to see exactly. )

marked as either guard/noaccess.  When the CPU executes code or reads data or writes to that region the IDT executes the exception handler which calls the usermode code to process the exception generated.  I won't discuss SEH/VEH or setting up an exception handler at all.

 

There are other ways to place breakpoints, such as using int3, int1, etc, and of course hardware breakpoints.  I won't discuss using more advanced kernel/hypervisior methods as that is way out of the scope of the discussion here.

 

Hardware breakpoints allow us to use debug registers to mark a specific 4 byte ( 32 bit ) or 8 byte ( 64 bit ) piece of memory, it could be code, or data to be triggered on read, write, or execute or some combination thereof.  You have a total of 4 breakpoints to work with here.

 

As for int3, etc, you simply modify the byte in the target area you want to hook and use your exception handler to redirect code flow to where you want.

 

I know I promised I would discuss the debugger panes, but as you have to realize there is a lot of ground we have to cover before we can even screw around with IDA, Ollydbg, CheatEngine, xdebugger, etc.

Link to comment
Share on other sites

Let us continue on the memory road shall we? 

 

Why is a page typically defined as 4096 bytes?  Well it is largely due to processor architecture, not relating to any choice by any OS developer(s) in particular.  I could delve deeper into the why, but that's for another time.  If you're still doubly curious, wikipedia has an alright article on the subject.

 

http://en.wikipedia.org/wiki/Page_(computer_memory)

 

We briefly touched on some of the semantics in windows regarding allocations and special cases for the protections.

 

For the record memory allocation types can be broken down into:

 

MEM_IMAGE

MEM_PRIVATE

MEM_MAPPED

 

The dlls/exes you know and love are typically mapped as MEM_IMAGE.  Whereas heap and other data will typically be mapped as MEM_PRIVATE, and MEM_MAPPED is for data views not for executable images.  CreateFileMapping anyone?

 

All of the types can be treated as sections, but mostly MEM_IMAGE and MEM_MAPPED are.  I won't discuss various nuances here either.

 

I also briefly touched on why you should prefer the heap over using VirtualAlloc citing the allocation granularity.  However I will delve a bit more deeply into the why.  If you are allocating a few hundred bytes, if you call VirtualAlloc, you are effectively mapping a whole page at 64k grandularity.  Whereas if you used the heap through malloc/new/HeapAlloc/RtlAllocateHeap you could avoid allocating a whole new page.  Not only that you are also increasing the working set for your process, ergo please use the heap whenever the need arises.

 

Next topic will cover more things, such as the PEB and other undocumented internal structures, along with a brief overview of how executable images are mapped into memory.  We'll start to delve into the debugger panes.

 

 

Link to comment
Share on other sites

So you made it this far?  Good.

 

We're going to cover the PEB now, otherwise known as the Process Environment Block.

 

This little doozy of a structure can be located at FS:[0x30] for 32 bit and FS:[0x60] for 64 bit.

 

The Process Environment Block is referenced within the Process Basic Information struct itself.  The PEB contains a weatlh of information, such as the loaded images in a doubly linked list called the loader module list, there are 4 linked lists in total, the parameters, session id, if it's being debugged, the pointer to the heap, and much much more.

 

If you want a pretty good idea of the whole scope of the struct I can only recommend you look at the ReactOS source code.  If you have any questions feel free to ask.

 

For the sake of simplicity I must end the discussion on the PEB here for now.  I will try to delve into it more in the future.

 

Executable images are largely mapped into memory by LoadLibrary.  I am going to give you a very brief overview of how this works.

 

First we check to see if we have already loaded this module, if so we just return.  If not, we resolve the path to the file on disk.  We basically read the whole file in memory and create a section object for it, aligning it to virtual memory.  If the image requires base relocation we handle that.  Then we check do an import lookup.  Once that's done we adjust the protections on the individual regions within the executable.  Then we call the module entry point, which can be the TLS and/or the export to say WinMain or main().  The TLS is always called first period.

 

Typically you'll have:

 

DOS/PE Header

.text - PAGE_EXECUTE_READ or PAGE_WRITECOPY, or something thereof

.bss - uninitialized variables, microsoft visual studio merges this into either the .data or .rdata section

.idata - import section

.edata - exports

.data - all your data which is both readable/writable

.reloc - this section contains the rvas and base fixups to apply

.rsrc - optional if the executable doesn't have icons, menus, or something like that

.etc - you can postpend any number of sections you want up to a limit

 

Executable images loaded by LoadLibrary always have a section object associated with them period.  VirtualQuery on the region will show you as MEM_IMAGE.  You can retrieve the path to the file on disk by calling GetMappedFileName.

 

You can load an image into memory without invoking LoadLibrary, but you must manage the imports, relocations, tls callbacks, etc manually.  This is called manual mapping.  You'd use VirtualAlloc to do your dirty work, thus showing up only as MEM_PRIVATE to all VirtualQuery reads.

 

I'm going to finally cover the debugging aspects in the next post, I feel I've covered enough base ground to warrant that.  We will have to discuss threads, handles, and other operating system concepts in order to further our education.

 

Clarification:  When I speak of images or modules I am referring to the exes / dlls you know and love

Link to comment
Share on other sites

You know... I honestly know nothing at all about memory protection modes and page tables and stuff like that. Don't think I ever needed it to do C&C hacking, either. I generally just dump stuff in my disassembler, find out what to change, and, um, change it :P

 

Header editing, though... that's quite a different beastie O_o

Link to comment
Share on other sites

You know... I honestly know nothing at all about memory protection modes and page tables and stuff like that. Don't think I ever needed it to do C&C hacking, either. I generally just dump stuff in my disassembler, find out what to change, and, um, change it :P

 

Header editing, though... that's quite a different beastie O_o

 

You need all around knowledge to mod/hack efficiently.  Header editting is easy, modify the number of sections in the file header, make sure you aren't going over the size of headers unless you fix it up and then make sure you modify the SizeOfImage ( optional header ) and add your section accordingly.  If you should happen to exceed the header size you're going to have to fix those pesky relocations and realign your sections by the file alignment and adjust the virtual address/ raw addresses accordingly. 

 

CFF Explorer is the best tool for the job.

Link to comment
Share on other sites

Debug time! Wee hee we made it. 

 

First things first, we need some tools of the trade.  I've grown to love Ollydbg so much, so you can use that or you can use xdbg.  Both are fine tools.

 

http://x64dbg.com/#start

http://ollydbg.de

 

Both are very much alike so most of what we do easily translates between the two.

 

We'll also need CFF Explorer.

 

http://www.ntcore.com/exsuite.php

 

Next we'll need cheat-engine.  Make sure you download the 32 bit version. 

 

http://www.cheatengine.org/

 

You could download a hex-editor, but CFF Explorer already provides that functionality so you don't really have to, but if you want to, there's a number of free ones out there.

 

Lastly, we need IDA.  If you don't have access to the commercial version(s), you can use the free IDA 5.0.

 

https://www.hex-rays.com/products/ida/support/download_freeware.shtml

 

This is a very short post because next post we will dig into messing with C&C95.  We're going to take a whack at modifying some good stuff.  Like making Obelisk of Lights able to shoot down Orcas and A-10, locating other stuff like Super Weapon timers, and so on.  All the while you're going to be learning about branches, opcodes, modrms, sibs and the whole nine.

 

We'll also cover threads, handles and so much more.

Link to comment
Share on other sites

Ok so I'm going to be working from Ollydbg here so let's get this party started.  I will be using 1.10 initially then we will switch to 2.0, or xdbg.  It doesn't matter.  First we're going to need to make sure that C&C95 runs in window mode.  Once you have done that, startup the game.

 

Now we're going to attach Ollydbg to the game.

 

Start ollydbg.

 

Click attach.

 

Look for C&C95, then attach it to the game.

 

wJSjSIg.jpg

 

This is what you should see if you're using Ollydbg.

 

Let's focus our attention to the right most pane.  The registers pane for the current executing thread.

 

You'll notice there are a number of registers.  EAX, ECX, EBX, EDX, ESP, EBP, ESI, EDI, EIP.

 

EAX -> typically used to return the result of a routine, known as the accumulator

ECX -> used in loops typically, for certain types of calling conventions ie class methods is used to pass the this pointer, or in the __fastcall calling convention

EBX -> arithmetic

EDX -> used for arithmetic mainly, but also used in __fastcall calling convention to pass arguments via registers

ESP -> stack pointer, holds the current frame pointer

EBP -> pointer to the base of the current frame pointer for a routine

ESI -> Extended Source Index, used for string operations mainly

EDI -> Extended Destination Index, string operations mainly

EIP -> Extended Instruction Pointer, holds the pointer to the currently executing instruction.

 

Followed up by your flags and your "segment" registers of ES, CS, SS, DS, FS, GS

 

CS -> Code Segment

SS -> Stack Segment

DS -> Data Segment

ES -> Extra Segment

FS -> Far Segment ( in windows we use this to get at the PEB in process )

GS -> Extra Segment or as I call it the Global Segment ( my wording, not Intel/AMD's )

 

The last error of the thread is right underneath that.  Search errorcodes on MSDN if you want to grasp the significance of that.

 

To the right of the segment registers you have a doozy of flags.  These are related to your branching instructions.

 

C -> carry flag ( think of addition here )

P -> parity flag ( odd even )

A -> adjust flag ( mainly used for binary coded decimal arithmetic I've hardly ever used this one )

Z -> zero flag, typically set if the two are equal, eg ( a == b )

S -> sign flag ( for jumping if the result was signed ie negative )

T -> trap flag ( for debugging )

D -> direction flag, left to right or right to left for string operations

O -> overflow flag ( eg if we overflowed  for an arithmetic operation, ie if we had 8 bit registers 127 + 127 = 254, but since we use most significant bit for sign, we have an overflow )

 

EFL -> Extended Flags

 

We use this for our branching instructions.  We'll cover those soon enough.

 

Lastly we have our FPU portion, suffice to say I'm not going to delve into this much right now.

 

Below the registers pane you'll see the stack pane for the current executing thread.

 

0CE1FF5C   77B1F926  RETURN to ntdll.77B1F926 from ntdll.DbgBreakPoint

 

This represents the top of the stack for the current function frame.  Notice it says return, and if you notice in the CPU pane we have  RETN instruction.  This pops the address stored at the current ESP off the stack into the EIP register and resumes execution.  We'll cover this in depth in future posts.

 

I'm going to end it here for now.  We'll get started on understanding more soon enough.

 

Link to comment
Share on other sites

Alright enough learning, for now, let's have a little fun first.  Just follow my lead and we'll get that obelisk shooting down aircraft and shooting across the map in no time, heck let's even throw in another parlor trick, we'll make it shoot nukes.  Lotsa fun no?  We'll go over every step of this later so you'll learn the concepts so you understand what is happening here.

 

First let us understand the difference between hot and cold patching.

 

Hot patching is modifying the executable in memory but not committing the changes to disk, thus not overwriting the original file.

Cold patching is modifying the executable and committing the changes to disk overwriting the original file.

 

AG is supposed to be AA, but I mistyped, and I'm only adding this here as a forewarning, I may fix this in the future.

 

Start your game up.

 

Now attach Ollydbg.

 

Press F9 to run the program.

 

Now press alt and e to get to executable modules and select C&C95.

 

Press enter.  If it goes back to ntdll, repeat the process over.

 

You should see.

 

ITZrtfh.jpg

 

Right click and look for search for, then all referenced strings.

 

Note: Ollydbg 1.10 for some reason burps if you try to do this I haven't bothered investigating as using 2.0 works just fine.

 

You should see.

 

R70HSYv.jpg

 

Press ctrl f and search for laser.

 

You should see.

 

0XR2Kp4.jpg

 

This is the projectile used by the obelisk.

 

Now I labelled a few things for our convenience.

 

Note olly kinda messed up the comments I made.

 

ujFQRZf.jpg

 

We have 2 methods for modifying the obelisk.  I'm going to go ahead and explain method 1.

 

Modify the AG entry to push 1.  With the line selected, press spacebar, and type push 1, then click enter.  You're done, save the modifications to disk and restart C&C95.  BACKUP YOUR C&C BEFORE YOU DO THIS.

 

Right click edit, Copy to Executable, then either right click save file, or click the big red x, and follow through.

 

Method 2 is harder.

 

Now we're going to modify the actual entry in the array where it's stored.

 

I'm going to cut some corners here to shave some time explaining things and go ahead and hand out where the array is in memory.

 

004B62FD  |. 8B1495 BC295000     MOV EDX,DWORD PTR DS:[EDX*4+5029BC]

 

To access the laser entry we simply do the following:

 

Take the index we found for the laser which is 0xC.

 

then:

 

0xC * 4 + 0x5029BC.

 

Ok so we've done the math, you should have 0x5029EC.

 

Press ctrl and g in the hex pane and paste that address.  You should see.

 

Note I surrounded it with a big red box.

 

tCZcYX3.jpg

 

Then we reverse the endian-ness to get.

 

0x531A60.

 

Ctrl and g again in the hex pane and go to that address.  You should see.

 

toPeQeV.jpg

 

Big red box again.  Follow my lead exactly.

 

See the 1101?  Change that to 1109

 

3h5djsX.jpg

 

Selec the 01 by clicking on it.  Press ctrl e, then type 09.

 

Press ok.

 

Congratulations, now your obelisk can shoot air targets.

 

Now we're going to modify the range of the obelisk and some other attributes, note, we will not be cold patching for this one.

 

We're just going to follow the same steps above.  I'm going to provide you the address you need.

 

The index for the Obelisk's weapon is 20, converted to hex 0x14.

 

0044114A  |. 8A84C1 D834500>MOV AL,BYTE PTR DS:[ECX+EAX*8+5034D8]

 

To get to the obelisk, we need to:

 

address = 0x14 * 8 + 0x14 + 5034D8

 

You should have 0x50358C.

 

Press ctrl g and paste that address and press enter.  You should see.

 

VQyCoB3.jpg

 

If you want to have your obelisk shoot nukes, change the 0xC ( Lasershot Projectile ) to 0xE ( AtomicDn Projectile ), by clicking on it, ctrl and e, change to 0xE enter.  Hahaha, just be careful.

 

The damage is 0xC8, so you should change to 0xFF, follow above.

 

The rate of fire is 0x5A, but because of the way the game treats the obelisk/laser, well you're limited to how fast you can fire, to set the fire rate to 0, you'd set it to 0xFD causing an overflow because the game adds 3 to the rate of fire.  There is a way to fix the obelisk so it can fire nice and fast, but that's for another time perhaps.

 

The range of the weapon is 7 cells, to a maximum of the map size.  So set it say to 30 cells, 0x1E or something.

 

And that's it.

 

Pick under siege c&c.  Sell a couple of units on the repair bay and whatever you need so you can build an obelisk.  Build yourself a power plant or 2 and the obelisk and enjoy the fruits of your labor.

 

Note: If you changed the projectile to AtomicDn it won't be able to shoot down those pesky A-10s or Orcas, I challenge you to fix that yourselves and while you're at it, change the speed of the projectile from 0x64, to 0xFF ( instant )

 

Whew that was a lot wasn't it?  Questions/Comments?

Link to comment
Share on other sites

lol. Here we go out of our way to prove hacking is a tool we can use to do nice things like fixing game bugs and adding features, and what do you use as example for this? Cheating in the game :D

 

You missed the entire point of the exercise.  Nothing gets them excited to learn quite like seeing what you can do with the game.  Fixing game bugs and adding features will be covered in future tutorials, and consider this is an intro to modding/hacking.  What I've shown is perfectly in the bounds of that.

Link to comment
Share on other sites

So we had fun right?  Those parlor tricks were neat no?  Thats peas compared to the power of what you can do.  For now however, we need to get back to learning, but I hope it inspired you to dig and learn on your own in addition to what I've been posting here.

 

Let's learn how to encode a x86 instruction shall we?

 

So everybody and his grandma knows that a x86 instruction has a few parts

 

prefix(es)

opcode

opcode2

modrm

sib

relative

displacement

immediate

 

There are a total of four prefixes, lock prefix/repeat, segment override, and 2 size override prefixes.

 

Your lock and repeat prefixes start at 0xF0 -> 0xF3, excluding 0xF1.

 

Your segment overrides:

 

#define PREFIX_SEGMENT_CS   0x2e
#define PREFIX_SEGMENT_SS   0x36
#define PREFIX_SEGMENT_DS   0x3e
#define PREFIX_SEGMENT_ES   0x26
#define PREFIX_SEGMENT_FS   0x64
#define PREFIX_SEGMENT_GS   0x65

 

Your size overrides are 0x66 and 0x67. 

 

0x66 overrides the operand size

0x67 overrides the address size.

 

We could discuss the opcode/opcode2, but I feel that it's wiser to save it for another time.

 

Let's move onto the modrm and sib now.  How do we intrepret the modrm and sib?

 

Well think back to the first tutorial, remember I stressed how important understanding bitmasking was?  Well here is why.

 

http://pdos.csail.mit.edu/6.828/2006/readings/i386/fig17-2.gif

 

Notice the register is packed into the byte using 3 bits, with the least most significant bit specifying if it is a register or a memory operation.  The most significant bits, bits 6 & 7 is the mode.

 

The SIB is largely self explanatory, where bits 6-7 specify the scale, 1, 2, 4, 8.  The index and the base are registers usually, otherwise it could happen to be the displacement.

 

Seem familiar and visible from the last tutorial?  I hope so.

 

We use the relative when doing calls/jumps.  We'll cover this in the future.

 

The displacement can be a memory address or an offset from a memory address.

 

The immediate can be any value, 8 bit, 16 bit and 32 bit thereof.

 

The only nuance I can recall about 64 bit is that aside from the rex prefix, the only difference is that the immediates are 64 bit whereas displacements/relatives are stuck to 32 bit.

x64 also features rip-relative addressing.

 

I'll edit this post to give a few nice examples of this so it's easier to comprehend.

 

 

http://www.c-jump.com/CIS77/CPU/x86/X77_0090_addressing_modes.htm

http://www.c-jump.com/CIS77/CPU/x86/X77_0060_mod_reg_r_m_byte.htm

http://www.c-jump.com/CIS77/CPU/x86/X77_0100_sib_byte_layout.htm

 

77995FEE | FF 94 D8 EF BE AD DE     | call dword ptr ds:[eax+ebx*8-21524111]  |


77995FEE | FF A0 EF BE AD DE        | jmp dword ptr ds:[eax-21524111]         |
77995FEE | FF A4 D8 EF BE AD DE     | jmp dword ptr ds:[eax+ebx*8-21524111]   |
77995FEE | FF A4 0F EF BE AD DE     | jmp dword ptr ds:[edi+ecx-21524111]     |

77995FEE | FF 14 C5 EF BE AD DE     | call dword ptr ds:[eax*8-21524111]      |
77995FEE | FF 15 A8 0E 00 00        | call dword ptr ds:[EA8]                 |

77995FEE | FF 10                    | call dword ptr ds:[eax]                 |
77995FEE | FF 17                    | call dword ptr ds:[edi]                 |
77995FEE | FF 20                    | jmp dword ptr ds:[eax]                  |
77995FEE | FF 27                    | jmp dword ptr ds:[edi]      

77995FE8 | FF 24 90                 | jmp dword ptr ds:[eax+edx*4]            |
77995FE8 | FF 24 D0                 | jmp dword ptr ds:[eax+edx*8]            |
77995FE8 | FF A4 D0 EF BE AD DE     | jmp dword ptr ds:[eax+edx*8-21524111]   |
77995FE8 | FF 24 D5 EF BE AD DE     | jmp dword ptr ds:[edx*8-21524111]       |

77995FDE | FF A2 EF BE AD DE        | jmp dword ptr ds:[edx-21524111]         |

 

For all the examples above, modrm is after the FF opcode, INSTR_FAR_PREFIX.

 

You'll notice I chose an easy displacement, 0xDEADBEEF, and the sib always comes before the displacement, and after the modrm.

 

Play around with bitshifting and the links above, get a feeling for what's going on.  This is critically important especially if you're tracing blocks and trying to deobfuscate/register swap or determine jumptables, etc.

 

Link to comment
Share on other sites

You missed the entire point of the exercise.  Nothing gets them excited to learn quite like seeing what you can do with the game.  Fixing game bugs and adding features will be covered in future tutorials, and consider this is an intro to modding/hacking.  What I've shown is perfectly in the bounds of that.

I'm just joking, man ;)

 

Though I guess, at the moment, this indeed the only way to mod C&C95.

 

They might want to take a look at my ini files to see what options are identified for each type so far ;)

Link to comment
Share on other sites

You are doing an great job, peter! Besides that, I never used a lot of these stuff, but I will take a look in a short future.

 

lol. Here we go out of our way to prove hacking is a tool we can use to do nice things like fixing game bugs and adding features, and what do you use as example for this? Cheating in the game :D

 

Cheating is one of the awesomeness of hacking a game. But it is good to know that cheating an online game is not trivial as this.

Link to comment
Share on other sites

Updated the post to include examples to understand modrm/sib bytes, and links detailing these in detail.

 

I will add more material as I decide what topics need to be covered.  Next post will touch on threads, handles, and other OS concepts and CompSci concepts.

 

For the record I find modrm and sib to be the most complicated and nuanced topics to discuss because without a firm understanding of bitmasking and playing around with different combinations, it's difficult to understand.

 

If you can grasp these concepts, then anything else I throw at you is pie.

Link to comment
Share on other sites

  • 5 weeks later...

I know I said I wanted to discuss threads and handles, but as I recalled, being able to read the documentation on sandpile, intel/amd manuals and x86ref is pretty important, what I am going to cover is how to interpret a key piece of this.

 

http://ref.x86asm.net/geek32-abc.html

 

FF 2 CALL r/m16/32 Call Procedure

FF 3 CALLF m16:16/32 Call Procedure

FF 4 JMP r/m16/32 Jump

FF 5 JMPF m16:16/32 Jump

FF 6 PUSH r/m16/32 Push Word, Doubleword or Quadword Onto the Stack

 

Ok so FF is our opcode right, and the mneonic is pretty obvious, as is the description.  BORING!

 

But we have these strange numbers, 2, 3, 4, 5, and 6, what are they for?

 

Look in the manuals and you'll see /2, or /3, etc.

 

They tell you how to encode the modrm for that instruction.

 

Skip to the end of the page where you see

 

32 bit ModR/M Byte

 

Notice this:

 

(In decimal) /digit (Opcode) 0 1 2 3 4 5 6 7

 

hmm...  now match with the row/column.

 

That's how you intrepret it folks.  Really simple I hope.

 

So to recap, if you see FF /2

 

You know that you're dealing with CALL r/m16/32.

 

The acceptable modrm values are:

 

0x10->0x17, 0x50->0x57, 0x90->0x97, 0xD0->0xD7.

 

Remember if you set the least significant bits to 4, you are telling the cpu a sib byte follows ( or if mod is 11, you use ESP ), and if you set it to 5, you are telling it you want to use displacement addressing ( or EBP if mod is not 0 ).  In x64 it's rip-relative addressing instead of displacement only addressing.  Remember the links I posted earlier exactly clarify these rules.

 

I think unless anybody requests help on the SIB bytes which I believe are extremely self explanatory we will cover those.

 

For now read over and enjoy.

 

 

Link to comment
Share on other sites

Thread time.  About time no?  Maybe we'll play around a bit more after this post.

 

So what is a thread?  A thread is simply a unit of execution consisting of instructions.  It is managed by the OS scheduler.

 

Every process must have at least 1 thread.  A thread is scheduled to run for a time slice.  Typically that is 20-40ms.

 

Understand this is different from multi-threading in which you have more than 1 physical cpu core, eg 2 cpus executing 2 threads.  In fact an effective scheduler with only one cpu with effectively written code on the portion of the programmer can out perform an application which has 4 cpu cores and 4 threads to work with at any one time.  I wonder why we don't focus more on the scheduling of threads and efficiency of our code instead of throwing more cores at the problem?  Considering that multi-threading sometimes only adds a 20-30% boost in overall speed...

 

Let's move on shall we?

 

Threads can have their priority set.  What this means is we are instructing the scheduler that our thread is more important than another so it's more likely to run in lieu of another thread.  That means more time slices for us to work with.

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686277(v=vs.85).aspx

 

Each thread has an ID associated with it, it's thread ID, which is unsigned long as well as guess what?  A handle.  Threads can also have their own storage ( TLS ) in the process which they store variables.  Threads are also associated with their parent process.

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx

 

Threads can also be suspended and resumed, and their context set.

 

What is a context?  A context is a stored data structure containing all the pertinent details about the thread at the point which it was queried.  Things such as the status of it's registers, last error and so on.  You can suspend a thread and redirect the EIP to resume execution somewhere else.  Please reference earlier in the post where we talked about the debugger pane with the registers to get a better grasp of the context.  Along with these links below:

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms679362(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/ms679284(v=vs.85).aspx

 

When a thread is suspended obviously it doesn't run.

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686345(v=vs.85).aspx

 

And to resume it we would call:

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms685086(v=vs.85).aspx

 

Naturally threads can be terminated, there are several ways to go about that.  Please reference:

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686724(v=vs.85).aspx

 

Threads can also "spin" that is waiting to acquire access to a resource or a certain condition or set of to be met, ie through a lock or such.  We'd use something like:

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms686298(v=vs.85).aspx

 

Or better yet...

 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx

http://msdn.microsoft.com/en-us/library/windows/desktop/ms687025(v=vs.85).aspx

 

Bare in mind Sleep merely instructs the scheduler not to schedule the specified thread to run for period X.  WaitForXObject is used to tell us if an object is signaled or not.

 

We will cover handles soon as that is a very large post unto itself.  Handles to files, processes, threads, locks, windows, etc.

 

That's all I can think of off the top of my head for now, I'll probably read over this post some more and edit it if I see any glaring errors.

 

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...