Research: Last Epoch - Doing It Wrong..

19 minute read

Most people that know me, know that my favorite genre of games is ARPGs and MMOs. Without a doubt, Diablo has been my favorite franchise of the genre by far. I still play Diablo 2 to this day on and off, between the original and with mods (MedianXL for example). To me, Diablo II is probably one of, if not, the best games ever made still to this day. Diablo, as a whole, has completely shaped the dungeon crawler, endless grind for loot ARPG genre. Sadly though, we have not really seen that many good ARPGs come to life since Diablo II. And Diablo III was (and still is) a complete cluster fuck destroyed by Activision. (And lets not even get started on Diablo: Immortal…)

With that, I have been keeping my eye on various titles in the space over the last handful of years. Recently we got the much long awaited Wolcen, however not everything that takes a while to release comes out good. Wolcen sadly had a lot of promise, then decided to ditch their own ideas in favor of copying Diablo 3 instead. Since then, the updates have been lacking in quality and the game as a whole is basically dead already.

The next major title to have any real interest in the community has been Last Epoch. However, they have had their own fair share of disappointments and issues during their current Early Access. I have mostly stayed away from grabbing the game yet because of the many issues and bad feedback so far, as well as the developers ego’s. Nothing is more off-putting than a game with lead devs that can’t take critism, feedback, or anything that isn’t positive / sucking their dicks to make them feel like they are gods. (Grim Dawn ring a bell anyone lol?)

The game recently went on sale so a friend of mine and I decided to grab it for cheap and check it out. What I am going to get into next is a complete cluster fuck of stupidity. Between terrible design choices, complete lack of security, and absolute terrible PR handling, Last Epoch is an example of how to do everything wrong.

The sad part is, the game is not that bad. But the team behind it (or at least the ones handling their PR) are absolutely terrible. Lets dig in..

Preface

Before I begin I want to make a few things clear. This is going to be both a research piece and a bit of a rant. While it is fun to laugh at stupidity of developers who refuse to listen or even attempt to work with someone trying to help them, some things are not ok.

  1. Piracy. I DO NOT condone piracy. Pay for the games you play. Regardless of how stupid 1-2 developers or PR team members may act, they are not the entire company. Tons of people work on these games and need to be paid for their time and hard work. Regardless if you hate the developers, pay for the game.

  2. Trolling. I Do NOT condone anyone going and acting like assholes to Last Epoch’s team at all. The story of my experience is going to probably piss some people off and make some want to go be dicks ‘for the memes’, but simply put; don’t. This was my experience, I’ve dealt with it already and have since moved on. There is no need for anyone to go act-out on my behalf. Simply put, just don’t get the game if you feel the need to do anything.

Getting The Game

To start, my friend and I purchased the game via a streamer on Twitch who partnered with a third-party reseller that was selling the game for 25% off at the time. (chrono.gg via ZiggyD on Twitch) Purchasing the game was instant and no issues at all. A key is sent to activate on Steam then you simply link it to your account on Last Epoch’s website. Again, all easy and simple. Once registered you are given two options to get the game:

  • Install via Steam.
  • Install via stand-alone launcher.

I opt’d for the Steam install as most of my games are via Steam, so it’s easier for me to have it all in a central location. Either version is fine and both are literally the same thing.

Taking a Peek

One of the first things I generally do with any game I buy is check out what its coded in, if its protected with anything, how it’s packaged, etc. As a game hacker and reverse engineer, it’s one of my things I do to basically anything I put on my system. Once the game was installed, taking a peek revealed a few key factors:

  • It is a Unity game.
  • At the time of install; it was not protected. (Stock Unity, Assembly-CSharp.dll present and unprotected.)
  • No use of SteamStub on the main exe.
  • No use of the SteamAPI library.

After seeing that, it led to a few jokes with my friend and a second look to check out the Assembly-CSharp.dll in dnSpy to see what all was there and if anything was obfuscated. To my surprise, none. The game is fully unprotected and just stock compiled with Unity. I was quite surprised that they hadn’t done anything to protect the game files at this point.

