0% found this document useful (0 votes)
291 views74 pages

Warcraft PDF

Uploaded by

kyawmoesoe
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
291 views74 pages

Warcraft PDF

Uploaded by

kyawmoesoe
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 74

Hacking World of Warcraft™

An exercise in advanced rootkit design

Greg Hoglund
Hacking World of Warcraft™ - an hoglund@hbgary.com
exercise in advanced rootkit design ©2006 Greg Hoglund

Why Games?
• Games are fun, we like them, and it’s a fresh
topic
• Games are complex online applications
• There is a healthy community of game hackers
(that is, those who write game hacks & discover the exploits)
• Computer Gaming is BIG BUSINESS
– Microsoft reports that gaming is the third most
common activity on their platforms, just after browsing
the Web and email

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Why cheat?
• It’s a challenge
• Because I suck at this game
• Because I like having an unfair advantage
• Because cheating makes the game more
fun to play
– Such as automating repetitive and boring
tasks
• Because cheating makes money $$$$ !

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

MassivelyMultiplayerOnline’s
(MMO)
• Think D&D mixed with IRC
• More than 50 active MMO’s in the world
• Over 10 million players of MMO’s
worldwide, and the number is doubling
every year (whoa!)
• 2.9 Million players of MMO’s actively
subscribed in the U.S. alone (IDG estimate)
• 6 million subscribers to WoW worldwide

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
http://mmogchart.com/Chart4_files/Subscriptions_21524_image001.gif
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

http://mmogchart.com/Chart7_files/Subscriptions_12473_image001.gif
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
MMO’s and Money
• There are real exchange rates between in-
game currency and real money
– The “wealth”in some MMO worlds is greater
than some small “real”countries
• There was a $600 million dollar real,
secondary market selling ‘virtual gold’in
2005 (estimates vary)
• Current calculations show that “farmers”
can make about $1.17 hour playing WoW
– This is good enough for China!
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The exchange
The exchange rate
rate isis
roughly 10
roughly 10 cents
cents to
to aa gold-
gold-
piece on
piece on WoW
WoW servers
servers

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
http://www.gameusd.com/
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

“It’s easier than making shoes!”


• In China, over a half-million people “farm”
MMO games
– Some sleep on cots near the computers and
work in shifts
• Actually, people choose this job. It can be
better than working your Dad’s state-
owned farm
• Almost anyone can get this job – opens
the door for “unskilled”labor

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
A video documentary on Chinese gold farmers can be found here:
http://youtube.com/watch?v=ho5Yxe6UVv4

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The Smooth Criminals


• One fella made $1.5 million dollars in one year, $700k of
that for himself personally
– He used exploits and “dupe-cheats”and had to enlist outside
help to “launder”all the in-game currency
– He is reported as saying: "[SWG] built my new house, which I
paid for in cash, So when you ring my doorbell, it plays the
Star Wars music.“
• Rich Thurman earned $100,000 dollars farming 9-Billion
gold in Ultima Online
– He had up to 30 computers running macro’s at one time
• Single exploit macro’s have been sold for more than
$3000 each to gamers
• Some cheat-sites make around $40k/month in
subscription fees

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Overview of cheating technology

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Two kinds of cheating
• “Exploits”- actual game bugs, which are
exploited to:
– Teleport
– Duplicate items or gold
– See stuff your not supposed to see
• Botting
– Both AFK and non-AFK
– Only performing legal inputs, but in an
automated fashion

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Botting
• People want to do this because
– Because ‘grinding’is really boring
• And WoW, in my opinion, has a big problem w/ this
– Because they are ‘farming’
• Running the game to farm a resource, possibly
running multiple accounts at once

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Combat bots
• Aimbotting
– Bots cannot be used for ‘aimbotting’in WoW –
it’s not a first person shooter
• PvP bots
– In WoW these are possible, but their
effectiveness has never been proven
– Possible advantage for optimized DPS
– Possible advantage to move behind enemy
player
• Orientation plays a big part of combat mechanics

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Soft cheating –vs- Hard cheating?


• Cheating by using bugs in the game is definitely
cheating
– For example, dupe bugs and telehacking
• AFK Botting is considered cheating by the end
user license agreement
– But, in my opinion, AFK Botting should not be
– Why force players to perform repetitive tasks?
• Using a PvP bot would most definitely be
cheating. And, most people would do it if they
could. ?

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
How do bots work?
• MACRO’s & Scripts (most common)
• Memory read & write
• DLL Injection
• Debugging

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

