- Joined
- Mar 21, 2016
- Messages
- 2,985
Can you please make it XP compatible?
Probably not as XP is kind of not supported anymore. Would mean having to use older, pre Vista APIs to do stuff in general.Can you please make it XP compatible?
Are you the developer of War3Loader?Probably not as XP is kind of not supported anymore. Would mean having to use older, pre Vista APIs to do stuff in general.
No, but if it was easy then surely the developer would have made it support XP already? As a software engineer I know that Windows changed a lot of its APIs between XP and Vista to make them more future proof, and these new APIs are not supported by XP. Microsoft has dropped pretty much all support for XP by now.Are you the developer of War3Loader?
Which requires one to download and install the XP compatibility module of VS, something not necessarily done by default (unless installing on XP for obvious reasons). If using C++ one then needs to change the windows version define macro to be appropriate for XP so that a suitable windows header is used (defaults to Windows 10). One then needs to remove all Windows Vista and later API calls, which might reduce compatibility for Vista and later operating systems as there is a reason they were added in the first place. It can turn out to be as simple as a rebuild, but also it could turn out to be impossible.Well, clicking on the appropriate checkbox in Visual Studio 2015/2017 so that it produces an exe compatible with XP, would be a first step.
I notice that War3Loader.exe seems to call the debug libraries, if I understand correctly what VirusTotal says about its PE imports.
@Dr Super Good:Microsoft has dropped pretty much all support for XP by now.
Hey, I have tested it. It's working, but sometimes the map crashed when loading. Only sometimes...
Btw, this is supposed to work on 1.28.1 and above right?
Just comment out all BJDebugMsg in a function called "StackSearcher".After replacing pjass.exe I imported to clean map: Typecast, Memory, Version, Bitwise, ObjectData, Utils libraries (from 1st post). Got error on saving: "Undeclared function Int2Hex()"
in library "Version". Do I need additional lib?
GetAbilityCurrentCooldown
call BJDebugMsg("Warning! Unsupported version!\nVersion-specific offsets have not been initialized!")
in Init function, it results in crash.DisplayTimedTextToPlayer
, it also results in crash. if i == 2894996 then
set PatchVersion = "1.28"
call Init28()
elseif i == 2889044 then
set PatchVersion = "1.27b"
call Init27b()
elseif i == 2586768 then
set PatchVersion = "1.27"
call Init27()
elseif i == 5205600 then
set PatchVersion = "1.26"
call Init26()
else
I did the same 2 steps as you with no crash (tried few times to run war3),- If I removed line:
call BJDebugMsg("Warning! Unsupported version!\nVersion-specific offsets have not been initialized!")
in Init function, it results in crash.
- If I replace it with other function like
DisplayTimedTextToPlayer
, it also results in crash.
//# +nosemanticerror
?Not exactly. I use TH's JNGP.do you have jass parser file as linked in first post? I do,
Nope. I don't remove anything else than what I mentioned above.did you move/delete commands
In short if I modify anything in that function, it will result in crash. I have re-confirmed it over and over again, and that's really what's happening here. I literally have no idea what's going on here.
exitwhen pJassContext*65536/65536 == 0x88 and pJassContext>65536 and Memory[pJassContext/4+8] == f+888
function Init
(f+888). So, if you modify ANYTHING in function Init, that offset will change, and stack searching will fail, causing a crash.Basically I use VM's instruction pointer (pJassContext/4+8) to verify that the candidate address is really the Jass Context that I want. The problem is, I am comparing that address to an offset from
function Init
(f+888). So, if you modify ANYTHING in function Init, that offset will change, and stack searching will fail, causing a crash.
But don't worry, I've already got a better search method, wait for it in the next hours.
If this much work was going into Memory Hack development, we'd truly have some wonderful things by now.
I retried several times. It works, no crashFirst step of detection is ready. Jass context and main game class are automatically detected now. Please test the attached map and tell me if it works properly. Soon I will be updating the other libraries to work with this code.
library Version initializer Init requires Memory//, HexNumber
because I have no HexNumber libI commented:
library Version initializer Init requires Memory//, HexNumber
because I have no HexNumber lib
game crashes everytime I call GetUnitArmor, GetHeroPrimaryAttribute or IsUnitStunned,
warcraft ver 1.27.1.7085.
(I had no problems with previous libraries)
function ConvertHandle takes handle h returns integer
return Memory[Memory[Memory[Memory[GameState/4]/4+7]/4+103]/4 + GetHandleId(h)*3 - 0x2FFFFF]
endfunction
must be something more to fix, it still crashes
function ConvertHandle takes handle h returns integer
return Memory[Memory[Memory[GameState/4+7]/4+103]/4 + GetHandleId(h)*3 - 0x2FFFFF]
endfunction
yes, it works perfectly for me now, thanks, IsUnitStunned if really cool functionIndeed, there was 1 "Memory[]" read more than the necessary. Now it is working.
function GetUnitAttackAbility takes unit u returns integer
return Memory[ConvertHandle(u)/4+0x1e8/4]
endfunction
function GetUnitBaseDamage takes unit u returns integer
return Memory[GetUnitAttackAbility(u)/4 + 0xA0/4]
endfunction
function GetUnitAttackSpeed takes unit u returns real
return RMemory[GetUnitAttackAbility(u)/4 + 0x1B0/4]
endfunction
function GetUnitAttackSpeedSec takes unit u returns real
local real r = GetUnitAttackSpeed(u)
return 1 / r
endfunction
function GetUnitAttackBackswing takes unit u returns real
return RMemory[GetUnitAttackAbility(u)/4 + 0x190/4]
endfunction
function GetUnitZ takes unit u returns real
return RMemory[ConvertHandle(u)/4 + 0x28C/4]
endfunction
// I made this type-safe, thanks for the 'AHer' ability field!
function GetHeroStatGain takes unit u, integer attribute returns real
if GetUnitAbilityLevel(u, 'AHer') != 0 then
return RMemory[Memory[ConvertHandle(u)/4 + 0x1F0/4]/4 + 0xCC/4 + 2*attribute]
endif
return 0.
endfunction
function CountUnitsInGroupEx takes group g returns integer
return Memory[ConvertHandle(g)/4 + 0x34/4]
endfunction
function IsUnitEthereal takes unit u returns boolean
return Memory[ConvertHandle(u)/4 + 0x1C0/4] != 0
endfunction
Library Version's 'integer GameState' variable doesn't seem to get initialized in 1.28.5 (it does in 1.26).
yes, it works perfectly for me now, thanks, IsUnitStunned if really cool function
by the way , I tryed GetUnitFlags(u) game prints 3..4 digit integers. Can you explain what it shows?
Can you please provide an older script code for mouse natives? They worked nicely and I can't find the code anymore. No need for any "mouseEnv".
Is there a way to access Gameplay Constants in-game, using Memory Hack? (without having to create a global and assigning the same value of a certain data field to that global)
Code has been updated, please check the changelong and get the updated versions of all libraries.
Are you sure? I always test it in 1.28.5 before releasing and it was working just fine. Try the new code now, things changed a bit but it should be working as well, I tested here and it worked.
I don't remember exactly, that function is from the time me and Draco were messing with memory hack and discovering new stuff. That integer contains a lot of flags, things like invisibility, invulnerability and so on. I don't recall the meaning of each flag, maybe someday I will make functions like IsUnitInvisible that make use of it. Maybe you can find some info at Draco's Memory hack thread too.
The old code was using fixed memory offsets, and because of that it only worked up to 1.28.1, since I needed to manually update offsets each time a patch was released. As I said I am migrating everything to automatic detection, so the mouse functions are disabled until I write the code to detect the mouse offsets.
But don't worry, I've already got a method to find these offsets, soon I will be releasing an implementation. It won't be too long, I promise.
Of course! Any information that is used by the game can also be retrieved with memory access, it's just a matter of finding where it is. For now I am still developing the basis of memory hack (address detection and base API), but in the future things like this shall be implemented and much more! Maybe gameplay constants are stored in a place that will require me to detect another game offset, I don't know, but it shouldn't be much of a problem.
Also don't forget that you have Draco's thread as a reference, even though his code is a bit messy he made a huge progress in many areas, and you can port many functions from his code to this library if they use offsets that are already being detected.
When compiling in debug mode (vJass) it doesn't work (the GameState variable is not initialized), but in release mode (vJass's inliner kicks in) it does work. I tend to always "stay" in debug mode =)...Are you sure? I always test it in 1.28.5 before releasing and it was working just fine. Try the new code now, things changed a bit but it should be working as well, I tested here and it worked.
This reminds me, on a slightly tangetial note. I was meaning to ask someone this. Excuse me if you've been asked this a ton of times already.
How do you find the offsets for each version? Do you use signature scanning for it, or have you been doing it manually in disassembly or something?
Can you please give a general overview of the procedure you use to update the offsets? Just what tools you use and the general outline of the process, not asking for a detailed tutorial, haha.
I've just been recently looking into a certain, but very useful tool for WC3 called Lua Engine (I think it's on d3scene), but it's outdated and it uses fixed offsets (a few hundred of them), which have obviously broken with new versions, and I'm looking to update it for the latest version of WC3 since I can use it for my map. You'd help me immensely if you gave me some pointers in which direction I should dig.
GetUnitPointValueByType
from patch 1.26. As you can see it hashes the rawcode, then calls GetObjectData passing the UnitData pointer as the 1st argument, in the ECX register.MOV EAX,[67B1C494]
in the place of MOV EAX,[ECX+24]
and MOV ECX,[67B1C48C]
instead of MOV EDX,[ECX+1C]
GetUnitPointValueByType
. You can easily find any native this way.Having a ton of experience in this area, I can tell you that there isn't a standard step-by-step way that will work for every case. Actually what introduced me to the programming world many years ago was exactly this matter: I got the source code of a hack for the game Gunz: The Duel and, having never programmed anything in my life, I managed to find the updated offsets and recompile the hack for a newer version of the game client.
Obviously I didn't figure everything just by myself. Someone on those forums had posted a simple tutorial of finding some offsets in gunz.exe through signature searching. It worked on that particular case, but this method isn't guaranteed to work everywhere, especially in the case of WC3.
More important than specific byte signatures, is the ability to recognize similar code patterns across different versions. This requires some disassembly and reverse engineering skills, but it's something you'll be acquiring with time. Let's take a look at an example:
View attachment 281150
This is the disassembly of the nativeGetUnitPointValueByType
from patch 1.26. As you can see it hashes the rawcode, then calls GetObjectData passing the UnitData pointer as the 1st argument, in the ECX register.
Now let's take a look at the same native from patch 1.27a:
View attachment 281144
Looks completely different, doesn't it? Where's the call to GetObjectData, and what is all this new code after IntegerHash? Did Blizzard change this function so drastically?
Now just take a look at GetObjectData itself, from 1.26:
View attachment 281145
Can you see the similarities? The code that comes after the IntegerHash call looks very similar to the code of GetObjectData itself. With the difference that we now haveMOV EAX,[67B1C494]
in the place ofMOV EAX,[ECX+24]
andMOV ECX,[67B1C48C]
instead ofMOV EDX,[ECX+1C]
The reason looks simple now: the call to GetObjectData has been inlined! And this is not something unexpected, because Blizzard changed the compiler for the 1.27 patch, migrating to a newer version of Visual Studio, which has a more aggressive optimizer. Many other function calls have been inlined like this.
So we can deduce, if [67B1C494] is [ECX+24], and [67B1C48C] is [ECX+1C], then the address of pUnitData is certainly 67B1C470. Subtract it from the base of game.dll (at this time it was 66F30000) and the result is 0xBEC470, the offset of the UnitData table for patch 1.27.
Well I suppose I did nothing but complicate the things for you until now right? As I said this job inevitably requires good disassembly skills, but I can give you some hints:
First step: Open game.dll from 1.26 and go to the offset you want. See if it's located in code or data sections, and find references to it.
As an example, I'll use the offset of MouseEnv object (game.dll+AB4F80), which gives access to mouse functions.
View attachment 281146
As you can see this object has 5 references, and 3 of them are in the same function. So the deal is, if we find this function in patch 1.27, we got our address.
Second step: now what you need to do is find something in this function that could possibly be used to search for it in the new patch. Strings and constants are always good candidates, WC3 has a lot of debug information in it's executables, like classes and source file names, and sometimes they helps us to find functions across versions.
View attachment 281148
Well, it found 42 references to the string "CGameUI.cpp". This is not so bad, in the worst case we could look at those functions one by one, and see if we find something more-or-less similar to what we are looking for. But let's try something different, the constant 1AAF:
View attachment 281149
Wow, we've got it! Only one reference, and it's exactly what we want! Just compare the two functions, and see that they are very similar. Some small changes here and there, like a conditional move (CMOVNZ) instruction in the place of a Jump, but as I said, this is just compiler optimization.
If you want more confirmation, just look at the function calls: they both are calling Storm.#401 with exactly the same parameters, and after that an object constructor (67279FA0) is called, which is also very similar to the one from 1.26 (6F2FE9F0) This is definitely the function we want.
So the address of MouseEnv in this version is 67B16350, subtract from base of game.dll and we get 0xBE6350.
If you cannot locate something noticeable that you can use to search, or if the search yields too many results, you can try looking for the callers and callees of that function. Try repeating the process above with a function that calls or is called by the function you want, and if succeed you will most likely find it too.
If the address you're looking is located in a Jass native, or a function that is called by a native, then it's bingo! All natives have well-defined addresses, and you can easily locate them through their name:
View attachment 281147
That is the initializer of the Jass natives table. The address of the native is in the MOV ECX instruction, right below the native name. In this case it is 6F3B2E20 for the nativeGetUnitPointValueByType
. You can easily find any native this way.
Well, these are some methods that I use to find offsets myself. Other things include looking at objects' vTables and searching for generic instructions (OllyDbg allows to search for generics like "MOV R32, [R32+24]" which returns any MOV instruction that fits this pattern, independently of the used registers). As I said, every case is specific, and it all depends on your skills.
I'm pretty sure you'll learn a lot if you really dig into this subject. Reading some assembly lessons is a good starting point (it is how I started myself, learned ASM before any other language). And don't be afraid to ask for any further help, if I don't help you, certainly someone else will.
Having a ton of experience in this area, I can tell you that there isn't a standard step-by-step way that will work for every case. Actually what introduced me to the programming world many years ago was exactly this matter: I got the source code of a hack for the game Gunz: The Duel and, having never programmed anything in my life, I managed to find the updated offsets and recompile the hack for a newer version of the game client.
Obviously I didn't figure everything just by myself. Someone on those forums had posted a simple tutorial of finding some offsets in gunz.exe through signature searching. It worked on that particular case, but this method isn't guaranteed to work everywhere, especially in the case of WC3.
More important than specific byte signatures, is the ability to recognize similar code patterns across different versions. This requires some disassembly and reverse engineering skills, but it's something you'll be acquiring with time. Let's take a look at an example:
View attachment 281150
This is the disassembly of the nativeGetUnitPointValueByType
from patch 1.26. As you can see it hashes the rawcode, then calls GetObjectData passing the UnitData pointer as the 1st argument, in the ECX register.
Now let's take a look at the same native from patch 1.27a:
View attachment 281144
Looks completely different, doesn't it? Where's the call to GetObjectData, and what is all this new code after IntegerHash? Did Blizzard change this function so drastically?
Now just take a look at GetObjectData itself, from 1.26:
View attachment 281145
Can you see the similarities? The code that comes after the IntegerHash call looks very similar to the code of GetObjectData itself. With the difference that we now haveMOV EAX,[67B1C494]
in the place ofMOV EAX,[ECX+24]
andMOV ECX,[67B1C48C]
instead ofMOV EDX,[ECX+1C]
The reason looks simple now: the call to GetObjectData has been inlined! And this is not something unexpected, because Blizzard changed the compiler for the 1.27 patch, migrating to a newer version of Visual Studio, which has a more aggressive optimizer. Many other function calls have been inlined like this.
So we can deduce, if [67B1C494] is [ECX+24], and [67B1C48C] is [ECX+1C], then the address of pUnitData is certainly 67B1C470. Subtract it from the base of game.dll (at this time it was 66F30000) and the result is 0xBEC470, the offset of the UnitData table for patch 1.27.
Well I suppose I did nothing but complicate the things for you until now right? As I said this job inevitably requires good disassembly skills, but I can give you some hints:
First step: Open game.dll from 1.26 and go to the offset you want. See if it's located in code or data sections, and find references to it.
As an example, I'll use the offset of MouseEnv object (game.dll+AB4F80), which gives access to mouse functions.
View attachment 281146
As you can see this object has 5 references, and 3 of them are in the same function. So the deal is, if we find this function in patch 1.27, we got our address.
Second step: now what you need to do is find something in this function that could possibly be used to search for it in the new patch. Strings and constants are always good candidates, WC3 has a lot of debug information in it's executables, like classes and source file names, and sometimes they helps us to find functions across versions.
View attachment 281148
Well, it found 42 references to the string "CGameUI.cpp". This is not so bad, in the worst case we could look at those functions one by one, and see if we find something more-or-less similar to what we are looking for. But let's try something different, the constant 1AAF:
View attachment 281149
Wow, we've got it! Only one reference, and it's exactly what we want! Just compare the two functions, and see that they are very similar. Some small changes here and there, like a conditional move (CMOVNZ) instruction in the place of a Jump, but as I said, this is just compiler optimization.
If you want more confirmation, just look at the function calls: they both are calling Storm.#401 with exactly the same parameters, and after that an object constructor (67279FA0) is called, which is also very similar to the one from 1.26 (6F2FE9F0) This is definitely the function we want.
So the address of MouseEnv in this version is 67B16350, subtract from base of game.dll and we get 0xBE6350.
If you cannot locate something noticeable that you can use to search, or if the search yields too many results, you can try looking for the callers and callees of that function. Try repeating the process above with a function that calls or is called by the function you want, and if succeed you will most likely find it too.
If the address you're looking is located in a Jass native, or a function that is called by a native, then it's bingo! All natives have well-defined addresses, and you can easily locate them through their name:
View attachment 281147
That is the initializer of the Jass natives table. The address of the native is in the MOV ECX instruction, right below the native name. In this case it is 6F3B2E20 for the nativeGetUnitPointValueByType
. You can easily find any native this way.
Well, these are some methods that I use to find offsets myself. Other things include looking at objects' vTables and searching for generic instructions (OllyDbg allows to search for generics like "MOV R32, [R32+24]" which returns any MOV instruction that fits this pattern, independently of the used registers). As I said, every case is specific, and it all depends on your skills.
I'm pretty sure you'll learn a lot if you really dig into this subject. Reading some assembly lessons is a good starting point (it is how I started myself, learned ASM before any other language). And don't be afraid to ask for any further help, if I don't help you, certainly someone else will.
When compiling in debug mode (vJass) it doesn't work (the GameState variable is not initialized), but in release mode (vJass's inliner kicks in) it does work. I tend to always "stay" in debug mode =)...
Quite a nice approach to something low-level. Reading that also made me want to go about this memory thing myself, too. However, while using the debugging software, should warcraft 3 be running?
Another thing, how did you get the index of the natives? (Some arbitrary R for a native N)
Very nice explanation.
Making hacks for Gunz was certainly a fun time. By now I don't remember anything about reverse engineering though![]()