A few updates later, they did convert to using il2cpp instead.

First Launch..

My friend and I both knew before hand the game had some means of ‘online’ requirement based on what the Last Epoch devs have mentioned all over their site and forum. The game would be ‘online’ until early access was over, then when fully released, it would include a full offline and online mode separately. For us, we didn’t care about that for now, but definitely liked that an offline mode would be included in the full game later on.

Upon starting the game, you are greeted with a login screen. This asks you to log into your Last Epoch site account. This is used to verify you own the game and are allowed to play. Knowing that the game is completely unprotected, I figured this meant they were using some means of the Steam API or other more in depth means of validation to ensure only actual owners of the game were playing it.. nope!

Last Epoch API… Written by a StackOverflow copy paster?

Once I saw the login screen, I knew I wanted to dig into the game to see how they were handling authentication. It was interesting to see if they were doing anything special here because the game was completely unprotected. Oh boy was I in for a major “what the actual fuck”…

Instead of anything more modern and secure, I find a poorly planned out PHP API backend the game uses currently to deal with every aspect of any remote callback. For login, the game makes a simple POST request to the API via:

private string devUrlAffix = "dev.";
private string GetApiUrl()
{
    if (this.useDevServer)
    {
        return "https://" + this.devUrlAffix + this.urlBase + "/api/";
    }
    return "https://" + this.urlBase + "/api/";
}
List<IMultipartFormSection> list = new List<IMultipartFormSection>();
list.Add(new MultipartFormDataSection("email", username));
list.Add(new MultipartFormDataSection("password", password));
using (UnityWebRequest www = UnityWebRequest.Post(this.GetApiUrl() + "login.php", list))
base.StartCoroutine(SocialManager.instance.CallAPILogin("login", "https://lastepochgame.com/api/login_status.php"));

