Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
I haven't quite payed attention to this for a while... did blizzard fix this now or does it still work?
Has the feature only partially been fixed?
Can it still read memory? I am curious about the mouse thing of Aniki...
Can it still write?
If the typecasting library still works, and apparently it does, this whole project works as intended.
You have even been given long instructions on how to find and update addresses in the other thread.
If you as a community don't care enough to update this, it's your own fault, and that seems to be the case (before the ad hominem - I don't use Warcraft or Jass, so I obviously don't care about correct offsets).
That being said, I also don't quite understand why the people behind this library are so against cleaning it and making it usable to the regular joe.
That's kind of a key element of sharing code...that it be shareable.
we aren't against it, thats why its opened and non-obfuscated. we wrote it as is.
as pm of dota I had no time to clean code, and my teammates barely able to clean this stuff on themselves. instead of delaying it for another 6 months I just pushed it here.
will be updated with cleaned version when I get free time. idk.
The Memory Hack is FIXED! Write access is not possible anymore in patch 1.27b!!!
And here are the magical 2 lines of code that solved the problem:
So it seems that finally Blizzard decided to accept coding suggestions, and they did EXACTLY what I proposed to them: They fixed the real vulnerability in the VM, instead of just removing the ability to typecast.
This means that not only typecasting code variables is still possible in patch 1.27b, but also we still have read-only memory access working in this patch!
My memory-reading library is still working 100% after the update, it just needs the corrected offsets. I'll be updating it soon, and I'll also be integrating some functions from Draco's code in my library.
If you want to know more about the story and details of the now-fixed exploit, check my thread about it. Also, for reference, following is the email I sent to Blizzard with a detailed explanation of how the exploit works, and how they could fix it (and they did exactly what I suggested).
Dear Blizzard developer,
Yesterday I contacted Blizzard support, to report about a critical security issue that I just found in Warcraft III. It is possible to create an exploit and execute arbitrary machine code in a player's machine from inside a Warcraft map. The support responded quickly and told me to write to this email.
I don't know who will be reading this, but I know that Warcraft III has been created many years ago, and it's probable that you are not one of the developers of that game. Maybe Blizzard doesn't even have a specific team assigned to WC3 at the present moment, I don't know. But don't worry, I will explain everything about the exploit with all details.
Introduction
Warcraft III uses a special and unique language for the map script, the JASS language (an acronym for "Just Another Scripting Syntax"). It is a very verbose and easy to understand language.
JASS works in a way very similar to Java: when a map is loaded, the JASS script is interpreted and compiled into JASS bytecode, which is then executed by the JASS VM. It is a very simple VM, it has a little more than 30 operations, and is entirely implemented within a single function of the Warcraft code.
There are 6 basic types in JASS: integer, real, string, boolean, handle and code. The code type works as a function-pointer type: you assign a function to it, and it will hold the memory address of the bytecode of that function. Then you can pass that address to any native that executes code, such as ForGroup.
The JASS language is strongly-typed, which means that all variables must have their type specified in the declaration, and cannot be assigned a value of another type. However, we have the ability to typecast values between one type and another. This capability was not planned by Blizzard, but was discovered by the map making community many years ago.
Typecasting values can be used for many things. When it was discovered, it started a whole revolution in Warcraft map making. It allowed map makers to develop very sofisticated custom maps, creating things that were never thought to be possible, and turning WC3 into a very powerful game engine, with a huge amount of available resources.
We also have the ability to typecast function pointers. With that, it is possible to write and execute the JASS bytecode directly, instead of having it created from the map script. This brings many possibilities such as dynamic generation of code, and a dramatic speed increase, unlocking the VM to its full pontential.
The problem
However the JASS VM has a critical security issue that has been present ever since the game was released. With some special bytecode, it's possible to unlock the ability to read and write memory anywhere in the process, allowing a map to take complete control over the computer.
By the time of patch 1.23, some guy discovered the vulnerability and released a proof-of-concept map. This forced Blizzard to release the 1.24 patch, which fixed that particular exploit. However, that patch didn't solve the problem completely, it just patched the particular methods used by that code, but the real vulnerability is still present.
The vulnerability lies in current implementation of arrays in the JASS VM. Internally, an array variable is actually a pointer to a special structure, that is represented here:
JASS:
struct JassArray<T>
{
unsigned int maxSize
unsigned int currentSize
T * data
}
Of course we don't have the Warcraft source code, so I don't know the real names of the struct and its members, but this representation allows you to understand. Notice that the size of this struct is actually 16 bytes, because the first 4 bytes are reserved for a vTable.
Whenever the JASS VM is going to read or write an array index, it will check if the requested index is smaller than the current allocated size. If that's the case, it will simply read or write at the data pointer, offset by the requested index.
If the requested index is bigger than the current size, and it's a read operation, it will return 0. If it's a write operation, the array grows dynamically - a new memory area is allocated as necessary, and all data is copied there.
The problem is that it's possible to directly assign an address to an array variable, making it point to any struct we want. Consider the following JASS code:
JASS:
globals
integer array Memory
endglobals
function main takes nothing returns nothing
set Memory = 0x2114D008
endfunction
As you can see, this code is invalid. It tries to assign a value to an array variable, which is impossible. We can only read or write to the members of an array, not to the array itself. That makes no sense.
If you write that code in a map, the JASS parser will refuse it, and the map will not run. But if we make this operation directly with JASS bytecode, the VM will accept it!
Here is a representation of this operation in JASS bytecode:
As I already said, we have no way to know the real names used by Blizzard. These are the names that the community uses to describe the JASS opcodes.
The instruction LITERAL (opcode 0xC) loads an immediate value into a register of the VM. The VM has 256 registers, here we are using register 01. The number 09 is the type argument - in the VM all registers are type-safe, and when we load them with some value, we must declare the type. The number 9 corresponds to the type integer array.
The instruction SETVAR (0x11) is then used, to store the contents of R1 in a variable. The assignment only works if the type declared in the register is the same type of the variable. That's why we must use type 09 in the LITERAL instruction.
The number 0x2 is the variable id. In the JASS VM all operations handle variables and functions by their name. Those names are stored in the String Table - every string used in the game is stored there, and assigned a unique id. Then those ids are used in the JASS bytecode.
Notice that it's not possible to obtain the id of a specific variable at runtime. However, because the ids are assigned sequentially, it's possible to manually edit the scripts of a map and declare a variable at the very beginning of the code. With this, we have the guarantee that this variable gets the id #2 (the number #1 is reserved for the string "main").
After executing this bytecode, the variable Memory will point to the address 0x2114D008. This address is located in one of the DLLs loaded by Warcraft, and points to a 16-byte structure that is compatible with JASS arrays. On that structure, the currentSize of the array will be a very large number, and the data pointer is 0.
With those values, the Memory array will be able to read and write memory to the entire address space of the process! And once we have that ability, we can easily take control of the machine using standard code injection techniques.l.
How to fix it
All array types in the JASS VM have a number greater than or equal to 09. To prevent the JASS bytecode from tampering with an array you just need to check the type whenever a write operation is performed, and allow it to execute only for types smaller 9, which covers only the basic types.
You will find that all write operations are made by a single function, that is called in many places from the JASS VM. All VM operations that write data to a variable or register use that function. It already provides type-safety, so that you can't write a value of a type to a variable of another type.
So, in addition to the type-safety check, it also needs to check if the destination type is >=9, and if it is, don't write the data. If you need a reference, just look at the implementation of the SETARRAY operation (0x12). You will see that it checks if the variable is an array type, and if it isn't, nothing is done. You just need to implement that same check in the function that writes to variables and registers.
With this patch, the JASS VM will be completely secure against all possible hacks with bytecode. Arbitrary code execution from within a map script will never be possible again. It's a very simple fix, and since doesn't remove any feature from the JASS language, it won't break compatibility with any already existing map.
And btw, it would be nice if you can also take a look at The Hive Workshop forums, specially at the Patch 1.27 wish list. We have been waiting many years for a new patch, and we would love to see some new features that could aid in map making. I know that many things in that list might be pointless or not worthy implementing, but maybe you can make some very simple changes like increasing (or removing) the multiplayer map size limit in this new patch. That would help the map making community a lot.
Anyway thank you very much for your attention! I hope you can fix this exploit and release a new Warcraft patch before someone else discovers about the exploit too. If you need any help with fixing that, or have any doubts about my explanation, feel free to reply to this email. I will gladly help with anything to solve this issue.
Thank you Blizzard developer, and have a good day!
Good job Blizzard! This time I must agree that you did the best for everyone! Thank you!
Disgusting. Blizzard playing the part of King Retardo by crippling the people that keep the game alive. RIP
Since Bnet is useless now, what other services are there to play War3 on? I used to know of Garena, but I think now you need a VPN to connect if you're in the US. GameRanger seems like a good option.
Disgusting. I continued working on API for this because it was alleged that it was still working, just needed new offsets. Blizzard likes playing the part of King Retardo by crippling the people that keep the game alive. RIP
Since Bnet is useless now, what other services are there to play War3 on? I used to know of Garena, but I think now you need a VPN to connect if you're in the US. GameRanger seems like a good option.
You can still use the parts of the API that work with read-only access, such as functions that retrieve unit stats and alike. The code just needs to be migrated to my library and it will work as it is.
Surely many people will miss the write access, but that's the price you pay for safety. If you really want it, you can just continue with the old patch and tell your playerbase to not install the update if they want to keep playing your map.
Yeah, thats what I was saying. Besides, if Preload isn't fixed, then there still is no safety. After these are all fixed, there will probably STILL be exploits out there. It's just unfortunate Blizz did this.
Yeah, thats what I was saying. Besides, if Preload isn't fixed, then there still is no safety. After these are all fixed, there will probably STILL be exploits out there. It's just unfortunate Blizz did this.
AFAIK they won't be "fixing" the preload bug anymore than they already have. The most you could do with this bug is fill up someones hard drive.
These changes are good IMO, and if someone needs write access to memory they can stay on a previous patch. The fact that Blizzard allows us to read from memory is great, and the current changes show that they're listening to us.
Karaulov presents:
Create d3d9.dll and opengl32.dll files in Warcraft III folder , that would kill the game!
Look at f_cking Blizzard protection !
Code:
call Preload("Blizzard has only stupid bad coders")
call Preload("Crash Warcraft III at next run")
call Preload("All rights reserved! (Karaulov)")
call Preload("For Warcraft 1.27b")
call PreloadGenEnd("./../d3d9.dll..................................................................................................................................................................................................................................................pld")
call PreloadGenEnd("./../opengl32.dll..............................................................................................................................................................................................................................................pld")
So it seems that finally Blizzard decided to accept coding suggestions, and they did EXACTLY what I proposed to them:...
To prevent the JASS bytecode from tampering with an array you just need to check the type whenever a write operation is performed, and allow it to execute only for types smaller 9, which covers only the basic types. ...
It takes a bunch of Blizzard programmers (with access to source code, etc.) to come up with this "fix" which it seems wasn't much liked by the wc3 mappers/folks back then.
While it takes 1 guy? with only disassembly at their disposal to properly fix/protect the Jass VM from writing memory at arbitrary locations.
PS: I guess Blizzard were rather busy implementing the hashtable natives (and Diablo 3?) and decided to go the easy/faster route instead of trying to understand and fix the exploit.
PPS: There's still read memory access so that's nice! =)
Groups actually holds a count of units inside of it local integer c=ReadMemory(ConvertHandle(g)+0x34)
Probably BJ alternative which goes through ForGroup been created to deal with "leaks" like removed units which aren't deleted from the group. But there's no reason for this one to be hidden anyway.
Second funny thing - FirstOfGroup actually returns the latest unit of group.
Unit pathing consists of 3 fields, which is belong to unit's ID struct.
One of them define unit movement behaviour and the way pathfinder threats unit. That's flag-based value so it allows to combine multiple types, tho unsure if it ever works.
Second field describe how unit interacts with other pathfinders (e.g. doesn't block pathing for other units)
Third field stands for pathable tiles I believe (0x1 = cannot move, 0x2 = unknown, 0x4 = can go througth anything, 0x8 = blocked by items (default windwalk))
In order to apply changes we have to call SetUnitPathing (true) so game will convert changes to desired unit. Effect lasts until this native invoked with default values (mean you altered unit's pathing to flying, for instance, and then disabled that so other units of the same ID won't have it), or another altering effect (windwalk) called.
This allows to swap unit's pathing on fly with minimum efforts, tho bit flags kinda unknown yet, but there's really just few of them - flags above 0x10 aren't checked anywhere as far as I can see.
Btw, flying units changes height based on nearby cliffs to have a smooth transition when flying into cliffs so I guess NONE movement type is still pathing with least computation involved?
Btw, flying units changes height based on nearby cliffs to have a smooth transition when flying into cliffs so I guess NONE movement type is still pathing with least computation involved?
Third field stands for pathable tiles I believe (0x1 = cannot move, 0x2 = unknown, 0x4 = can go througth anything, 0x8 = blocked by items (default windwalk))
obviously I don't know how they've made autocast, other than that it only ever exists hardcoded for me. are you able to add "turn on autocast" UI on an ability that doesn't normally have it, for example?
nope, UI being redrawn on every possible action, so even if you manage to fool game, you have to make ground for that. why won't use default autocast tho?
nope, UI being redrawn on every possible action, so even if you manage to fool game, you have to make ground for that. why won't use default autocast tho?
it's just an example. abilities have a lot of UI behaviors like if a unit remembers their order after using it, the "already on" thing wind walk does, fok/hex type cast time differences, channel, orbs, targeting weird things like corpses, ivory tower targeting and probably several I didn't think of. normally only way to use those is to use the base ability that has it, with all the effects and limitations that comes with.
we've successfully implemented a unit with 3 abilities based on berserk, modifying base order, blocking invoking effect (means it stacks with normal berserks). So yeah, you can make use of abilities w/o calling it's hardcoded functions, at least some of those.
anyway, for movements:
field1
//0 = "none", 1 = "foot", 2 = "fly", 4 = "horse", 8 = "hover", 16 = "water"?, 32 = "amph"
field 2
//0xCA by default
//seems to be TexturePlacement analogue
//0 = pathable for others, 1=0, only 2+8 (A) makes unit non-pathable
field 3
//0 = pass through anything, 1 = cannot move, 2= default, 4 = default for fliers,
//8 = ww (stuck at items, can't go thtough units/buildings, pathable for others)
//0x10 = can go through units but not items/buildings
//0x20 = free, 0x40 = cant move, x80 == 2 ? , above == 0
FML
it looks like disabled pathing but bit easier - double click force shortest path pretty much 100% cases. Guess pathing type being cached on unit's creation, so only proper way to manipulate it is using flying units as the base one, and changing them to ground unit on creation. Then, if you toggle them to fly, they won't become retarded
well game.dll+308c7c stands for wheel usage. if you place your hook inside there, properly cleaning out existing code, and make some ASM injection, you may be able to detect wheeling. else I believe it's inpracticaly to check with IsKeyPressed, since it takes very short time to spin.
but in case if you wanna disable spinning I have this for 1.26
game.dll+308c7c :
e8d91a74 = wheel cam
e8d91a75 = wheel cam disasbled
Funny thing - cleave abilities ONLY works if they have COOLDOWN flag (0x200), which is setup on foreswing. Using writing I can support any melee unit cleave while doing orb-attack. delicious
function GetUnitStringParam takes integer id, integer off returns string
local integer k=GetUnitUIDefAddr(id)
if k < 1 then
return ""
endif
// call echo(Int2Hex(k))
set k=(k+off)/4
if Memory[k]>0 then
return ConvertNullTerminatedStringToString(Memory[k])
endif
return ""
endfunction
Because strings are stored at second level so you need to use GetAbilityStringParam2
JASS:
function SetUnitUIStringParam takes integer id, integer includedLevel, integer offset, string val returns nothing
local integer k=GetUnitUIDefAddr(id)
if k < 1 then
return
endif
call echo("Set str for "+Id2String(id)+" for val="+val)
set k=(k+offset)
if includedLevel==0 then
call WMem(k,GetStringAddress(val))
else
if includedLevel==1 then
if RMem(k)<1 then
call WMem(k,GetStringAddress(val))
endif
call WMem(RMem(k),GetStringAddress(val))
endif
endif
endfunction
function GetUnitUIStringParam takes integer id, integer includedLevel, integer offset returns string
local integer k=GetUnitUIDefAddr(id)
if k < 1 then
return null
endif
set k=k+offset
if includedLevel==0 then
if RMem(k)>0 then
return ConvertNullTerminatedStringToString(RMem(k))
endif
else
if includedLevel==1 then
if RMem(k)>0 and RMem(RMem(k))>0 then
return ConvertNullTerminatedStringToString(RMem(RMem(k)))
endif
endif
endif
return null
endfunction
function GetUnitUbertip takes integer id returns string
return GetUnitUIStringParam(id,1,0x268)
endfunction
function SetUnitUbertip takes integer id, string s returns nothing
call SetUnitUIStringParam(id,1,0x268,s)
endfunction
where RMem == ReadRealMemory & WMem==WriteRealMemory
Because strings are stored at second level so you need to use GetAbilityStringParam2
JASS:
function SetUnitUIStringParam takes integer id, integer includedLevel, integer offset, string val returns nothing
local integer k=GetUnitUIDefAddr(id)
if k < 1 then
return
endif
call echo("Set str for "+Id2String(id)+" for val="+val)
set k=(k+offset)
if includedLevel==0 then
call WMem(k,GetStringAddress(val))
else
if includedLevel==1 then
if RMem(k)<1 then
call WMem(k,GetStringAddress(val))
endif
call WMem(RMem(k),GetStringAddress(val))
endif
endif
endfunction
function GetUnitUIStringParam takes integer id, integer includedLevel, integer offset returns string
local integer k=GetUnitUIDefAddr(id)
if k < 1 then
return null
endif
set k=k+offset
if includedLevel==0 then
if RMem(k)>0 then
return ConvertNullTerminatedStringToString(RMem(k))
endif
else
if includedLevel==1 then
if RMem(k)>0 and RMem(RMem(k))>0 then
return ConvertNullTerminatedStringToString(RMem(RMem(k)))
endif
endif
endif
return null
endfunction
function GetUnitUbertip takes integer id returns string
return GetUnitUIStringParam(id,1,0x268)
endfunction
function SetUnitUbertip takes integer id, string s returns nothing
call SetUnitUIStringParam(id,1,0x268,s)
endfunction
where RMem == ReadRealMemory & WMem==WriteRealMemory
globals
//26
set pDrawTextAtResourcePanel=GameDLL+0x60CA10
//27
set pDrawTextAtResourcePanel=GameDLL+0x0C1020
endglobals
function DrawTextAtResourcePanel takes integer panelAddress, string s returns nothing
local integer a=GetStringAddress(s)
if a>0 and panelAddress>0 then
call CallThisCallWith2Args(pDrawTextAtResourcePanel,panelAddress,a)
endif
endfunction
//where panelAddress can be anything, but you gonna use
local integer panelAddress=GetFrameTextAddress("ResourceBarGoldText",0)
set panelAddress=GetFrameTextAddress("ResourceBarLumberText",0)
set panelAddress=GetFrameTextAddress("ResourceBarUpkeepText",0)
set panelAddress=GetFrameTextAddress("ResourceBarSupplyText",0)
globals
//26
set pDrawTextAtResourcePanel=GameDLL+0x60CA10
//27
set pDrawTextAtResourcePanel=GameDLL+0x0C1020
endglobals
function DrawTextAtResourcePanel takes integer panelAddress, string s returns nothing
local integer a=GetStringAddress(s)
if a>0 and panelAddress>0 then
call CallThisCallWith2Args(pDrawTextAtResourcePanel,panelAddress,a)
endif
endfunction
//where panelAddress can be anything, but you gonna use
local integer panelAddress=GetFrameTextAddress("ResourceBarGoldText",0)
set panelAddress=GetFrameTextAddress("ResourceBarLumberText",0)
set panelAddress=GetFrameTextAddress("ResourceBarUpkeepText",0)
set panelAddress=GetFrameTextAddress("ResourceBarSupplyText",0)
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.