rev 2
This is a little tricky without spoilers. But.
I can see the password string is pushed through this function. It’s really difficult to see what’s going on. It appears to do something with hexadecimal.
char * DoSomethingToPass(longlong passString)
{
ulonglong *puVar1;
longlong in_r13;
ulonglong i;
ulonglong auStack152 [16];
longlong local_18;
local_18 = *(longlong *)(in_r13 + -0x7010);
.opd.FUN_1002a640(auStack152,(ulonglong *)&DAT_100a1cd0,0x80);
puVar1 = .opd.FUN_10026640(0x11);
.opd.FUN_1002aa40(puVar1,0,0x11);
i = 0;
while( true ) {
if (0xf < i) break;
*(undefined *)((longlong)puVar1 + i) = *(undefined *)(passString + auStack152[i]);
i = i + 1;
}
if (local_18 != *(longlong *)(in_r13 + -0x7010)) {
/* WARNING: Subroutine does not return */
.opd.FUN_100358d0();
}
return (char *)puVar1;
}
That returned variable is then called in a function like this;
likelyAnswer = StringMurder((ulonglong)pcVar3,(longlong)passWordString,passLength);
That function looks like this.
ulonglong * StringMurder(ulonglong param_1,longlong passWordString,ulonglong StringLength)
{
longlong param1holder;
ulonglong *likelyAnswer;
ulonglong *puVar1;
longlong lVar2;
longlong stringLengthOffset;
longlong in_r13;
ulonglong i;
lVar2 = *(longlong *)(in_r13 + -0x7010);
i = 0;
param1holder = ::StringLength(param_1);
stringLengthOffset = StringLength + param1holder;
likelyAnswer = .opd.FUN_10026640(stringLengthOffset + 1);
.opd.FUN_1002aa40(likelyAnswer,0,stringLengthOffset + 1);
puVar1 = .opd.FUN_10026640(stringLengthOffset + 1);
.opd.FUN_1002aa40(puVar1,0,stringLengthOffset + 1);
FUN_100003a0();
FUN_10000380();
while( true ) {
if (param1holder + StringLength <= i) break;
*(byte *)((longlong)likelyAnswer + i) =
*(byte *)((longlong)puVar1 + i) ^
*(byte *)(passWordString + (i - (i / StringLength) * StringLength));
i = i + 1;
}
Clear((longlong)puVar1);
if (lVar2 != *(longlong *)(in_r13 + -0x7010)) {
/* WARNING: Subroutine does not return */
.opd.FUN_100358d0();
}
return likelyAnswer;
}
I feel like the first param doesn’t actually exist. But you can see the for loop is manipulating the bytes one by one using the length of the string.
So my thoughts are, each character of that password string changed to hex then each byte of the hex manipulated using that (pwlength(i-(i/length)*length)) algorithm.
Also how is (i / StringLength) not throwing an exception? is this some lower language/byte stuff where it just disregards decimals? also the C operator ^ is some pretty advanced stuff, is that ghidra sending me another curve ball or is this @Futility coding slightly above machine language? lol.
Following along generally is one thing but I’m having a lot of trouble coming out the other side with the correct password string.
Am I in the right ball park here?
Edited for some fancy colours in the code and secondary thoughts.
Gonna start in the weeds and then give general tips
- “I feel like the first param doesn’t actually exist” - You can see it being
strlen
‘d early on and that length being used later. It is highly unlikely that this is a fluke. What makes you think it doesn’t exist? - “each character of that password string changed to hex then each byte of the hex manipulated “ - Might be nitpicky, but nothing is being “changed to hex”. A program is a pile of bits. Those strings are just piles of bits. When the pile’o’bits is sent to something like
printf
, you have to tell it “these bits right here, these should be displayed as ASCII, or as their hex representation or as an int or just group them up into bytes or whatever”. You can try this with a simple program yourself (below). Notice that they’re all equivalent-0x74736574
is1953719668
in decimal and treating0x74736574
as ascii characters gets you the stringtest
(backwards, in this case, due to endianness). All this to say- I’ve changed nothing, I’m just iterating through the string and treating its bytes as… well… bytes. - “Also how is (i / StringLength) not throwing an exception?” - Integer division doesn’t throw an exception if it gets a non-integer answer. It simply drops the remainder portion (think
floor()
). Again you can try this yourself using the shell below by uncommenting the finalprintf
. - “also the C operator ^ is some pretty advanced stuff” - Not sure what we mean by this. The
^
is akin to the|
or&
operators, which are all basic bitwise operators. In this case I’m just doing an XOR, which sort of a fundamental building block of many cryptographic algorithms.
Anyway- so far we’re looking (very!) good. You’ve managed to narrow down the search space considerably and you’re well on the way to understanding what’s happening. Just a few hiccups. I would make the following suggestions:
- I would check exactly where the output from your
StringMurder
function is going. How important is it to know exactly what’s going on here? - If you don’t understand the math that’s happening (
(pwlength(i-(i/length)*length))
), maybe making a small program to iterate through it a couple of times could help? That is, give dummy values to the variables and then print out what the answer is at each step. Maybe you’ll see a pattern or something? This is a good general hint overall- if something looks too complicated to work out in your head, feel free to pop in a few test values and run it through to see what pops out.
Keep it up!
#include <stdio.h>
/*
* tester.c
* compile with something like: gcc tester.c -o thing
* output:
* str: test
* num: 1953719668
* hex: 0x74736574
*/
int main(int argc, char** argv){
char hello[4] = "test";
printf("str: %s\n", hello);
printf("num: %d\n", *((int*) hello));
printf("hex: 0x%x\n", *((int*) hello));
//printf("%d\n", 5/2);
exit(0);
}
Still working on this one.
I see what you are saying about not ‘changing’ a type but outputting its bytes in different ways. I think you are hinting that if you think of a string as bytes/number/int its easily manipulated.
This/these functions are confusing to me. The loop runs 16 times. It adds the password string to each element of that array then moves it to dynamic memory?
ulonglong * SomethingToPassString(longlong passString)
{
ulonglong *malloc;
longlong in_r13;
ulonglong i;
ulonglong auStack152 [16];
longlong local_18;
local_18 = *(longlong *)(in_r13 + -0x7010);
.opd.FUN_1002a640(auStack152,(ulonglong *)&DAT_100a1cd0,0x80);
malloc = .opd.FUN_10026640(0x11);
.opd.FUN_1002aa40(malloc,0,0x11);
i = 0;
while( true ) {
if (0xf < i) break;
*(undefined *)((longlong)malloc + i) = *(undefined *)(passString + auStack152[i]);
i = i + 1;
}
if (local_18 != *(longlong *)(in_r13 + -0x7010)) {
/* WARNING: Subroutine does not return */
.opd.FUN_100358d0();
}
return malloc;
}
.opd.FUN_1002a640(auStack152,(ulonglong *)&DAT_100a1cd0,0x80);
Really struggling with this function. I feel like its a default function from C. But I don’t understand how it fills the array. If that’s what it does at all.
And the var malloc is just 17 bytes of space yeah? How will that string possibly fit into that?
It’s not a default C function. That’s all me, baby. And again, maybe writing a small sample function to do what you’re seeing here and then running it a couple times could help show what’s going on.
Also, maybe just look again at what you’ve said. it seems like you’re probably a bit closer than you realize.
The loop runs 16 times.
|| And the var malloc is just 17 bytes of space yeah? How will that string possibly fit into that?
|| It adds the password string to each(?) element of that array then moves it to dynamic memory?
I’m sure this will make perfect sense to me once I complete the challenge and not a second before.
Currently feeling like Professor Sprout at the water park. https://www.youtube.com/watch?v=eJNRXYmNpKw