Coder Social home page Coder Social logo

Read String (linux) about kiwi HOT 13 CLOSED

andoryuuta avatar andoryuuta commented on May 29, 2024
Read String (linux)

from kiwi.

Comments (13)

Andoryuuta avatar Andoryuuta commented on May 29, 2024 1

@l3lackShark No worries, glad it works!

In response the the original issue post though, I'll probably add ReadNullTerminatedUTF8String() and ReadNullTerminatedUTF16String() functions to kiwi for the most common use cases, the latter would have resolved this issue from the beginning. I'll open a separate issue for this.


To add some more information about your specific case though, @zecman is right about it being null-terminated as well.
If you look at the managed-code portion of the net core string implementation, it says:

For empty strings, _firstChar will be '\0', since strings are both null-terminated and length-prefixed.

Which is why cheat engine is able to find the end of the string. A different way you could read the string by using a null-terminator would be something like:

func ReadNullTerminatedUTF16String(proc kiwi.Process, addr uintptr) (string, error) {
	var rawData []uint16
	for {
		// Read a single uint16
		c, err := proc.ReadUint16(addr + uintptr(len(rawData)))
		if err != nil {
			return "", err
		}

		// Check if the uint16 is 0 (null terminator).
		if c != 0 {
			// Not zero, append it to our slice.
			rawData = append(rawData, c)
		} else {
			// Got null terminator, exit loop
			break
		}
	}

	// Decode the UTF16 into a Go string type.
	return string(utf16.Decode(rawData)), nil
}

func main() {
	strDataAddr := uintptr(0x......) // Address of the `m_Characters` field.
	str, err := ReadNullTerminatedUTF16String(proc, strDataAddr)
	if err != nil {
		panic(err)
	}
	fmt.Println(str)
}

Do note: this way of reading it will be significantly slower than the other method because it's reading 1 uint16 per call, instead of the full string data in 1 call.

from kiwi.

Andoryuuta avatar Andoryuuta commented on May 29, 2024

Strings are very ambiguous things unfortunately. Different encodings, null-terminated or not, encoding endianess, etc. Because of that, I originally decided to leave it up to the user of the library via ReadBytes.

However, looking at it now, it would definitely make sense to add something for the most common case of null-terminated utf8 strings (would be able to read from a typical C char*-style string).

Would a ReadNullTerminatedUTF8String() work in your use case?

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024

Even though removing nulls is a step in the right direction, it would probably not satisfy my needs.
This becomes a problem when the string size is unknown. Currently I have a very silly function that is not really error-proof. I'm not even sure how I can improve it at this point haha. I'm still kind of new to reading bytes and stuff, let alone to Go in general. Basically I'm trying to get rid off or end on all unexpected/Non-UTF/Special characters. https://pastebin.com/wpyeG1Kx

from kiwi.

Andoryuuta avatar Andoryuuta commented on May 29, 2024

Null-terminated doesn't mean to remove the nulls, it means to read bytes one at a time until you hit a null character that signals the end of the string.

In your case however, I believe you are trying to read a UTF16-encoded string with the length stored separately, because of how strings are implemented in .NET core.

I looked up other Osu! projects (as it seems like that is what you are trying to read from), and found some string reading code here. After looking up what Osu! Lazer was made in (C# / .NET core), I was able to pull open the net core source code and look at how strings are layed out in memory (assuming 32bit):

    0x0: void*   virtual_function_base;
    0x4: DWORD   m_StringLength;
    0x8: WCHAR   m_Characters[0];

So to read that, you'll probably have to do something like this:

package main

import (
	"errors"
	"fmt"
	"unicode/utf16"

	"github.com/Andoryuuta/kiwi"
)

// From https://stackoverflow.com/questions/15783830/how-to-read-utf16-text-file-to-string-in-golang
func utf16toString(b []uint8) (string, error) {
	if len(b)&1 != 0 {
		return "", errors.New("len(b) must be even")
	}

	// Check BOM
	var bom int
	if len(b) >= 2 {
		switch n := int(b[0])<<8 | int(b[1]); n {
		case 0xfffe:
			bom = 1
			fallthrough
		case 0xfeff:
			b = b[2:]
		}
	}

	w := make([]uint16, len(b)/2)
	for i := range w {
		w[i] = uint16(b[2*i+bom&1])<<8 | uint16(b[2*i+(bom+1)&1])
	}
	return string(utf16.Decode(w)), nil
}

func ReadNetCoreString(proc kiwi.Process, addr uintptr) (string, error) {
	strLen, err := proc.ReadUint32(addr + 0x4)
	if err != nil {
		return "", nil
	}

	rawData, err := proc.ReadBytes(addr+0x8, strLen*2) // Multiplied by 2 because it's a UTF16 string.
	if err != nil {
		return "", nil
	}

	return utf16toString(rawData)
}

func main() {
	proc, err := kiwi.GetProcessByFileName("...")
	if err != nil {
		panic(err)
	}

	stringAddr := uintptr(0xFFFFFFFF) // Need to get the address of the net StringObject.
	str, err := ReadNetCoreString(proc, stringAddr)
	if err != nil {
		panic(err)
	}

	fmt.Println(str)
}

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024

Wow, thanks for taking the time on my case. I tried to apply these functions, but I get bunch of Chinese characters in return by default.
By looking into the structure of the strings I couldn't pick up any values that would represent strLength
image
However, ChetEngine is able to figure it out by itself
image (100 is a manual value, actual string is less than this number)

Here is how it looks like in memory viewer:
image

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024

But you are right, the text is indeed in UTF-16. By changing the Display type to UTF-16 in CE, the strings look normal:
image
And here is addr+0x4:
image

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024

Wait, I was wrong, working on it..

from kiwi.

gitzec avatar gitzec commented on May 29, 2024

this string is terminated by zeros... use a pointer to get byte 1 and read until you get the zeros. happy game hacking ;-)

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024
0x0: void*   virtual_function_base;
0x4: DWORD   m_StringLength;
0x8: WCHAR   m_Characters[0];

This was so valuable to me, thank you very much.

https://www.youtube.com/watch?v=JmLH1r0KMms

from kiwi.

gitzec avatar gitzec commented on May 29, 2024

Maybe you could read the mem in blocks and iterate through these to save syscalls? Thanks for sharing this base.

from kiwi.

Andoryuuta avatar Andoryuuta commented on May 29, 2024

Yes, reading in 2048-byte blocks in how I implemented it on the V0.2 branch, (decreasing the block size if there is an error, so that it will still work if the string is on the end of a allocated memory region). I haven't merged the it into master yet because I haven't had the time to setup a ubuntu VM to test the changes on.

from kiwi.

l3lackShark avatar l3lackShark commented on May 29, 2024

@Andoryuuta I could confirm that ReadNullTerminatedUTF16 works like a charm, I switched to it from my solution. Let me know if you want to test something else.

from kiwi.

Andoryuuta avatar Andoryuuta commented on May 29, 2024

Thanks for testing it! I'll merge it to master now.

from kiwi.

Related Issues (9)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.