Harmony Patching Transpilers - Identifying a Switch Statement and it's cases
I am having issues accessing the cases of a switch statement and adding to them with a Harmony Transpiler.
I was thinking that their may be specific op codes that pertain to cases but it seems that in IL, the operand of the switch stores the indexs of the case statements. Is there a way to, using the operand of the switch statement, figure out what case number is what and also be able to add a default case?
99 Replies
I expect the answer is no in general: while there may be a way to do this when you specifically have a switch IL op code, there is no guarantee that a switch in C# will get compiled to a switch in IL
What if i know that there is a switch in the IL
just theoretically

I know that this will be present in the compiled version
You'll probably want to look at what other decompilers do, like ilspy, and see what you can crib from them
DNSpy is not reliable in this case?
It's not open source so you can't look at it?
Oh, wait, I'm thinking of dotpeek
Yeah, it would probably be fine, though it is archived
I am using DNSpy to see the code
It has obfuscated names but that is it

Ok, this is when I ask: what are you decompiling
A C# executable
We both know that is not what I'm asking
It is a game from years ago
A game called SpeedRunners
Uses the FNA/XNA microsoft framework
.Net 4.7.2
I am sorry I am not 100% sure what else you could be asking
What I'm generally asking is if you're developing cheats for use in an online game against the terms of that game
No, I am developping a modding framework
I have direct contact with and permission from the developer
This game is like 12 years old
We have a small community around it and myself and a small team are designing a framework
The item system is super super messed up, so we have to resort to Transpilers to get it to work, but I am currently creating a system for adding custom items
I see, ok
Just making sure we aren't violating server rules here, that's all
All good, I can understadn the concern
let me copy this message out of chat then
if you had this
the IL is
Yeah
I saw this
But i see now labels in IL
labels arent really an IL thing
IL only uses offsets
The Operand of the Switch IL just contains the index and offset
Ah right
but it is convenient shorthand supported by ilasm
So should I be using the offset extracted from the operand to try and identify where cases are
Cecil will be giving you them as Instruction objects, iirc
which you can check the index of
(if thats what youre using)
I use DNSpy
They're using Harmony
Whatever that exposes
Oop[s
yeah
harmony mb
I forgot what Cecil is im a bit tired sorry lol
Transpilers return an IEnumerable of Opcodes
harmony gives them to you with their own wrapper over CodeInstruction
Yes
i think
yeah they use Labels
I see
When i used labels it did not find anything
https://harmony.pardeike.net/articles/patching-transpiler-codes.html
they turn all offsets into labels and back for you
labels just being a wrapper over the instruction theyre targeting, so you can swap out the target easily
Ah i see, so the label for an offset would match "1" as the case?
mmm this would just give you a
Label[]
when you look at the switchThe easiest method for me here was that I was just going to define a default case for the switch (As one doesnt exist and will never exist)
then just find the switch opcode and put stuff before the br that comes right after it
And then put my own custom switch statement in there that is generated based off the amoutn of custom items being added
I see i see
between these two

Right, and I wouldn't have to edit the operand?
yes
any time its given a number that isnt in the range it just goes to the next instruction
Gotcha gotcha, that makes sense
Ah soooo then I can add my own switch statement as the default case for switching modded items
i would recommend just immediately calling a helper method, if possible
instead of writing a bunch of IL
Yeah that was my plan
I was going to insert a hook here to run a helper
also damn if youre in contact with these developers try to get something that isnt obfuscated :kekw:
I did ask about this but he is hesitant to give away the code
Which is fair enough
He provides us snippets when we are stuck
but being obfuscated isnt the WORST thing in the world, we have made pretty significant attempts to rename plenty of variables and methods so that we can understand what is going on
let me give it a go!
Then i will resolve the issue
thank you guys so much for your help!
@Aaron It worked
THANK YOU SO MUCH
OpCodes are a bit new to me
:c0_catboysalute:
I am a robotics engineer who works primarily in C++ so this has been a learnign experience
the harmony discord is a fairly active place https://discord.gg/xXgghXR
I COULD NOT find a harmony discord
thank you so much
theres a bunch of others for C# modding depending on the area, but most of the ones i could link right now are unity related, or at a lower level than Harmony
Yeah this game isnt unity
So good if there is just a generic one
@cat I was wondering if you could assist further, I have built a decentish framework around adding custom items
But I have a question regarding accessing the variable that is switched on
The harmony server has had no active responses lol
I need to essentially access and set the value of the variable that is switched on, but I am unsure how to extract that from the opcodes

I see the variable is loaded above
yeah it's just in local 1
the sub is just to make the switch work, since it's 0 based, and they have no 0 case
oh ok
ohhhhhh
Shit ok yeah that makes sense
so i can actually just acess it as local 1
yeah you can just ldloc.1 before your call
or, if you also want to change it in the method, ldloca to pass it by ref
Ah ok ok ok
@cat The reference for writing doesnt seem to work
Forgive me if I have done this wrong but:
`
uhhh is anything actually using local 1 after that point
After which point
I was confused because i thought sub wouldve popped the stack
i.e: i wouldve needed to store the variable again
@333fred Do you know anything about this?
Harmony opcode insertion
mb, i am confusing things together
I thought using the Call opcode popped anything loaded onto the stack
as arguments
it does
I mean is local 1 actually used after this point by anything in the method
well BroadcastModdedItemUsed is the method referenced by broadcastMethod
I thought adding the "ref byte ID" would allow me to manipulate local 1
it does
but if nothing reads local 1, that doesn't matter
of course, because it is an address
ahhhhh C#
so does anything use local 1 after you change it
Well i guess because it is loaded into local storage not really
or do they all load the field again
The field is loaded again
This is why ah yes
Ok so i need to actually modify the global variable value
not the local copy in the method at this stage
seems so
Do you knpow how I owuld go about doing that
should just be able to ldloc.0 and write to the same field normally
assuming you are referencing this DLL, anyway
the value I am accessing is from the game .exe file
the one i load the address of
yeah, are you referencing the exe
I assume so? Given that everything works correctly
then yeah you can just
ldloc.0
and take that class as the parameter instead
and write to the field in your methodAh ok, i will see if i can figure it out, should be straight forward enough
Do i need to know the name of the field for this
yes
shit
ok
I mean
if you really have to, you can have the method return the new value and do the write in IL with stfld instead
Literally all of our other code manages to be refelction based, so im gonna have to find a work around
We don't use any obfuscated names anywhere currently
i can write to the global field with stfld?
I need value persistence between method calls that's the issue
it's a field on a class, no?
yes
then yeah, you write to it with stfld
if it's an instance field, you have to push the instance you want to write to before the new field value
so my method will return a value and i can use stfld on stloc.1 with the returned value?
and that means i dont need to know the name
Apologies if I am getting this wrong
It's just a "protected byte"
you would do
oh so i still need the obfsucated field name
I mean
you could get the field from the earlier ldfld instead of using the name directly
that would be what i need to do i reckon
but I would be surprised if never using any obfuscated name ever is maintainable in the long run
We have been very successful thus far
quite a good portion of the game has been unpacked and proxied without obfuscated names
Just a lot of IL pattern searching
Ok so do i need to add instructions after the ldfld from earlier to allow this value to be accessed later on
to do it without the obfuscated name
no, you can just steal the operand is all I meant
I am not following, sorry, if this is too many questions its ok if you wanna dip lol
so, you have the list of instructions
Yes
you know where the ldfld is for the field you want to set
yes
is that maintained after stloc.1
you can take that instruction's
Operand
and use that as the operand for an stfld, to tell it to store to the same fieldOHHH OF COURSE YES
right i misunderstood the way you said the message before
Yes yes yes, ok thank you very much
I think i am doing something wrong
you need an ldarg_0 before the ldloc
you're writing to an instance field, so you need an instance
(you also don't really need to be using ldloca/ref anymore)
easy as
ill try this in a few hours once i get back to my ocmputer
Just tested, it works, thank you, i love you, you are amazing!