Tutorial Pattern Search - Yet another way to automatically find an address.

Kur0

BANNED
Pattern Search or known as Find Pattern is commonly used in the PC hacking scene, Pattern Search is a method to find an address based on a given Array of Bytes with a wildcard. To put it in words, it's basically like searching for hex in a hex editor but instead of searching in a file, it's directly searching through memory.

The code looks like this:
C++:
uintptr_t GetBaseAddress(const char *name) {
    uintptr_t base = 0;
    char line[512];

    FILE *f = fopen("/proc/self/maps", "r");

    if (!f) {
        return 0;
    }

    while (fgets(line, sizeof line, f)) {
        uintptr_t tmpBase;
        char tmpName[256];
        if (sscanf(line, "%" PRIXPTR "-%*" PRIXPTR " %*s %*s %*s %*s %s", &tmpBase, tmpName) > 0) {
            if (!strcmp(basename(tmpName), name)) {
                base = tmpBase;
                break;
            }
        }
    }

    fclose(f);
    return base;
}

uintptr_t GetEndAddress(const char *name) {
    uintptr_t end = 0;
    char line[512];

    FILE *f = fopen("/proc/self/maps", "r");

    if (!f) {
        return 0;
    }

    bool found = false;
    while (fgets(line, sizeof line, f)) {
        uintptr_t tmpEnd;
        char tmpName[256];
        if (sscanf(line, "%*" PRIXPTR "-%" PRIXPTR " %*s %*s %*s %*s %s", &tmpEnd, tmpName) > 0) {
            if (!strcmp(basename(tmpName), name)) {
                if (!found) {
                    found = true;
                }
            } else {
                if (found) {
                    end = tmpEnd;
                    break;
                }
            }
        }
    }

    fclose(f);
    return end;
}

#define INRANGE(x, a, b)        (x >= a && x <= b)
#define getBits(x)              (INRANGE(x,'0','9') ? (x - '0') : ((x&(~0x20)) - 'A' + 0xa))
#define getByte(x)              (getBits(x[0]) << 4 | getBits(x[1]))

uintptr_t FindPattern(const char *lib, const char *pattern) {
    auto start = GetBaseAddress(lib);
    if (!start)
        return 0;

    auto end = GetEndAddress(lib);
    if (!end)
        return 0;

    auto curPat = reinterpret_cast<const unsigned char *>(pattern);
    uintptr_t firstMatch = 0;
    for (uintptr_t pCur = start; pCur < end; ++pCur) {
        if (*(uint8_t *) curPat == (uint8_t) '\?' || *(uint8_t *) pCur == getByte(curPat)) {
            if (!firstMatch) {
                firstMatch = pCur;
            }
            curPat += (*(uint16_t *) curPat == (uint16_t) '\?\?' || *(uint8_t *) curPat != (uint8_t) '\?') ? 2 : 1;
            if (!*curPat) {
                return firstMatch;
            }
            curPat++;
            if (!*curPat) {
                return firstMatch;
            }
        } else if (firstMatch) {
            pCur = firstMatch;
            curPat = reinterpret_cast<const unsigned char *>(pattern);
            firstMatch = 0;
        }
    }
    return 0;
}

Given an example, a function called:
Code:
bool GetClipHasInfiniteBulletsFromEntity();// 0x01EB2944

Let's take a look at 0x01EB2944 in IDA Pro:

1634455687288.png


Now let's put in a case where you need to manually find this function with IDA Pro, copy the offsets then do stuff with it, for sure it's time-consuming.

It's where Pattern Search comes in handy, first let's grab our AOB with the correct wildcard. You can see that the function starts with 00 10 A0 E1, now you can simply put it like this:

C++:
auto result = FindPattern("libGame.so", "00 10 A0 E1");
LOGI("result is %p", result);

But turns out it gives you an incorrect offset, why? It's because there could be thousands of 00 10 A0 E1 in the libGame.so memory. So let's make it more specific!
The next 4 bytes after 0x01EB2944 (or 0x01EB2948) is 30 00 9F E5, now where this comes a bit complex.
0x01EB2948 is Load Address instruction, which means that the relative address (737ADC0 - 1EB2954) could change when the game updates, This is where the wildcard plays.

