YimMenu / HorseMenu

HorseMenu, a beta-stage mod menu for Red Dead Redemption 2 and Red Dead Online, inspired by YimMenu, that protects against crashes and enhances your experience.
71 stars 34 forks source link

[Request]: Calling script functions #164

Closed tuyilmaz closed 3 months ago

tuyilmaz commented 4 months ago

Problem

No response

Solution

Writing a script function wrapper as in YimMenu

Reason

It's possible to add gold, cash, xp, loot tables, and collectibles instantly with script functions.

Additional context

No response

Rxann commented 4 months ago

Okay, I will work on it for my upcoming PR

maybegreat48 commented 4 months ago

It's possible to add gold, cash, xp, loot tables, and collectibles instantly with script functions.

How exactly?

tuyilmaz commented 4 months ago

It's possible to add gold, cash, xp, loot tables, and collectibles instantly with script functions.

How exactly?

Script: net_main_offline Signature: 22 05 24 Args: {AwardHash, false, 255, 0, false}

maybegreat48 commented 4 months ago

What's the function name? And can you give me an example award hash?

tuyilmaz commented 4 months ago

Currently I don't have the latest decompiled scripts, but here is the function:

BOOL func_531(int iParam0, BOOL bParam1, Player plParam2, int iParam3, BOOL bParam4) // Position - 0x23A3F Signature - 22 05 24
{
    var unk;
    var unk13;
    var unk26;

    unk.f_1 = 10;
    unk13.f_1 = 11;
    return func_1940(iParam0, &unk26, &unk13, &unk, bParam1, plParam2, iParam3, bParam4);
}

And for award hashes: 315612159 (250 cash) -1306394522 (1 gold)

AAA-ALR commented 3 months ago
UnkByteData_program RDR2.exe+0x59B29E0
UnkByteData_script_vm RDR2.exe+0x59B2940
get_script_program(int8_t*,Hash) RDR2.exe+0x2AFF0B8

void script_function_call(const Hash scriptHash, uint32_t ip, std::initializer_list<uint64_t> args)
{
    auto thread = rdr_util::FindScriptThread(scriptHash);
    auto program = get_script_program(UnkByteData_program, scriptHash);
    if (thread && program)
    {
        auto tls_ctx = rage::tlsContext::Get();
        auto stack = (uint64_t*)thread->m_Stack;
        auto og_thread = *Pointers.CurrentScriptThread;
        auto og_running_in_scrthread = rage::tlsContext::Get()->m_RunningScript;
        *Pointers.CurrentScriptThread = thread;
        rage::tlsContext::Get()->m_RunningScript = true;
        rage::scrThreadContext ctx = thread->m_Context;
        for (auto& arg : args)
            stack[ctx.m_StackPointer++] = arg;
        stack[ctx.m_StackPointer++] = 0;
        ctx.m_ProgramCounter = ip;
        ctx.m_State = rage::eThreadState::idle;
        *Pointers.ScriptVM(stack, *Pointers.ScriptGlobals, UnkByteData_script_vm, program, &ctx);
        rage::tlsContext::Get()->m_RunningScript = og_running_in_scrthread;
        *Pointers.CurrentScriptThread = og_thread;
    }
}

Added CHEAT_XP example

script_function_call(rage::joaat("net_main_offline"),0x23A3F,{0xED1FAB28, false, 0, 0, false});

Maybe there will be a lot of skid menus added soon lol

xiaoxiao921 commented 3 months ago

Nice work!

AAA-ALR commented 3 months ago

@AAA-ALR From what I can tell from your code, you're basically hijacking a (random?) script, forcing its Instruction Pointer to the function you want to call? I'm assuming FindScriptThread finds a non-running script thread? Why can't you simply just allocate a new script for the thread runner and use it? There's always a chance if you use something like freemode (IDK what RDR2 calls it), that some script will do GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH and have it return non-zero, and it thinks that the script is running, when in actuality it's just us doing script schenanigns.

tl;dr: Is there a more efficient way to create a script in the thread stack?

I don't know much about the game scripts,But this net_main_offline is like a freemod of GTA

tuyilmaz commented 3 months ago

After a little research I've discovered there are award hashes located in: update_4.rpf/x64/data/itemdatabase/catalog_awards_mp.rpf/catalog_awards_mp.ymt This file contains award multipliers, too. However there is another file called 'AwardsDatabase_****' which is downloaded from the servers when you join RDO. I'm pretty sure this file contains latest gold, xp, cash multipliers for missions. It also contains all the outlaw pass unlocks, but they're server only. I'm not good at RE, so I don't know if it's possible to trigger those transactions.

@AAA-ALR nice work, but I don't think we need the script thread for this function. We can just create our own thread and stack call the ScriptVM function then delete them.

Rxann commented 3 months ago

UnkByteData_program RDR2.exe+0x59B29E0

