Categories > Coding > C++ >

Need help with recreating lua_pushvalue/setobj2s

New Reply

Posts: 6

Threads: 3

Joined: Nov, 2024

Reputation: 0

Posted

So, my code crashes. I have decided I won't use shuffles (nihon did this back in the day).
I have recreated index2addr, I have luaO_nilobject & pseudo2addr addresses correct.

inline TValue* index2addr(lua_State* L, int idx)
{
	StkId top = gettop(L); // L->top
	StkId base = getbase(L); // L->base
	if (idx > 0)
	{
		TValue* o = base + (idx - 1);
		if (o > top)
			return *reinterpret_cast<TValue**>(base + offsets::lua_nilobject);
		else
			return o;
	}
	else if (idx > LUA_REGISTRYINDEX)
	{
		return top + idx;
	}
	else
	{
		return pseudo2addr(L, idx);
	}
}

This is index2addr.

inline void r_setobj2s(TValue* obj1, TValue* obj2) // thanks to immune
{
	TValue* o1 = (TValue*)obj1, * o2 = (TValue*)obj2;
	o1->tt = o2->tt;
	o1->value = o2->value;
}

This is setobj2s.

inline void lua_pushvalue(lua_State* L, int idx)
{
	TValue* o = index2addr(L, idx);

	r_setobj2s(*reinterpret_cast<TValue**>(L + offsets::top), o);
	*reinterpret_cast<uintptr_t*>(L + offsets::top) += 0x10;
}

And this is pushvalue.

Idk why, but setobj2s crashes... Any guesses?

  • 0

  • Comment

RealNickk

nick / reversed-coffee

patron

Posts: 31

Threads: 3

Joined: Mar, 2023

Reputation: 10

Replied

So, it's been a while since I've used the Lua API but maybe I can help. From what I remember, index2addr (aka index2adr on Lua) translates a stack index to the virutal memory address of a stack value (aka TValue* or StkId). And since Luau forces inlines for this function by default, you cannot simply call a function address.

 

Well, let's take a look at the Luau source for this:

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

 

The Lua stack works in an interesting way. There's a couple of ways to pull a TValue* off of the stack. I'll briefly explain what I'm seeing because it's been a while and I need a refresher on this.

 

If the index is bigger than zero, it's going to add onto the base of the stack and not pull at the exact bottom of the stack. This is important if we're executing inside of an LClosure which has a slice of the stack in its own base, so it can jump back after the call ends. It will pull the base of the stack and then add to that relative to the index. From what I see, you're doing this correctly. However, you are adding the luaO_nilobject offset to the stack base, which I find odd since I believe luaO_nilobject is a static pointer and does not exist on the stack. This could cause a crash if you try to pull from higher than the stack's current length by throwing an access violation error due to that memory address not being accessible. I could also be wrong, but look into that.

 

If the index is larger than REGISTRYINDEX, it'll pull from the stack's top added to the index (which is negative, so it will pull relative from the stack's top). This looks about right to me. Then you have a fallback to pseudotoaddr which looks right.

 

Let's now look at setobj2s, known as setobj in Luau. It is defined as a macro:

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

 

I am not very familiar with this, so take my sayings with a grain of salt. It appears that we are copying the contents of the first object to the second object in the stack. This essentially overwrites the first object with the contents of the second object without requiring the object's memory address to change in the stack. This optimizes memory usage by not requiring memory realloaction.

 

In your source, you're copying everything separately which might be okay, but let's look at how TValue is defined:

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

 

This could be your issue because you are not copying over the extra space. I do not know what this extra space is used for, but I believe there is still some function to it, even if it appears to just be extra space. Make sure you're copying that extra space over as well. Again, this might not be an issue, but it is not engaging in the same behavior as vanilla Luau is doing.

 

Then you have a definition of lua_pushvalue. Let's take a look at Luau again:

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

 

An important thing to note is that something is being done with the GC, seeing luaC_threadbarrier. I do not know what this is, but the comments give some clarification. Essentially, it looks like it could mess with the garbage collector. This might not cause any immediate issues.

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

 

In lua_pushvalue, you are pulling the TValue* (StkId) and you're copying the contents of the object that was pulled into the object at the top of the stack. The stack is then incremented to signal a value push. This essentially adds the same object to the stack but at the top. In your code, you are calling setobj2s to copy the value to the top, so I don't see anything wrong there. However, I see you're incrementing by 0x10. While this is not technically a problem, it can hurt maintainability because as we saw with the definition of TValue, the size can be arbitrary if Roblox ever wants to increase their extra space. This can cause an overshoot or undershoot in the incrementing of the stack's top offset, which can lead to a misalignment that will lead to access violation errors. You should instead get the size of TValue using sizeof or define the size and increment., or as Luau defines it, simply calling ++ on top (if you're using offsets):

https://i.imgur.com/4Wp23L0.png

 

Hope this provided some insight.

  • 0

  • Comment

Used to be involved with game hacking, now I'm involved in cybersecurity. https://reversed.coffee/blog

Login to unlock the reply editor

Add your reply

Users viewing this thread:

( Members: 0, Guests: 1, Total: 1 )