MACRO’s
• Inject keystrokes and mouse movement
• Sample pixels and read memory locations
– Takes over the GUI
– You can’t use the computer
– Error prone
– Screen and controls must be preconfigured
exactly as required

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Example: ACTool

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Process manipulation
• Read & Write memory data
– Coordinates
– Speed
– Which direction your facing
• Can be used in conjunction w/ MACRO
– Data can be read directly instead of inferred
from pixel colors or text
• Can be used for exploits
– Map hacks, teleporting, speed hacks, etc

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Example: Bubba’s Warcraft Hack

• In-game interface (cool!)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Total Client Replacement
• Forget memory offsets and hiding – write
your own client
• Done in First Person Shooter games to
cheat
• Can we make WoW look a bit like Warcraft
3?
• Requires significantly more initial work

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
One Free WoW 0day
• Specify someone else's GUID in
CMSG_STANDSTATECHANGE
• Can make other players sit
• Makes for some fun PvP

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Thread Hijacking
• Hijack main system thread
– Eliminates thread safety issues
• Call internal functions within game client
directly
– You can minimize the game program
– It runs itself
– It doesn’t have errors in sampling
• Eliminates need for MACRO altogether

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Thread hijacking
• Used in a few WoW botting programs
WoW.EXE
INJECTED
MAIN DLL
THREAD

RenderWorld(..)
DETOUR PATCH

Loops hundreds of times per second

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

WoW!Bot

External program w/ C# scripting, very extensible

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Enter the Warden
• Absolutely brilliant countermeasure technology
to catch cheaters and AFK Botters
• Uses ‘fear of the unknown’to keep cheaters
down
– Code is downloaded from server on-the-fly
– Code can change at any time
• Scans for publicly available bot & cheat
programs
• Thousand of players have been banned!

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden
WoW.EXE
INJECTED
MAIN DLL
THREAD

RenderWorld(..)
DETOUR PATCH

WARDEN
CODE

This warden code runs about every 15 seconds

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Warden
WoW.EXE
WARDEN
CODE

? ?
RenderWorld(..) INJECTED
DETOUR PATCH DLL

DLL
DLL
DLL

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden
WoW.EXE
WARDEN
MEMORY CODE WINDOW TEXT

? WINDOW TEXT

?
OTHER PROCESS
?

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
How to find the Warden in memory
• It gets called in response to a server
message
– If you hook or sniff the “NetClient”class in
WoW.EXE, you can locate the call into the
warden by tracing what happens after
message type “0x2E8”arrives. This is the
warden message*.

*of course, this information was obtained using publically available resources
that can be found using a well skilled google search.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Follow the packets

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Packet includes a type field

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden is called thru a function


pointer (naturally)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Note: strings present in binary

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden Deadlisting
Thispage
This pagechanges
changesdynamically
dynamicallywith
witheach
each
Gamelaunch
Game launch

Thepage
The pagecontains
containscode
codewith
withstandard-looking
standard-lookingfunction
functionentry
entry
Andexit
And exitpoints
points

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Warden Deadlisting

Readand
Read andcompare
comparelocal
localprocess
processmemory
memory

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden Deadlisting

Readand
Read andcompare
comparelocal
localDLL
DLLnames
names

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Warden Deadlisting

Readand
Read andcompare
compareexternal
externalprocess
processnames
names

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Warden Deadlisting

Readand
Read andcompare
comparememory
memoryfrom
fromother
otherprocesses
processes

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Warden Deadlisting

Readwindow
Read windowtext
textfrom
fromall
allopen
openwindows
windows

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The Governor – keeping Warden


honest
• A program that reveals what the Warden is
doing

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
End of cheating?
• Just turn off the warden?
– If the warden responses don’t come back,
server disconnects you within 15 seconds
• Forge responses?
– You need to crack their encryption
– Remember that it scans anything it wants to
– Remember it can change at any time

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

What if we could forge a Warden?


• If you have captured a known Warden,
you could probably make a forgery for that
single unique warden, and just exit the
game if any other version is detected
– Safe
– Annoying as all get out if you’re the poor sap
who has to keep the forgeries updated
– Extra annoying because some warden
versions are unique to certain servers, player
accounts, etc

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Anti-warden
do you trust it?

FuxxoZ – can be used in conjunction w/ WoW!Bot

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

RealmBot

“Currently, RB avoids every check in the warden client, making it undetectable.


This is because Realm Bot and Realm Defender do not use any memory mods
whatsoever, randomize their window names, and do not use detour hooks for
common functions, things that WoW is looking for...”

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
WoWHelper