UnkByteData_script_vm RDR2.exe+0x59B2940

get_script_program(int8_t*,Hash) RDR2.exe+0x2AFF0B8

void script_function_call(const Hash scriptHash, uint32_t ip, std::initializer_list<uint64_t> args)

{

  auto thread = rdr_util::FindScriptThread(scriptHash);

  auto program = get_script_program(UnkByteData_program, scriptHash);

  if (thread && program)

  {

      auto tls_ctx = rage::tlsContext::Get();

      auto stack = (uint64_t*)thread->m_Stack;

      auto og_thread = *Pointers.CurrentScriptThread;

      auto og_running_in_scrthread = rage::tlsContext::Get()->m_RunningScript;

      *Pointers.CurrentScriptThread = thread;

      rage::tlsContext::Get()->m_RunningScript = true;

      rage::scrThreadContext ctx = thread->m_Context;

      for (auto& arg : args)

          stack[ctx.m_StackPointer++] = arg;

      stack[ctx.m_StackPointer++] = 0;

      ctx.m_ProgramCounter = ip;

      ctx.m_State = rage::eThreadState::idle;

      *Pointers.ScriptVM(stack, *Pointers.ScriptGlobals, UnkByteData_script_vm, program, &ctx);

      rage::tlsContext::Get()->m_RunningScript = og_running_in_scrthread;

      *Pointers.CurrentScriptThread = og_thread;

  }

}

Added CHEAT_XP example


script_function_call(rage::joaat("net_main_offline"),0x23A3F,{0xED1FAB28, false, 0, 0, false});

Maybe there will be a lot of skid menus added soon lol

UnkByteData_script_vm seems to indicate if globals are enabled or not. Incredible work on this though!

tuyilmaz commented 3 months ago

@Rxann If you haven't started working on this. I wrote a function wrapper using YimMenu and @AAA-ALR's reply. I can create a PR.

Rxann commented 3 months ago

@Rxann If you haven't started working on this. I wrote a function wrapper using YimMenu and @AAA-ALR's reply. I can create a PR.

@maybegreat48 has already made an implementation that will be included into the menu soon. Thank you for asking before creating a PR though.

maybegreat48 commented 3 months ago

@maybegreat48 has already made an implementation that will be included into the menu soon. Thank you for asking before creating a PR though.

To add some context, our development workflow now involves a private development repository to prevent certain paid menu grifters (especially a menu named "Fortitude") from stealing our source before it makes it to HorseMenu

tuyilmaz commented 3 months ago

@maybegreat48 I understand. May I ask if you're just implementing the function wrapper or are you doing more research on this give award thing? Thanks for your efforts btw.

maybegreat48 commented 3 months ago

I've reversed the file containing the award hashes, but many of them don't seem to work as intended, likely due to the lack of proper YMT parsing support for RDR2

tuyilmaz commented 3 months ago

Here is an award item from that file: `

0x12CFDBFF
    <maxclaims value="-1"/>
    <ignoresmodifiers value="false"/>
    <debugonly value="false"/>
    <serveronly value="false"/>
    <awarditems>
            <experience value="0"/>
            <items>
                    <Item>
                            <key>currency_cash</key>
                            <path>0x96F2DE61</path>
                            <qty value="25000"/>
                    </Item>
            </items>
    <unlocks/>
    </awarditems>

` And from what I tried I found out that debugonly and serveronly awards can't be given using that script function. Can you please give more context about what made you think that they don't work as intended?

AAA-ALR commented 3 months ago

Here is an award item from that file: <Item key="0x12CFDBFF"> <key>0x12CFDBFF</key> <maxclaims value="-1"/> <ignoresmodifiers value="false"/> <debugonly value="false"/> <serveronly value="false"/> <awarditems> <experience value="0"/> <items> <Item> <key>currency_cash</key> <path>0x96F2DE61</path> <qty value="25000"/> </Item> </items> <unlocks/> </awarditems> </Item> And from what I tried I found out that debugonly and serveronly awards can't be given using that script function. Can you please give more context about what made you think that they don't work as intended?

NETWORK::NETWORK_AWARD_HAS_REACHED_MAXCLAIM I guess

maybegreat48 commented 3 months ago

Can you please give more context about what made you think that they don't work as intended?

Anything that's supposed to give you gold doesn't actually give you gold This is how the file looks for me

 <Item type="ItemDatabaseAward" key="hash_12CFDBFF">
    <key>hash_12CFDBFF</key>
    <maxclaims value="-1" />
    <ignoresmodifiers value="false" />
    <debugonly value="false" />
    <serveronly value="false" />
    <awarditems>
     <experience value="0" />
     <items itemType="ItemDatabaseAwardItem">
      <Item>
       <key>hash_00000001</key>
       <path />
       <qty value="0" />
      </Item>
     </items>
     <unlocks itemType="ItemDatabaseAwardUnlock" />
    </awarditems>
   </Item>