(extracted from main diary)
-
SHA256 : 878d5137e0c9a072c83c596b4e80f2aa52a8580ef214e5ba0d59daa5036a92f8
-
Probably the scariest trojan of the current days. Let's explore it. I using ghidra again.
-
According to ghidra, the only import is
KERNEL32.DLL::WTSGetActiveConsoleSessionId
-
I wonder what it can possibly be with so little and i'll have to find out.
-
The obvious step for now is to find out how it load other functions to be able to do anything.
-
There isn't that much function and a quick overview found this stuff, i renamed the functions with my own naming convention.
-
I have no idea what it's doing. I'll have to (posibly) patch the function signature too.
-
There is also a lot of repetitive call to the same function pointer
-
Then i'll have to trace back the references to the function pointers
-
Here is how it looks for now
void k_DLL_loadfunction?(void)
{
undefined4 uVar1;
undefined4 local_8;
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(0x21,0x54b7e774,&DAT_0040c040);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(1,0x3c505b91,&DAT_0040c0c8);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(2,0x10577008,&DAT_0040c214);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(1,0x7194b56b,&DAT_0040c0c4);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(1,0x20edec96,&DAT_0040c0cc);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(2,0x620cb38e,&DAT_0040c21c);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(0xe,0x5a7185ae,&DAT_0040c230);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*_k_DLL_FP1)(uVar1);
k_DLL_loadfunction2?(0x4a604ebc,&local_8);
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(3,0x73ee0ad8,&DAT_0040c224);
uVar1 = (*_k_DLL_FP3)(0,local_8);
(*_k_DLL_FP1)(uVar1);
k_DLL_afterLoadFunction?();
return;
}
- Tons of "CALL dword ptr [k_DLL_FP1/2/3]" and there isn't a single write directly refering to the FP's addresses
- It must be part of a struct or an array
- AND their addresses are : 0040c1e8, 0040c17c, 0040c1a8, they're quite close to eachothers
- AND there is a lot of 4 bytes data in there. possibly a huge list of (function?) pointer
- if i scroll up a little bit i find the address "0040c040", an XREF find me a PUSH to this address.
- and it lead directly to "k_DLL_loadfunction3?(0x21,0x54b7e774,&DAT_0040c040);"
- Heh :)
- If we look at all the &DAT_ in k_DLL_loadfunction3, the address are not that far to each other
- And not far from the FP too.
- I'll bet on an array of struct for now and take a closer look at k_DLL_loadfunction3?
- (Btw, the 2nd argument may be a hash)
- Duuuuh ! Of course there is a loop, of course the "suspected hash" is XOR'd
- Time for celebration
void __cdecl k_DLL_loadfunction3?(uint loop,uint hash?,void *FP?)
{
char cVar1;
int iVar2;
int iVar3;
int iVar4;
int iVar5;
uint uVar6;
int in_ECX;
uint uVar7;
int in_EDX;
char *pcVar8;
uint uVar9;
uint i;
iVar5 = *(int *)(in_ECX + 0x3c) + in_ECX;
uVar6 = *(int *)(iVar5 + 0x78) + in_ECX;
iVar2 = *(int *)(uVar6 + 0x1c);
iVar3 = *(int *)(uVar6 + 0x20);
iVar4 = *(int *)(uVar6 + 0x24);
uVar9 = 0;
if (*(int *)(uVar6 + 0x18) != 0) {
do {
pcVar8 = (char *)(*(int *)(iVar3 + in_ECX + uVar9 * 4) + in_ECX);
uVar7 = 0;
cVar1 = *pcVar8;
while (cVar1 != '\0') {
pcVar8 = pcVar8 + 1;
uVar7 = uVar7 * 0x1003f + (int)cVar1;
cVar1 = *pcVar8;
}
i = 0;
if (loop != 0) {
do {
if (*(uint *)(in_EDX + i * 4) == (uVar7 ^ hash?)) {
uVar7 = *(int *)(iVar2 + in_ECX + (uint)*(ushort *)(iVar4 + in_ECX + uVar9 * 2) * 4) +
in_ECX;
if ((uVar6 <= uVar7) && (uVar7 < *(int *)(iVar5 + 0x7c) + uVar6)) {
uVar7 = FUN_00401a20();
}
*(uint *)((int)FP? + i * 4) = uVar7;
break;
}
i = i + 1;
} while (i < loop);
}
uVar9 = uVar9 + 1;
} while (uVar9 < *(uint *)(uVar6 + 0x18));
}
return;
}
- The call to function2 is always the same : k_DLL_loadfunction2?(0x4a604ebc,&local_8);```
- It's not loading something as my naming imply. it's probably doing a system call.
- it is now named "k_DLL_systemCall?"
- We have a repetition of this pattern
k_DLL_systemCall?(0x4a604ebc,&local_8);
uVar1 = local_8;
(*_k_DLL_FP2)(local_8);
k_DLL_loadfunction3?(0x21,0x54b7e774,&DAT_0040c040);
uVar1 = (*_k_DLL_FP3)(0,uVar1);
(*(code *)k_DLL_FP1)(uVar1);
- One of them have to a LoadLibrary() or something close to it.
- it would be way to inconveniant if it wasn't the case (but we never know with malware, i may be deep into a rabbit hole instead)
- i'll take a small break, rename stuff, explore some more.
- And it's getting late anyway.
The function calling k_DLL_loadfunction is this one, looks familiar ? i hope it does or you haven't been paying attention
/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */
undefined4 k_DLL_beforeLoad?(void)
{
undefined4 uVar1;
undefined4 uVar2;
int iVar3;
int iVar4;
int iVar5;
undefined local_364 [520];
undefined local_15c [128];
undefined local_dc [128];
undefined4 local_5c [11];
undefined4 local_30;
undefined4 local_18;
undefined4 local_14;
undefined4 local_8;
(*_DAT_0040c1d4)(0,local_364,0x104);
uVar1 = FUN_004019e0();
k_DLL_systemCall?(0x4dbac13f,&local_8);
uVar2 = local_8;
(*_DAT_0040c200)(local_15c,0x40,local_8,uVar1);
uVar2 = (*(code *)k_DLL_FP3)(0,uVar2);
(*(code *)k_DLL_FP1)(uVar2);
k_DLL_systemCall?(0x4dbac13f,&local_8);
(*_DAT_0040c200)(local_dc,0x40,local_8,uVar1);
uVar2 = (*(code *)k_DLL_FP3)(0,local_8);
(*(code *)k_DLL_FP1)(uVar2);
iVar3 = (*_DAT_0040c1b4)(0,1,0,local_15c);
if (iVar3 != 0) {
iVar4 = (*_DAT_0040c1cc)(0,1,local_dc);
if (iVar4 == 0) {
(*_DAT_0040c120)(iVar3);
}
else {
iVar5 = (*_DAT_0040c158)();
if (iVar5 == 0xb7) {
(*_DAT_0040c128)(iVar3);
(*_DAT_0040c120)(iVar3);
(*_DAT_0040c120)(iVar4);
k_DLL_loadfunction?();
return 1;
}
(*k_DLL_FP4)(local_5c,0,0x44);
local_5c[0] = 0x44;
local_30 = 0x80;
iVar5 = (*_DAT_0040c118)(local_364,0,0,0,0,0,0,0,local_5c,&local_18);
if (iVar5 != 0) {
(*_DAT_0040c18c)(iVar3,0xffffffff);
(*_DAT_0040c120)(local_18);
(*_DAT_0040c120)(local_14);
(*_DAT_0040c120)(iVar3);
(*_DAT_0040c120)(iVar4);
return 1;
}
}
}
return 0;
}
undefined entry()
undefined AL:1 <RETURN>
undefined4 HASH:85f2c43 fp?
undefined4 HASH:3f3805b hash
undefined4 HASH:3fcdfb3 loop
entry XREF[2]: Entry Point(*), 004000f0(*)
00409ee0 56 PUSH ESI
00409ee1 68 f0 c1 PUSH first_imported_FP
40 00
00409ee6 68 6c 64 PUSH 0x3966646c
66 39
00409eeb 6a 09 PUSH 0x9
00409eed b9 14 20 MOV ECX,0xd22e2014
2e d2
00409ef2 e8 e9 7c CALL k_get_something_from_TIB undefined4 k_get_something_from_
ff ff
00409ef7 ba f0 11 MOV EDX,DAT_004011f0 = A7h
40 00
00409efc 8b c8 MOV ECX,EAX
00409efe e8 0d 7c CALL k_DLL_importWithHash undefined k_DLL_importWithHash(u
ff ff
The decompiled code was :
fp? = &first_imported_FP;
hash = 0x3966646c;
loop = 9;
k_get_something_from_TIB();
k_DLL_importWithHash(loop,hash,fp?);
But with that, 0xd22e2014 vanished into nothingness.
Clearly, it's there for a reason and ECX have to be an argument, right ? Luckily, there is a call convention for that kind of call.
__fastcall
Argument-passing order :
The first two DWORD or smaller arguments that are found in the argument list from left to right are passed in ECX and EDX registers;
all other arguments are passed on the stack from right to left.
And it become :
fp? = &first_imported_FP;
hash = 0x3966646c;
loop = 9;
k_get_something_from_TIB(0xd22e2014);
k_DLL_importWithHash(loop,hash,fp?);
Yet, it doesn't return anything, which is unlikely. And the 2nd call is :
00409efc 8b c8 MOV ECX,EAX
00409efe e8 0d 7c CALL k_DLL_importWithHash undefined k_DLL_importWithHash(u
ff ff
EAX could be the returned value of k_get_something_from_TIB and passed to k_DLL_importWithHash
Another fast call ? Mmmmm
I don't know... it look like it but i don't like the output.
void entry(void)
{
undefined4 uVar1;
int iVar2;
void *loop;
loop = (void *)0x9;
uVar1 = k_get_something_from_TIB(0xd22e2014);
k_DLL_importWithHash(uVar1,&DAT_004011f0,loop);
loop = (void *)0x48;
uVar1 = k_get_something_from_TIB(0x8f7ee672);
k_DLL_importWithHash(uVar1,&DAT_004010d0,loop);
uVar1 = (*(code *)k_DLL_FP3)(0,0x8000000);
iVar2 = (*DAT_0040c10c)(uVar1);
if (iVar2 != 0) {
(*k_DLL_FP4)(iVar2,0,0x8000000);
uVar1 = (*(code *)k_DLL_FP3)(0,iVar2);
(*(code *)k_DLL_FP1)(uVar1);
k_DLL_beforeLoad?();
}
(*k_quit?)(0);
return;
}
It's probably wrong.
At some point you gotta stop the guesswork, or even stop reading the decompiled output, and focus on the real code : ASM.
That's why i really like IDA : Assembly first.