“… Built in randomization and a completely external thread


helps make the program very discreet… ”

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

WoWSniffer

Runs as a totally stand-alone program.


Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
The short life of a Bot

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

WoW!Bot / WoW!Sharp
a WoW botting program
• Written by Jerremy Koot
• Started development January 2005, went live in
May
• Was about 2 months of development for the bot
and 2 months of other work making a service out
of it
– Had a small subscription fee (2 euro’s a month)
– Users had code-patching offsets downloaded on-the-
fly when they logged in
• Closed shop in Sept 2005 due to the Warden
– Jerremy figures, with all the work, he only made about
2 bucks an hour for all his time

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
WoW!Sharp goes down
• Remember, this was a moonlighted business
without any funding or staff
• Sometime in August 2005, Wow!Sharp was put
into a “lockout”period since a new warden had
been released (this was common practice)
• The new warden was detecting WoW!Sharp and
reverse engineering was difficult and taking
weeks
• Paying customers were growing impatient and
putting on pressure to unlock WoW!Sharp.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

WoW!Kit
trying to save the day w/ a rootkit
• Written in August/September of 2005, but was
NEVER RELEASED to the public
• Development was started WAAAAY too late in
the game. It needed a few more weeks of
development.
– Memory cloaking was blue screening like crazy!
• Under pressure, a pre-release version (w/o
memory cloaking) was tested for a few days and
resulted in MASS-BAN of all developer accounts
– My own version had memory cloaking enabled, and I
was one of the few who did not get banned

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Warden works!
It cost hackers time and money
• Bannings were a very hard blow to the team
– Many used their level-60 accounts to test with!
• Prompted the open-source release of the whole
project (“we give up!”)
– Not enough time or money to make it all worth it.
• Prompted a detailed analysis of the Warden client
(how were we detected?)
– This lead subsequently to the “governor”release

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The endgame
• Ultimately, users created private versions of the
WoW!Bot w/ the source. Some of these remain
undetected to this day.
• The development of WoW!Kit continued in
private
• WoW!Sharp was released because of customer
pressure, not a failure in technology
• Without all the external pressure, WoW!Sharp
development would have had more time to
develop a working solution.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
What did we learn?
• The core weakness of Warden is that it’s
signature based
– KEEP YOUR BOTS PRIVATE!
• Software tamperproofing WORKS, the
warden proves that

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Thoughts for the future?


• If we move the entire bot into the kernel,
will Blizzard write a kernel-mode Warden?
– Too expensive and dangerous
– Kernel code must be much more reliable
– 4 million kernels? I don’t think so… .
• Can a kernel-mode bot be released to the
general public w/o fear that the user-mode
warden will come up with something to
detect it?

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Introducing “The Supervisor”

Full kernel-implementation of what


we did w/ Wow!Sharp

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The Supervisor
• NO PROCESS!
• NO INJECTED DLL!
• NO INJECTED THREAD!
• NO ATTACHED DEBUGGER!
• NO SCANNABLE MEMORY!
• NO CODE PATCHES!

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Basics of the Supervisor
• Uses Shadow Branching, the technique
originally developed for WoW!Kit
• Injects “implant”code directly into a user-
mode memory page (yay, no injected DLL!)
• Command-and-control is via a kernel-
mode TCP/IP stack
– bot controller script can be on a separate
computer

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Overall architecture

WoW.EXE
IMPLANT
MSG
MSG
Botting
SUPER
Application
MSG

Controller Machine Slave Machine

Kernel-land memory

User-land memory

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Summary of components
Antifreeze.EXE
Driven by LUA script
Botting
Communicates using standard winsock
Application
Potentially could drive more than one bot (a farm?)
Not exposed to warden for detection

Supervisor.sys rootkit
Can be loaded before launching game
SUPER Accepts commands over network

Message structure
Can be used to send commands and
MSG
also query for results

IMPLANT Position-independent, like shellcode


Tiny little micro-kernel for executing and
responding to MSG

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Shadow Branching
• Instead of a detour, it uses hardware
breakpoints to hijack the main program
thread
– A hook on any number of points (i.e.,
PspGetContext) can protect the context from
being read from usermode
• Uses memory cloaking to protect injected
code
– Page table manipulations and/or timely
memory movements

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Injected code portion
WoW.EXE

MAIN THREAD

BREAKPOINT
UNCLOAK
INJECTED PAYLOAD
PAYLOAD
SUPER RECLOAK

RESTORE

MAIN THREAD