public IEnumerator CallAPILogin(string logStatus, string link)
{
    this.callAPIRunning = true;
    WWWForm wwwform = new WWWForm();
    wwwform.AddField("access_token", this.accessToken);
    wwwform.AddField("log_status", logStatus);
    using (UnityWebRequest www = UnityWebRequest.Post(link, wwwform))

Further following the login process shows us also how the game pulls friends list information and accessible DLC items as we get a free pet and such for backing the game currently. Upon a successful login, the client requests social information via:

base.StartCoroutine(this.SocialRefresh(this.baseURL + "social-lists.php"));

public IEnumerator SocialRefresh(string link)
{
    if (!this.callAPIRunning)
    {
        WWWForm wwwform = new WWWForm();
        wwwform.AddField("access_token", this.accessToken);
        using (UnityWebRequest www = UnityWebRequest.Post(link, wwwform))

So from this we can gather some basic information about how this API works:

  • API calls are made to the url https://lastepochgame.com/api/ in varying manners.
  • Successful logins give us an access_token to use with following API calls to tell the API who we are.

From here, I assumed the way they would send this data would be encrypted or something, but also no. They rely only on the basic HTTPS protocol handling of encryption, meaning anyone with Fiddler or WireShark can monitor the traffic and decrypt it locally without any trouble. So I did that next.

I fired up Fiddler, enabled HTTPS traffic decryption and targeted Last Epoch directly. I already knew the requests being sent, so I was more interested in the responses. (Keep in mind, the game was completely unprotected at this point so you could build the responses manually from the game files anyway.) Upon attempting to login and select my character, we see a few API calls.

  • POST: login.php - Used to login and validate.
  • POST: login_status.php - Used to ‘set’ our login status with the server.
  • POST: social-lists.php - Used to obtain our friends list information.
  • POST: users_items.php - Used to obtain our available DLC/cash shop content.

There’s a handful of other API calls, but none of them were really important or interesting to me.

Login Validation

The first thing we see upon entering some info and trying to play is the login.php call. This is used to log into the game and validate your access.

This API call returns a json blob containing the following information:

{"success":true,"msg":[],"data":{"access_token":"<token removed>","user_login":"atom0s","user_nicename":"atom0s","user_email":"atom0s","display_name":"atom0s","first_name":"","last_name":"","description":"","roles":["subscriber","alpha_backer","beta_backer"],"id":0}}

(Access token, email, and id changed for obvious reasons.)

So we can see that they just use this call to tell who can play the game. The main thing here being the overall success of the function and what’s returned in the roles array. Since the game uses the following to verify role access:

if (text.Contains("qatest") || text.Contains("administrator") || text.Contains("alpha") || text.Contains("beta"))

If no roles match, you are presented with an invalid access screen instead.

This is all there is keeping people from getting into the game.

Getting Banned For Literally Nothing

Please understand, I have not shared any of this info before making this post outside of my friend and I. I never posted how to do any of this or what to look for. So before you read the next information and screenshots, understand I have not done a single thing to warrant being banned the way I was.

I’ve browsed the games Steam community for basic bug reports and other information and happened across a few posts regarding offline mode. I felt like I could offer a basic input on what the game was doing and what was currently implemented. So I made a simple post:

https://i.imgur.com/0nygzAw.png

Nowhere in my post did I explain how to do any of this. I did not say where you could find such information, I did not encourage or promote it either. I simply said it was possible. This is no different than anyone saying something on the lines If I had a gun, I could rob a bank. Are they going to? Should they be arrested for simply saying something is possible? No. That’s completely insane.

Yet I was banned, without any warning or reason, completely from their Steam community. However, I figured that was it. Nope!

I attempted to appeal the ban via Steam but they completely banned me from communicating with them. I reported the ban to Steam given that Steam has some guidelines on communities to give valid reasons to bans and that bans should be the last resort. But obviously Steam is useless at adhering to their own guidelines and told me to contact the game devs directly to get it sorted.

So I opt’d to try their website instead. Upon logging in, I was greeted with:

https://i.imgur.com/iEnu6EC.png

They completely blocked me from their site as well. Along with the game. They removed my access from the game, which I paid for.

Here is what the game looks like when you are blocked trying to login:

https://i.imgur.com/EVZO4A2.png

By this point, I had already made a cheat table and proxy for the game to bypass the online check/pings and junk, as well as to unlock all the cosmetics to play around with everything for fun. I opt’d to release this to my supporters only and not publicly. (Again, my goal in any of this is not to ruin their game or kill sales, so I wont be releasing the proxy publicly at all. But again, you don’t need my stuff to bypass this, you can literally bypass it with just Fiddler.)

As a last attempt to reach out to the devs to appeal the ban and talk to them about their crappy API and offer suggestions, I was met with this on Twitter:

https://i.imgur.com/BkjaYuE.png

At this point, I was dying laughing at how stupid this was and how bad they handle anything said about their game. I literally only said something was possible, nothing else.

So in summary:

  • I discovered how weak their login validation was.
  • I mentioned the game was actually offline-mode already and you could technically bypass the validation. (Didn’t mention how or where to find that info though.)
  • Was banned from the Steam community.
  • Was banned from the game website.
  • Was banned from the game itself.
  • Was blocked on Twitter.

How mature! These are supposedly professionals in the game industry lol?

Banned From The Game

So something that completely made no sense to me was being completely banned from their game for doing nothing. Not only did they ban me from the Steam community, the official site and block me on Twitter, but they completely banned me from the game. I was confused as to why they’d do that given that I paid for the game and didn’t do anything to warrant it at this point.

Trying to login would yield a new API return from login.php:

{"success":true,"msg":["User owns the game in steam"],"data":{"access_token":"<removed>","user_login":"atom0s","user_nicename":"atom0s","user_email":"atom0s","display_name":"atom0s","first_name":"","last_name":"","description":"","roles":["subscriber"],"id":0}}

(Access token, email and id removed for obvious reasons.)

So they removed my roles but still mark me as owning the game on Steam via the message. Cute.

At this point, I said screw it and made the proxy actually stand-alone, where as before I was going to make it a sub-domain on my site that I can control.

https://i.imgur.com/iDF53co.gif

I do not plan to release this for legal reasons, but simply put, this takes like 10 lines of code using the old FiddlerCore. You can accomplish the same results just faking the returns in Fiddler itself.

This is honestly a low-effort thing to bypass.

Cosmetics / Cash Shop Items

So another thing I looked into was the current cosmetics setup. This is handled via the users_items.php API call. This just returns a list of items available to your account when you login. For example, it would look like this:

{"success":true,"msg":[],"data":[{"item_id":"19","item_name":"Juvenile Skullen","item_quantity":"1"},{"item_id":"24","item_name":"Cosmetic Coins","item_quantity":"350"}]}

As you can guess, the game just sees this list and marks items in the panel in-game as usable or not based on what was received. So I dug into dumping the games item information and found all the current listed items. You can simply return this instead to unlock everything:

{"success":true,"msg":[],"data":[{"item_id":"0","item_name":"Cosmetic0","item_quantity":"1"},{"item_id":"1","item_name":"Cosmetic1","item_quantity":"1"},{"item_id":"2","item_name":"Cosmetic2","item_quantity":"1"},{"item_id":"3","item_name":"Cosmetic3","item_quantity":"1"},{"item_id":"4","item_name":"Cosmetic4","item_quantity":"1"},{"item_id":"5","item_name":"Cosmetic5","item_quantity":"1"},{"item_id":"6","item_name":"Cosmetic6","item_quantity":"1"},{"item_id":"7","item_name":"Cosmetic7","item_quantity":"1"},{"item_id":"8","item_name":"Cosmetic8","item_quantity":"1"},{"item_id":"9","item_name":"Cosmetic9","item_quantity":"1"},{"item_id":"10","item_name":"Cosmetic10","item_quantity":"1"},{"item_id":"11","item_name":"Cosmetic11","item_quantity":"1"},{"item_id":"12","item_name":"Cosmetic12","item_quantity":"1"},{"item_id":"13","item_name":"Cosmetic13","item_quantity":"1"},{"item_id":"14","item_name":"Cosmetic14","item_quantity":"1"},{"item_id":"15","item_name":"Cosmetic15","item_quantity":"1"},{"item_id":"16","item_name":"Cosmetic16","item_quantity":"1"},{"item_id":"17","item_name":"Cosmetic17","item_quantity":"1"},{"item_id":"18","item_name":"Cosmetic18","item_quantity":"1"},{"item_id":"19","item_name":"Cosmetic19","item_quantity":"1"},{"item_id":"20","item_name":"Cosmetic20","item_quantity":"1"},{"item_id":"21","item_name":"Cosmetic21","item_quantity":"1"},{"item_id":"22","item_name":"Cosmetic22","item_quantity":"1"},{"item_id":"23","item_name":"Cosmetic23","item_quantity":"1"},{"item_id":"24","item_name":"Cosmetic24","item_quantity":"1"},{"item_id":"25","item_name":"Cosmetic Coins","item_quantity":"100000000"},{"item_id":"999","item_name":"Halo","item_quantity":"1"}]}

Item names don’t matter, it does everything based on just the id and quantity currently. This will enable all items that the game will list at the point I made the proxy.

Ladders - Hack free right? Lol…

Next, I looked into the ladders. They currently have a competitive aspect available via an arena ladder. However, knowing the game is fully client sided, this seems pointless and easy to abuse. We can find the API call that manages the ladder pretty easily in the games Assembly-CSharp.dll:

	public void updateLadderForCharacter(CharacterData data)
	{
		if (data == null)
		{
			Debug.LogError("LadderManager passed null character data with which to update ladder");
			return;
		}
		if (data.getCompetitiveCharacterVersion() < GameVersionManager.getCompetitiveCharacterVersion())
		{
			return;
		}
		if (data.getLifeState() == CharacterLifeState.HardcoreDied)
		{
			return;
		}
		if (this.IamAcheater(data))
		{
			return;
		}
		this.ladderID = this.GetLadderID(data.getHardcore(), data.getSoloChallenge());
		if (this.ladderID == 0)
		{
			return;
		}
		if (!this.playerAbilityList)
		{
			this.playerAbilityList = PlayerFinder.getPlayer().GetComponent<PlayerAbilityList>();
		}
		if (this.playerAbilityList)
		{
			this.equipedAbilities = this.playerAbilityList.getEquippedPlayerAbilities();
		}
		this.SubmitLadderUpdate(data);
	}

This is their method of validating you didn’t cheat. The IamAcheater call is just:

private bool IamAcheater(CharacterData data)
{
    if (data.getLevel() >= 99 && data.getMaxWave() < 20)
    {
        Debug.Log("Error code: LE-21. Please contact support.");
        return true;
    }
    if (data.getLevel() >= 95 && data.getMaxWave() < 10)
    {
        Debug.Log("Error code: LE-20. Please contact support.");
        return true;
    }
    if (data.getLevel() >= 85 && data.getMaxWave() < 5)
    {
        Debug.Log("Error code: LE-19. Please contact support.");
        return true;
    }
    return false;
}

Error codes to try to make people out themselves as a potential cheater lol. Can only imagine they’ll probably just instant-ban those players too without warning. This game has a lot of bugs currently, some of which can cause you to become invincible. So ultimately there is a chance of causing the game to bug out and get into a state this checks for. Hope they don’t ban other innocent players lol.

My Opinions of Last Epoch

So here we are, banned from the game for no reason. Where do I sit with the game as a whole?

Firstly, my goal in all of this research and such is not to harm the game at all. I want the game to succeed, we need more ARPGs. So I want to be sure that people understand none of this was ever intended for malicious reasons. I am not that kind of game hacker / reverse engineer at all.

I have played the game pretty thoroughly and have multiple characters near max level and have done most of the current end game content (Monolith). I was building a fairly large list of bug reports, suggestions, and such to report back to the developers. Needless to say that wont happen now.

The game is good, I won’t doubt that. It definitely has its share of issues and performance problems. And there are a lot of bugs currently. It’s early access so it’s expected. But overall there is a solid foundation to an ARPG here.

The staff is terrible. To ban someone without reason for literally just saying something was possible is insane. For me this is a killer. The fact that the forward-facing team behind the game is this ready to just hit the ban hammer on people completely kills any interest in me playing the game further. I can only imagine when they start getting an influx of baseless reports, bugs/glitches that hit their servers with unexpected data, and just negative reviews/feedback, how many people are going to land up banned just because they can’t be mature.

Never once did they reach out to me to ask me anything. They just immediately perma-banned me.

They also made it impossible to appeal the ban or even talk about the fact that they removed my access from a single player game after I paid for it, which is literally fraud lol. (No worries, I charged back on PayPal and won given chrono.gg never responded.)

For this, I would not recommend this game or company to anyone. Especially at this point in time. Perhaps when the game fully releases and proper offline mode exists, then sure. That way you wont have to deal with them ever directly. Just buy the game, download and never go on their community/sites/etc. afterward.

Given this game will have offline mode, I can see big potential for modding and community driven content. Just hope that their developers get their acts together and stop acting like idiots like this.

Suggestions to Last Epoch Devs

In the event you guys read this, here’s some feedback:

  1. Ditch your current API, it’s literally trash. This is like, basic entry level PHP shit with 0 protection. There are other sites already dedicated to posting bypasses to your stuff for pirating the game.
  2. Move to il2cpp. You did this recently, although keep in mind since you did it so late into the development, anyone with old copies of your Assembly-CSharp.dll can easily line things up with the new binary. I would suggest also looking into other means of obfuscation and protection because of how you guys have coded things thus far.
  3. Don’t use a basic web endpoint for any means of auth/validation/etc. This should not be sent like this from a game client. Build an actual server, create a real protocol, encrypt your data.
  4. Your client is doing so many things in plain text for this kind of validation, stop…
  5. Get better at public relations (PR). If you are going to act like this going forward, you are going to kill yourselves with bad reviews from doing stupid shit like this.

The ARPG scene needs more games to exist. However, lately every new company that pops up has done pretty poor at various things. There’s no reason for these games to fail because of stupid shit like this.

Cheers for the ban though, it’s helped me avoid another potential disaster in the future.

Comments