How to get a clean virtual binary from memory? menu

User Tag List

Results 1 to 7 of 7
  1. #1
    Glitt's Avatar Active Member CoreCoins Purchaser
    Reputation
    38
    Join Date
    Dec 2022
    Posts
    49
    Thanks G/R
    8/15
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    How to get a clean virtual binary from memory?

    I would almost rather or rather have my scuffed virtual file than opening x64dbg and letting it crash wow to get a dump. I know there are also some resources available to do this I've used two of them already, but they don't defeat my obsessive tendencies for the following reasons.

    - Usage of 3rd party libraries
    - Usage of CSharp

    Not trying to get into that debate it's just my personal preference or sanity issues to achieve archival automation of the exe's. I can totally use the file I have at this point, but Ida wants to complain about all sorts of misalignments and improper counts of things.

    What I've done:

    - Load wow suspended
    - Resume
    - Wait for the final module to appear
    - Suspend
    - Write headers and read memory based on image size
    - Relocations
    - Todo: imports, delayed imports, whatever the cost at some point but that isn't as important RN
    - Save to disk
    - Resume
    Last edited by Glitt; 08-12-2023 at 09:21 AM. Reason: just the important parts

    How to get a clean virtual binary from memory?
  2. #2
    wardrive's Avatar Active Member
    Reputation
    20
    Join Date
    Jul 2023
    Posts
    43
    Thanks G/R
    23/8
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Any particular reason you don't just create an automated backup of the binary in the wow directory, and use dumpwow? Pretty sure you could script that with minimal effort.

    Why reinvent the wheel? Unless I'm missing something (quite possible).

  3. #3
    Glitt's Avatar Active Member CoreCoins Purchaser
    Reputation
    38
    Join Date
    Dec 2022
    Posts
    49
    Thanks G/R
    8/15
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by wardrive View Post
    Any particular reason you don't just create an automated backup of the binary in the wow directory, and use dumpwow? Pretty sure you could script that with minimal effort.

    Why reinvent the wheel? Unless I'm missing something (quite possible).
    Yeah, it's what I've done it in the past, but it's the OCD nature of things. I'm aiming to have things with as little dependencies as possible. Also, it feels like a really fancy type of setup for every time I load my cheat it will check if there is already a file archived for the current patch and save the name with the version info included.

  4. #4
    _chase's Avatar Established Member
    Reputation
    96
    Join Date
    Dec 2019
    Posts
    58
    Thanks G/R
    17/50
    Trade Feedback
    0 (0%)
    Mentioned
    5 Post(s)
    Tagged
    0 Thread(s)
    Similar to wardrive, I'm inclined to use the solutions available already.

    My rough build process is as such:
    On new client version, I automate a git commit with the new version into my analysis / system bindings generation pipeline.
    I analyze using https://crates.io/crates/iced-x86 statically, then similar to reclass or what not I generate explicit C structs and function definitions.
    My C structs and function bindings are their own libraries generated per client version: "prisma_era", "prisma_classic", "prisma_retail", "prisma_era_ptr", etc...

    All of this can be done with github actions and use ubuntu runners

    Then I have a separate repo which uses this as a dependency and has the general common "prisma_rs" bindings.
    when you link against the prisma_rs for a script, you can choose what sys structs you need

    structs generation snippet
    Code:
        
        let mut namespace = Namespace::anonymous();
        namespace.add_constant(Constant::public(
            "ANALYZER_GIT_VERSION",
            FieldType::Ref(StructTag::of_lit("str").into(), None),
            format!("\"{}\"", git_version::git_version!()),
        ));
        namespace.add_constant(Constant::public(
            "ANALYZER_VERSION",
            FieldType::Ref(StructTag::of_lit("str").into(), None),
            format!("\"{}\"", env!("CARGO_PKG_VERSION")),
        ));
        namespace.add_constant(Constant::public(
            "CLIENT_VERSION",
            FieldType::Ref(StructTag::of_lit("str").into(), None),
            format!("\"{}\"", binary_version),
        ));
    
    let _active_player_compile = namespace.compile_struct(
            StructBuilder::new("ActivePlayer")
                .extends(StructTag::of_lit("Unit"))
                .field(StructField::public(
                    "inventory_count",
                    PrimitiveType::U32,
                    player_inventory_offset,
                ))
                .field(StructField::public(
                    "inventory_items",
                    FieldType::Ptr(StructTag::of_lit("Guid").into(), 1),
                    player_inventory_offset + 0x08,
                ))
                .field(StructField::public(
                    "skill_lines",
                    FieldType::Array(PrimitiveType::U16.into(), 256),
                    skill_lines,
                ))
                .field(StructField::public(
                    "skill_levels",
                    FieldType::Array(PrimitiveType::U16.into(), 256),
                    skill_levels,
                )),
        )?;
    sys bindings example output
    Code:
    #![allow(non_snake_case)]
    pub const ANALYZER_GIT_VERSION: &str = "b99e823-modified";
    pub const ANALYZER_VERSION: &str = "0.1.0";
    pub const CLIENT_VERSION: &str = "3.4.2.50664";
    
    #[repr(C)]
    pub struct ActivePlayer {
        pub vmt: *const *const usize,
        _padding_to_0x10: [u8; 0x8],
        pub r#type: u8,
        _padding_to_0x18: [u8; 0x7],
        pub guid: Guid,
        _padding_to_0xD8: [u8; 0xB0],
        pub entry_id: u32,
        _padding_to_0x148: [u8; 0x6C],
        pub position: Vector3,
        _padding_to_0x158: [u8; 0x4],
        pub rotation: f32,
        _padding_to_0x708: [u8; 0x5AC],
        pub casting_spell_id: u32,
        _padding_to_0x810: [u8; 0x104],
        pub channeling_spell_id: u32,
        _padding_to_0x868: [u8; 0x54],
        pub auras_len: u32,
        _padding_to_0x8F8: [u8; 0x8C],
        pub aura_table: [Aura; 0x100],
        _padding_to_0xD730: [u8; 0x1E38],
        pub summoner_guid: Guid,
        _padding_to_0xE674: [u8; 0xF34],
        pub skill_lines: [u16; 0x100],
        _padding_to_0xEA74: [u8; 0x200],
        pub skill_levels: [u16; 0x100],
        _padding_to_0x12988: [u8; 0x3D14],
        pub inventory_count: u32,
        _padding_to_0x12990: [u8; 0x4],
        pub inventory_items: *const Guid,
    }
    my safe prisma bindings then come out as such, where the structs alignment is abstracted away mostly.
    So this persists for era,classic,retail etc
    Code:
    /// Represents the local player of the client. There should only be one of these at a time.
    pub struct ActivePlayer<'client>(&'client sys::ActivePlayer);
    
    pub trait ActivePlayerAccess<'owner> {
        /// Total active inventory items
        fn inventory_count(&self) -> u32;
    
        /// List of item guids
        fn inventory_guids(&self) -> &[Guid];
    
        /// List of skill lines
        fn skill_lines(&self) -> &[u16];
    
        /// List of skill levels
        fn skill_levels(&self) -> &[u16];
    }
    
    impl<'o> ActivePlayer<'o> {
        pub fn can_cast(&self) -> bool {
            self.casting_spell_id().is_none() && self.channeling_spell_id().is_none()
        }
    
        pub fn professions<'i, 'owner: 'i>(
            &'owner self,
        ) -> impl Iterator<Item = (Profession, u16)> + 'i {
            self.skill_lines()
                .iter()
                .zip(self.skill_levels())
                .flat_map(|(id, level)| {
                    Profession::try_from_raw(*id).map(|profession| (profession, *level))
                })
        }
    
        pub fn secondary_skills<'i, 'owner: 'i>(
            &'owner self,
        ) -> impl Iterator<Item = (SecondarySkill, u16)> + 'i {
            self.skill_lines()
                .iter()
                .zip(self.skill_levels())
                .flat_map(|(id, level)| {
                    SecondarySkill::try_from_raw(*id).map(|profession| (profession, *level))
                })
        }
    }
    Last edited by _chase; 08-22-2023 at 08:19 AM. Reason: simplify

  5. Thanks Glitt, unsigned, klumpen (3 members gave Thanks to _chase for this useful post)
  6. #5
    scizzydo's Avatar Contributor
    Reputation
    134
    Join Date
    Oct 2019
    Posts
    96
    Thanks G/R
    5/54
    Trade Feedback
    0 (0%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Blizzard games do all the unpacking and imports during the TLS callbacks. These fire before main is ever executed. You don't need to ever resume the process, because once you create a new thread in the process, these TLS callbacks trigger. With that info, you can do what you want. You're over complicating things in your steps of what you've done. Also, just causing them to trigger and then launching Scylla you can dump it.

    You don't need to load x64dbg because Scylla also has it's own manual executable you can launch (saves the step of launching x64dbg just to launch scylla)

  7. #6
    scizzydo's Avatar Contributor
    Reputation
    134
    Join Date
    Oct 2019
    Posts
    96
    Thanks G/R
    5/54
    Trade Feedback
    0 (0%)
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    I pushed my tool to be public, after making tweaks and what not. The main idea was taken from @namreeb and his dumpwow. I also wanted to do it without any 3rd party libraries, without the need of multiple executables, etc. Maybe this can be of help to you:
    GitHub - scizzydo/memdump: Windows x64 PE process memory dumper do disk

  8. Thanks maikel233, _chase (2 members gave Thanks to scizzydo for this useful post)
  9. #7
    Glitt's Avatar Active Member CoreCoins Purchaser
    Reputation
    38
    Join Date
    Dec 2022
    Posts
    49
    Thanks G/R
    8/15
    Trade Feedback
    0 (0%)
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by scizzydo View Post
    I pushed my tool to be public, after making tweaks and what not. The main idea was taken from @namreeb and his dumpwow. I also wanted to do it without any 3rd party libraries, without the need of multiple executables, etc. Maybe this can be of help to you:
    GitHub - scizzydo/memdump: Windows x64 PE process memory dumper do disk
    Ty scizzy this is very kind of you to share. I'm currently busy with just some offsets and such to get some things in order, but I'm happy to check this out very soon!

Similar Threads

  1. [Guide] [Video] How to get Polyformic Acid Potion transformation from new scholomance
    By oclog1 in forum World of Warcraft Guides
    Replies: 0
    Last Post: 09-01-2012, 06:37 AM
  2. How to get a FREE Virtual Private server for atleast a month
    By Decker1 in forum WoW EMU Guides & Tutorials
    Replies: 2
    Last Post: 07-26-2008, 12:24 PM
  3. How to get the assistance you want from the thread you make
    By degoscar in forum WoW EMU Guides & Tutorials
    Replies: 0
    Last Post: 05-26-2008, 03:18 PM
  4. Replies: 3
    Last Post: 10-23-2007, 08:37 PM
  5. How to get unbanned from the test realm.
    By Sillientra in forum World of Warcraft Exploits
    Replies: 4
    Last Post: 09-25-2006, 07:54 PM
All times are GMT -5. The time now is 04:47 PM. Powered by vBulletin® Version 4.2.3
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2024 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search