Kernel-land memory

User-land memory
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hardware Breakpoints
• Implemented via DR registers
• Requires address of breakpoint in DR0-3
and a corresponding modification to DR7

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Problems w/ NtSetContextThread
• Does not seem to allow DR register modification
• Attempted CONTEXT_DEBUG_REGISTERS
and no error occurs, but subsequent read of the
trap frame shows that no set occurred
• DR register values, clearly present in trap frame,
are zero’d out in context returned from
NtGetContextThread
• Attempting to set other types of context, such as
EIP, results in instant blue screen

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Context Conclusion?
• Throw the NtSet/GetContextThread
routines in the trash heap. Go straight to
the trap frame for your work.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Accessing the Kernel Trap Frame


• Lies at the bottom of the kernel mode
stack
• The “trap frame”pointer stored in
ETHREAD lies! Don’t trust it. Calculate it
yourself!
– In actual fact, the “trap frame”pointer is
simply not initialized in many situations.
Sometimes it works. Your manual calculation
ALWAYS works, however.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Getting to the KTRAP_FRAME

FS:0

Initial
TIB
KTHREAD
Stack
(TCB) KTHREAD
KPCR InitialStack
DRxx

SelfPCR
EIP
ETHREAD KTRAP_
PRCB
FRAME

KPRCB
*CurrentThread
*NextThread
*IdleThread

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

What we use the trap frame for


• Control of DR registers
• Control of EIP
– Modifying EIP in the trap frame does not
cause the SET_OF_INVALID_CONTEXT blue
screen.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Kernel-mode APC’s
• We want to modify the kernel trap frame
while in the context of the target thread
• We can schedule a kernel-mode APC
against the target thread
• Unlike user-mode APC’s, the kernel-mode
APC does not have to wait for an alertable
state, it will execute immediately

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

WoW.EXE
INJECTED
MAIN CODE PAGE
THREAD

RenderWorld(..)
HARDWARE BP

? DR0

WARDEN
CODE

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Debug Interrupts
• Interrupt 1
– Called for single step, branch trace, ICEBP,
and hardware breakpoints
– We are only using it for hardware breakpoints
• Interrupt 3
– Called for embedded INT3 (0xCC) and INT-3
breakpoints

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Multiprocessor-safe interrupt
hooking
• We are using breakpoints so we need to
hook the debug interrupts
• Each CPU has it’s own IDT, so we need to
hook them all
• We can use a Deferred Procedure Call
(DPC) to schedule activity against a
particular CPU

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Proper interrupt hooking
• Use Deferred Procedure Calls scheduled
to each processor in the system

IDT 0

IDT 1

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Initialize the DPC’s and a waitable


event
void InitForInterruptHook()
{
////////////////////////////////////////////////////////
// setup for multiple processor IDT hooks
////////////////////////////////////////////////////////
gDPCP_SetInterruptHandlers =
ExAllocatePool(NonPagedPool,sizeof(KDPC));
KeInitializeDpc( gDPCP_SetInterruptHandlers,
DpcRoutine_SetInterruptHooks, NULL );

gDPCP_RemoveInterruptHandlers =
ExAllocatePool(NonPagedPool,sizeof(KDPC));
KeInitializeDpc( gDPCP_RemoveInterruptHandlers,
DpcRoutine_RemoveInterruptHooks, NULL );

KeInitializeEvent(&gEvent_Process_Set_Complete,
NotificationEvent, 0);
}

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Detect number of processors
KAFFINITY NumberOfProcessors; Bitmask
int n;
int pcount;

NumberOfProcessors = KeQueryActiveProcessors();
for(n=0; NumberOfProcessors; NumberOfProcessors >>= 1)
{
if (NumberOfProcessors & 1)
n++; Number of processors
}//end for

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The DPC scheduling


KeSetTargetProcessorDpc( gDPCP_SetInterruptHandlers, c );
KeInsertQueueDpc( gDPCP_SetInterruptHandlers, NULL, NULL );

DPC function will set the waitable object when it’s done

KeWaitForSingleObject(
&gEvent_Process_Set_Complete,
Executive,
KernelMode,
FALSE,
NULL);