30 00 9F E5 - E5 is a byte for "LDR" instruction, while the rest bytes to the left is the distance to the address. In this case, E5 won't change after the game update since E5 is bytecode for LDR, so we know that E5 won't change, we can make the AOB like this: ?? ?? ?? E5 (or ? ? ? E5), then combine it with previous AOB, it should look like this: 00 10 A0 E1 ?? ?? ?? E5.

"Now Kuro, what if the LDR doesn't use relative address? Let's say the instruction is LDR R0, [R4]?"
Okay, let's talk about this.

The bytes for LDR R0, [R4] is 00 00 94 E5, since there is no address being stored/loaded, you can just put 00 00 94 E5 in the AOB, pretty simple right?

But what if the instruction uses a small offset like at 0x01EB2944 in the picture? (LDR R1, [R1,#0xA90])
Let's take a look at the bytes (90 1A 91 E5),. as we can see the offset is 0xA90, you just need to find where 0xA90 is located in the bytes.
In this case, it's here 90 1A 91 E5 (It's reverse because the offset is stored in little-endian format).
Now since the 1 for 0x190 is merged with A, you can't put ?? ?A ?? E5, it won't work because the Pattern Search is not yet support those types of bitwise operations. so you need to make it like this: ?? ?? ?? E5.

Enough talk, I'll show you an example how to make AOB for the function above.
Code:
00 10 A0 E1        MOV             R1, R0
30 00 9F E5        LDR             R0, =(off_737ADC0 - 0x1EB2954)
00 00 8F E0        ADD             R0, PC, R0 ; off_737ADC0
10 20 90 E5        LDR             R2, [R0,#(dword_737ADD0 - 0x737ADC0)]
01 00 A0 E3        MOV             R0, #1
00 00 52 E3        CMP             R2, #0
06 00 00 1A        BNE             locret_1EB297C
90 1A 91 E5        LDR             R1, [R1,#0xA90]
00 00 A0 E3        MOV             R0, #0
00 00 51 E3        CMP             R1, #0
1E FF 2F 01        BXEQ            LR
41 05 D1 E5        LDRB            R0, [R1,#0x541]
00 00 50 E3        CMP             R0, #0
01 00 00 13        MOVWNE          R0, #1

In the first line, there is no offset/address being used, so our AOB is
Code:
00 10 A0 E1
In the second line, there is an address being used, so our AOB is now
Code:
00 10 A0 E1 ?? ?? ?? E5
In the third line, there is no address being used, so our AOB is now
Code:
00 10 A0 E1 ?? ?? ?? E5 00 00 8F E0
In the fourth line, there is an address being used, so our AOB is now
Code:
00 10 A0 E1 ?? ?? ?? E5 00 00 8F E0 ?? ?? ?? E5
In the fifth line, there is no being used BUT there is a constant being used (#1 / 1), since this is a constant value and we are sure this is not an offset, we don't need the wildcard this line, so our AOB is now
Code:
00 10 A0 E1 ?? ?? ?? E5 00 00 8F E0 ?? ?? ?? E5 01 00 A0 E3
Now, this process could take a long time, to make sure that your AOB is correct, go to IDA and press the Search for sequence of bytes button (this button:
1634457508007.png
) then put your AOB and press OK. If the result is only 1 address, if not then keep doing the finding AOB step until you have 1 address like this:

1634457680492.png



Then congrats! You just find your very first AOB for this function!
Now in C++, you can apply it like this:

C++:
auto AmmoFunc = FindPattern("libGame.so", "00 10 A0 E1 ?? ?? ?? E5 00 00 8F E0 ?? ?? ?? E5 01 00 A0 E3 00 00 52 E3");
DoHook(AmmoFunc, HookFunc, &OrigFunc);

Pretty simple right? Good luck on your own :)!
If you have any questions, please let me know!
 

Attachments

  • 1634455637640.png
    1634455637640.png
    34.7 KB · Views: 162
How are u guys viewing the aob pattern "00 10 A0 E1" I tried but didn't find it, am learning the aob thing, never did it before so if u guys can help. Is there a need of plugin or something like that if so what is it?
thanks for the tutorial
 
It can't be done through HxD because it will be impossible to know which part of the hex is a call from another address?
 
Back
Top Bottom