KeResetEvent(&gEvent_Process_Set_Complete);

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
The DPC routine itself
VOID DpcRoutine_SetInterruptHooks ( … )
{
ULONG procnum = KeGetCurrentProcessorNumber();
logprintf("DpcRoutine_SetInterruptHooks called on processor %d", procnum);

// UNProtect memory

__asm sidt idt_info


idt_entries = MAKELONG(idt_info.LowIDTbase,idt_info.HiIDTbase);
old_ISR_pointer_1 = MAKELONG(idt_entries[NT_INT_DEBUG_1].LowOffset…
old_ISR_pointer_3 = MAKELONG(idt_entries[NT_INT_DEBUG_3].LowOffset…

__asm cli // remember we disable interrupts while we patch the table


idt_entries[NT_INT_DEBUG_1].LowOffset = my_interrupt_hook_1;
idt_entries[NT_INT_DEBUG_1].HiOffset = (my_interrupt_hook_1 >> 16);
idt_entries[NT_INT_DEBUG_3].LowOffset = my_interrupt_hook_3;
idt_entries[NT_INT_DEBUG_3].HiOffset = (my_interrupt_hook_3 >> 16);
__asm sti

// REProtect memory

KeSetEvent(&gEvent_Process_Set_Complete, 1, FALSE);
}

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

What happens on an interrupt?


• A trap frame is pushed onto the stack
• Interrupt handler is called

ESP EIP of interrupt

Saved Code Segment

Saved EFlags

Hardware ESP

Hardware Stack Segment

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Important things about interrupts
• Don’t hang around, you need to return
promptly
• Schedule DPC’s for any work you need to
do
• Any modifications made to the CPU and
trap frame immediately become context
after you iret
– This enables us to store/restore context and
alter things like the EIP

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Shadow Branching
• Through interrupt, cause immediate and
total change in execution context in thread
• On command, put it back just like it was
before the shadow branch

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Overview
• Based on interrupt hooks
– Interrupt 1
– Interrupt 3
• No requirement to hook page fault handler

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Setup of interrupt handler


• Hook an interrupt to use for commanding a
shadow branch
• Use a debug interrupt so that the rootkit
cannot be debugged ‘live’with Softice or
Windbg

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
The interrupt handler

•Get the Trap frame


Int 3 •Alter the saved EIP in the trap frame
•return from the interrupt

typedef struct _XTRAP_FRAME


{
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
ESP DWORD HardwareEsp;
CPU
CPU DWORD HardwareSegSs;
} XTRAP_FRAME, *PXTRAP_FRAME

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Saving context
All registers, flags,
segment registers
Int 3

typedef struct _XTRAP_FRAME


{
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD HardwareEsp;
CPU
CPU DWORD HardwareSegSs;
} XTRAP_FRAME, *PXTRAP_FRAME

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
When rootkit code is done
All registers, flags,
segment registers
Int 3

typedef struct _XTRAP_FRAME


{
DWORD Eip;
DWORD SegCs;
DWORD EFlags;
DWORD HardwareEsp;
CPU
CPU DWORD HardwareSegSs;
} XTRAP_FRAME, *PXTRAP_FRAME

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Getting the entire context at the


time of the interrupt
• Some of the data must be read from the
trap frame, such as the saved EIP
• Most of it can be read right from the
processor

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Our context structure
typedef struct _MyContext
{
ULONG eax; Read from processor
ULONG ebx; Read from processor
ULONG ecx; Read from processor
ULONG edx; Read from processor
ULONG esi; Read from processor
ULONG edi; Read from processor
ULONG eflags; Read from trap frame
ULONG ebp; Read from processor
ULONG esp; Read from trap frame
ULONG eip; Read from trap frame
ULONG segss; Read from trap frame
ULONG segcs; Read from trap frame
} MyContext, *PMyContext;

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Sending commands via the


interrupt
• Values in registers
• Values on stack
• Values around the EIP
– Think ‘hard coded opcodes’

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Example ‘secret’command
__asm
{
pushad
mov eax, index
mov ebx, from_address
mov edx, to_address
int 3
56 push esi // this series of push/pops
pop esi // are a secret command key used
5E push esi // to tell the driver what it is we are
pop esi // trying to do
56 popad
}
5E

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Reading the ‘secret’command


code
ULONG anAddress = 0;
ULONG aSecretCode = 0;

anAddress = trapframe->Eip;

__asm
{
push eax
push ebx
mov eax, anAddress
mov ebx, dword ptr [eax]
mov aSecretCode, ebx
pop ebx
pop eax
}

if(aSecretCode == 0x5E565E56 )//esi


{

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Returning from the interrupt
• Passing the interrupt onto the original
handler
• Changing behavior depending on which
EIP threw the interrupt
• Setting arbitrary context before returning
• Setting the RF Bit to prevent recursion

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The whole hook


cli
call GetContext // save the current context for future reference
pushad // save all general purpose registers
pushfd // save the flags register
mov eax, esp //
add eax, 0x24 //
room taken by the pushad/pushfd instructions
push eax
call DebugHandler_1 // call function

mov eax, 0
mov g_already_handling, eax

popfd // restore the flags


popad // restore the general registers
jmp old_ISR_pointer_1 // goto the original ISR

sti Choose which route you want to take…


iretd

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Restart Flag (RF)
• When set, it causes any debug fault to be
ignored during the next instruction
– This is a way to skip over a breakpoint w/o having it
fire a second time
• So, when we return w/ an IRET, we need to
make sure the saved EFlags value in the trap
frame has RF set
• Note: x86 documentation says that RF should
be set automatically, but on VMWare this may
not be the case. Be safe and set it yourself.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Modifying Context
• Anything, even including EIP!
– By modifying EIP, you can cause the thread to
‘hyperspace’to a new location
– All kinds of in-place modifications are possible

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
mov esp, eax Put pointer to trap frame in eax first

mov eax, gCurrentContext.eip


mov ebx, gCurrentContext.esp
mov ecx, gCurrentContext.segss
mov edx, gCurrentContext.eflags
mov esi, gCurrentContext.segcs

mov [esp] XTRAP_FRAME.Eip, eax


mov [esp] XTRAP_FRAME.SegCs, esi
mov [esp] XTRAP_FRAME.EFlags, edx
mov [esp] XTRAP_FRAME.HardwareEsp, ebx
mov [esp] XTRAP_FRAME.HardwareSegSs, ecx
TrapFrame
Trap Frame

mov eax, gCurrentContext.eax


mov ebx, gCurrentContext.ebx
mov ecx, gCurrentContext.ecx
mov edx, gCurrentContext.edx
mov esi, gCurrentContext.esi
mov edi, gCurrentContext.edi
mov ebp, gCurrentContext.ebp
CPURegisters
CPU Registers
// boom!
sti
iretd
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Shadow Branching – our


implementation
1. Interrupt fires
2. EIP is pointing to the code we are branching
upon
3. Uncloak a memory page
4. Hyperspace to the uncloaked memory page
5. New code runs
6. When new code is done running, interrupt fires
again
7. Re-cloak the memory page
8. Hyperspace back to original location as if
nothing happened

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Shadow branch part I
if(anAddress == g_det_0_src)
Thesource
The sourceofofthe
the
{
exception is the code
exception is the code
// we are performing a shadow branch. Save address
address wehave
we
the currenthavecontext
hardware
hardware
// so we can use it later when restoring this breakpointset
breakpoint
thread. set
CopyContext( &g_det_0_ctx, &gCurrentContextupon
);
upon

Wesave
We saveoff
offthe
thecurrent
currentcontext,
context,so
soititcan
canbe
berestored
restoredlater
later

// set the new eip that we want to shadow-branch to


gCurrentContext.eip = g_det_0_dst;

// uncloak the target memory that we may be branching into


UncloakPages();
Weset
We setthe
thecurrent
currentcontext’
context’
ssEIP
EIPtotothe
thehyperspace
hyperspacetarget
target

Beforereturning
Before returningfrom
fromthe
theinterrupt,
interrupt,make
make
surethe
sure thetarget
targetmemory
memoryisisuncloaked
uncloaked

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Preventing recursion
• When our hardware BP fires, we are going
to shadow branch to a new code location
• When we are done w/ our new code, we
need to branch back to original location
• When original location executes, we
DON’T want to shadow branch again
– Else, we would be infinite ‘loop’

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Takenote
Take noteof
ofwhich
whichaddress
addresslast
lastcaused
causedaashadow
shadowbranch
branch
if(anAddress == g_det_0_src)
{
// set this so we don't repeat the same
// exception over and over
g_last_firing_detour = anAddress;

if(anAddress == g_last_firing_detour)
{
g_last_firing_detour = 0;
gCurrentContext.eflags |= 0x00010000; Set RF !!

__asm
{
mov eax, trapframe
call SetContext_and_IRET
}
} Whenthe
When theinterrupt
interruptfires
firesmake
makesure
surethat
thatyou
you
checktotosee
check seeififit’
it’
ssthe
theaddress
addressthat
thatlast
lastfired,
fired,
ififititis,
is,then
thenjust
justreturn
return

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Memory Cloaking
• Reading memory is required for integrity
analysis and scanning
• Virtual addresses must be translated into
physical address before memory can be
accessed
• The physical address is what controls the
view of memory
• You can alter this translation in several
places

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Page Table Manipulation
• Alter the physical base address for the
page
• Remap the virtual address to:
– Another resident page in the same process
– or, a page of NonPagedPool you allocate in
the driver

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Exploit the conversion

CR3

CPU
CPU
Virtual Address

RAM

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Exploit the conversion

CR3

CPU
CPU
Virtual Address

RAM

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

CR3 KPROCESS
Physical address
Non-PAE

Virtual Address
10 bits 10 bits 12 bits
Page Directory Index (1024) Page Table Index (1024) Byte Index (4096)

Page Tables Physical Memory

Page Frame
Page Dir
Individual
page table

PDE

PTE
1 per process

1024 per process

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
MS Windows: Getting the virtual addresses
of PDE entries (non-PAE)
Virtual Address
10 bits 10 bits 12 bits
Page Directory Index (1024) Page Table Index (1024) Byte Index (4096)
Page Directory Base

0xC0300000

10-bit address * 4

#define GetPDEAddress32(va)
( (PPDE32) ((((ULONG)va >> 22) << 2) + PROCESS_PAGE_DIR_BASE))
take top 10 bits --------^ ^-- leftshift 2 == *4 (32 bit entries)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

MS Windows: Getting the virtual addresses


of PTE entries (non-PAE)
0xC0300000
Note that the PDE is not used for lookup purposes

Virtual Address
10 bits 10 bits 12 bits
Page Directory Index (1024) Page Table Index (1024) Byte Index (4096)
Page Directory Base

0xC0000000
20-bit address * 4

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
PAE
• All entries in the page directory specify 64
bit physical addresses
• Virtual address is still 32 bits
• Enables a 32 bit virtual address to map
anywhere within a 64GB physical address
space
• CR4[PAE] = 1 (and CR4[PSE] = 0)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

CR3 KPROCESS
Physical address
PAE

Virtual Address
2 bits 9 bits 9 bits 12 bits
Page Directory Index (512) Page Table Index (512) Byte Index (4096)

PDP index
Page Dir Pointers Page Tables Physical Memory
(4)
PDP
Page Frame

Individual
page table
Page Dir

PDE

PTE

4 per process
2048 per process

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
MS Windows: Getting the virtual addresses
of PDE entries (PAE)
Virtual Address
PDP index
(4) 2 bits 9 bits 9 bits 12 bits
Page Directory Index (512) Page Table Index (512) Byte Index (4096)
Page Directory Base

Note that the PDP table isn’t used


0xC0600000
11-bit address * 8 Remember, 64 bit entries

#define GetPDEAddress64(va)
( (PPDE64) ((((ULONG)va >> 21) << 3) + PROCESS_PAGE_DIR_BASE_PAE))
//take top 11 bits --------^ ^-- leftshift 3 == *8(64 bit entries)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

MS Windows: Getting the virtual addresses


of PTE entries (PAE)
0xC0600000
Note that the PDE is not used for lookup purposes

Virtual Address
PDP index
(4) 2 bits 9 bits 9bits 12 bits
Page Directory Index (512) Page Table Index (512) Byte Index (4096)
Page Directory Base

0xC0000000
20-bit address * 8
Base remains the same as
Non-PAE but entries are 64 bits

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
// remap existing virtual address to new physical address
// this can be read using readprocessmemory OK, but it BSoD's if the program
// tries to read the memory - there is a consistency check.
VOID RemapVirtualAddress( PVOID virtual_address, ULONG physical_address )
{
if(TRUE == IsPAE())
{
PPDE64 aPDE64 = GetPDEAddress64(virtual_address);
if(aPDE64->Present)
{
PPTE64 aPTE64 = GetPTEAddress64(virtual_address);
if(aPTE64->Present)
{
aPTE64->MemoryBasePhysicalAddress =
(physical_address >> 12);

// invalidate the TLB


__asm invlpg virtual_address
}
else
{
conprintf("page is not marked present \r\n");
}
}
else
{
conprintf("page table marked as not present...\r\n");
}
}
else
{
// FIXME do non PAE
}
}

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Challenges: Memory cloaking


• Alterations to page tables are consistency-
checked on process shutdown
• If you modify the page tables directly, and
the process exits before you undo the
change, you get…

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

A bit better memory cloaking


(without all those darned page tables)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Memory cloaking via timely
memory movements
• Page table manipulations are both
complex and difficult to manage in regards
to paging and consistency checking
• A very simple and elegant solution is to
simply copy the memory in and out of a
“holding area”
- very stable

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

super
User-mode mapped memory
Allocate

“Holding pen” Cloak

Fill w/ AAAA

Uncloak

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The Implant

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
The Implant
• Position independent, like shellcode
• Parses PE headers to find GetProcAddress and
LoadLibrary
• Subsequently loads a table of function pointers
• Sets up it’s own memory page for cloaking
• Sets up the primary function hook
• Implements the micro-kernel that is executed
when the primary function hook fires

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Implant written in ‘c’


• The implant is written in ‘c’and
precompiled into the rootkit
• The implant compilation process has two
components, the DATA and the CODE

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Based at bottom of page
The header & loader code runs only once.
header
Finds data section.

loader Loader code, uses hashes to build function


call table.

Hook code handles parsing of messages,


Hook code cloaking, process instrumentation…

Data has a precise, parseable format.

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The “compilation”process

DLL name is hashed


DLL NAMEZ<null>
FUNCTION NAME<null>
Function name is hashed
FUNCTION NAME<null>
<null>
Data strings are stored as
DLL NAMEZ<null>
they are.
FUNCTION NAME<null>
FUNCTION NAME<null>
<null>
<null>
DATA<null>
DATA<null>
DATA<null><null>

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

super
User-mode mapped memory
Allocate

Inject Code
Branch to it
Done executing
“Holding pen” Cloak

Fill w/ AAAA
Hook Fires
Uncloak

Branch to code
Done executing
Cloak
Process repeats whenever hook fires

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The sum of all this


• We combine injected payload w/ cloaking
and thread hijacking to FORCE in-game
events
– Spell casting
– Movement
– Chat
– Acquire and clear targets
– Loot inventory

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
super
MAIN
THREAD INJECTED
CODE PAGE
RenderWorld(..)
HARDWARE BP uncloak
MAIN
THREAD

branch

complete
CastSpellByID( .. )
ScriptExecute( .. )
ClearTarget( .. )
MSG

RenderWorld(..)
MAIN
THREAD recloak
restore

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

The final product – “Anti-Freeze”

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Anti-Freeze

• It uses LUA scripting


• It uses SUPERVISOR
• As long as we don’t release it, it will
probably NEVER be detected by Warden
• Any Investors ?? ?

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

A Better Warden Client

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Better tricks
• Check timing of operations
• Scan the stack
– Should be predictable for legitimate calls
– Anti-warden would need quite a lot of additional logic to work
around it
• Use better and variable encryption schemes
– Causes transparent proxy developers a great deal of headache
• Perform warden scans from within functions, not just
from one place – try to catch injected memory while it is
uncloaked
• Play games with exceptions to see if you can catch the
debugger
• Use up all the DR registers yourself
• GO TO THE KERNEL!

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

FOTU - FearOfTheUnknown
• On the fly detection program, updated at any
time
• Requires positive response
– Scans for stuff you know should be there
• Uses negative response
– Scans for stuff you know shouldn’t be there
• Puts the go/no-go decision on the server
– User cannot control a decision, only the presentation
of memory
• Disseminate multiple versions via user account,
physical location, and server
– R/E’s can never get their hands on all of them

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Applications to Tamper Proofing
• Use Supervisor-like technology to make
reverse engineering more difficult
– Effectively prevent user & kernel-mode
debugging against your active process
– Use memory techniques to decrypt code and
data for short periods, frustrating debugging &
reverse engineering efforts
– This is already being done commerically

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Applications to Tamper Proofing


• Use supervisor-like technology to create a
more secure container for code and data
storage
– Intellectual property control
– Sensitive information control
• Scan application data to detect improper
use, security violations, etc

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Applications against malware
• Use supervisor-like technology to debug
and trace malware that includes anti-
debugging tricks
• Monitor compromised systems covertly,
even when those systems contain anti-
forensics tools
• Monitor an attacker over time without
being detected

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

Applications against insider threats


• Corporate Espionage now out-ranks
malware as the #1 security concern in the
Enterprise (analyst firm)
• Espionage cost US companies approx
$250 Billion dollars in 2005 (FBI)

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund
Covert monitoring can be used to
track and assess insider threats

Yes, there is a role for stealth in


the Enterprise world.

-John Pescatore
Gartner Group

Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

UPCOMING BOOK!

Exploiting Online Games


Greg Hoglund
Gary McGraw

To be published in 2007 by Addison Wesley


Hacking World of Warcraft™ - an exercise in advanced rootkit design ©2006 Greg Hoglund

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy