“Weaning” WinFXNet from greed (part 1)
In the first part of this article, we will quickly go through the process of how I looked for functions that prevent the program from starting.
For research we will need the following utilities:
Detect It Easy (DiE) (github) – file type detector.
Ghidra (github) or IDA/Binary Ninja – a disassembler of your choice.
x64dbg (github) or any debugger you like.
Interactive Delphi Reconstructor (IDR) (github) – interactive decompiler for Delphi.
Dhrake (github) – a set of scripts for Ghidr, tailored for Delphi after IDR.
The program itself – there is no installer anywhere on the Internet, so I uploaded it to github – I hope the file will live for some time.
We start by determining what the program is written in (from the tags and list of utilities, I think you already guessed):
Great, the old version of Delphi is from 2010 – that means IDR will decipher the forms freely. DiE also tells us that protection with NetHASP/Hardlock dongle is used.
Download IDR, unpack archives with binary stamps and analyze our exe file. We wait for the analysis to complete and select “Tools -> IDC generator” in the menu item:
The resulting file will have the following structure:
This file needs to be slightly edited – IDR generates long lines through the line separator, which is why IDA and other scripts will swear. Therefore, the resulting spaces and line breaks must be removed and everything turned into one line
Then we apply the generated IDC script: in IDA Pro, click File → Script File…, select the script and wait for application:
IDA
In Ghidra we will execute scripts from the Dhrake set:
Ghidra
We do everything as written on Github – execute DhrakeInit.java, indicate where our idc file is located, and wait for the script to complete its work.
Great, now the disassembler understands our functions and we don’t have to search for or edit anything manually. Sometimes it happens that functions have incorrect boundaries, but you can correct this yourself.
Let’s return to IDR, which by the way indicated for us a starting function with a simple name EntryPoint. IDR can try to recreate the source code for this function for us by pressing the Src key:
The program will most likely swear, but an approximate description of the assembler functions has been translated into Delphi code:
So, when the program starts, the TStartUpForm form opens – an excellent name for the form, it directly conveys the essence of its purpose. In IDR, open the Forms tab, find our form and see what’s what:
For now, let’s return to the code above and see what the disassemblers generated for the EntryPoint function:
IDA
void __noreturn EntryPoint()
{
int *v0; // ebx
char v1; // zf
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // [esp-Ch] [ebp-2Ch] BYREF
void *v3; // [esp-8h] [ebp-28h]
int *v4; // [esp-4h] [ebp-24h]
int v5; // [esp+4h] [ebp-1Ch]
int v6; // [esp+8h] [ebp-18h]
int v7[5]; // [esp+Ch] [ebp-14h] BYREF
int savedregs; // [esp+20h] [ebp+0h] BYREF
v7[0] = 0;
v6 = 0;
v5 = 0;
InitExe();
v0 = Application[0];
v4 = &savedregs;
v3 = &loc_660198;
ExceptionList = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)&ExceptionList);
TApplication_Initialize(ExceptionList);
TCustomForm_Create(*v0, 1);
*gvar_00685650 = (int)&loc_595777 + 1;
if ( (unsigned __int8)((int (__stdcall *)(struct _EXCEPTION_REGISTRATION_RECORD *))sub_5966B8)(ExceptionList) )
{
TCustomForm_Show();
TControl_Refresh();
TApplication_SetTitle();
TApplication_CreateForm(gvar_00685784, VMT_6160F8_TMainForm);
TApplication_CreateForm(gvar_006857E8, VMT_601A3C_TSpecialSettingsFrm);
TApplication_CreateForm(gvar_00685A24, VMT_5E1248_TAddressReport);
TApplication_CreateForm(gvar_006858D8, VMT_5DFA00_TSelectPanelsDlg);
TApplication_CreateForm(gvar_006859A0, VMT_5E0224_TSelectLoopsDlg);
TApplication_CreateForm(gvar_00685928, VMT_5E0A50_TSelectZonesDlg);
TApplication_CreateForm(gvar_00685A5C, VMT_603868_TDCRangeFrm);
TApplication_CreateForm(gvar_00685AEC, VMT_6044B8_TDCErrorFrm);
if ( ParamCount() > 1 && (ParamStr(), WStrFromUStr(v3), LeftStr(v7, 4), WStrEqual(), v1) )
{
TApplication_CreateForm(gvar_006858DC, VMT_5EBFA8_TFXCommHandler);
TApplication_CreateForm(gvar_00685B90, VMT_600974_TAutoConfigFrm);
TApplication_CreateForm(gvar_00685DC8, &off_5BA430);
}
else
{
TApplication_CreateForm(gvar_00685DC4, VMT_5DC858_TFXColSelDlg);
TApplication_CreateForm(gvar_006859C0, VMT_5D0788_TFXCGroupsDlg);
TApplication_CreateForm(gvar_006858DC, VMT_5EBFA8_TFXCommHandler);
TApplication_CreateForm(gvar_006855B8, VMT_6278E8_TAPFillDlg);
TApplication_CreateForm(gvar_00685E28, VMT_5FD8EC_TFileImportDlg);
TApplication_CreateForm(gvar_00685B94, VMT_5FEF7C_TFileExportDlg);
TApplication_CreateForm(gvar_00685DDC, VMT_5E77C8_TConfigInfoDlg);
TApplication_CreateForm(gvar_00685918, VMT_5DEE04_TSelectVisibleDlg);
TApplication_CreateForm(gvar_00685DC8, &off_5BA430);
TApplication_CreateForm(gvar_006855D0, &cls_FXDbgForm_TDbgFrm);
TApplication_CreateForm(gvar_00685E08, (char *)&loc_5B3C7B + 1);
TApplication_CreateForm(gvar_00685C48, VMT_5F23C8_TPreviewForm);
TApplication_CreateForm(gvar_00685E50, &cls_FXEsaFileMerge_TMergeEsaForm);
TApplication_CreateForm(gvar_006855D4, &cls_FXEsaFileMergeReport_TEsaReport);
}
UStrAsg();
*(_BYTE *)(*v0 + 91) = 0;
TApplication_Run();
}
__writefsdword(0, (unsigned int)v4);
UStrClr(&loc_66019F);
WStrArrayClr();
Halt0();
}
Ghidra
void EntryPoint(void)
{
undefined *puVar1;
char cVar2;
undefined4 uVar3;
int iVar4;
undefined4 unaff_EBX;
undefined4 *in_FS_OFFSET;
bool bVar5;
undefined4 uStack_30;
undefined *puStack_2c;
undefined *puStack_28;
undefined4 local_20;
undefined4 local_1c;
wchar_t *local_18 [5];
local_18[0] = (wchar_t *)0x0;
local_1c = 0;
local_20 = 0;
puStack_28 = (undefined *)0x65fed4;
@InitExe(&DAT_00658424);
puVar1 = Application;
puStack_2c = &LAB_00660198;
uStack_30 = *in_FS_OFFSET;
*in_FS_OFFSET = &uStack_30;
puStack_28 = &stack0xfffffffc;
TApplication.Initialize(*(undefined4 *)puVar1);
uVar3 = TCustomForm.Create(&LAB_00595778,1,*(undefined4 *)puVar1);
*(undefined4 *)gvar_00685650 = uVar3;
cVar2 = FUN_005966b8(*(undefined4 *)gvar_00685650);
if (cVar2 == '\0') goto LAB_00660175;
TCustomForm.Show(*(undefined4 *)gvar_00685650);
TControl.Refresh(*(undefined4 *)gvar_00685650);
TApplication.SetTitle(*(undefined4 *)puVar1,&DAT_006601b4);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_00616150,gvar_00685784);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_00601a94,gvar_006857E8);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005e12a0,gvar_00685A24);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005dfa58,gvar_006858D8);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005e027c,gvar_006859A0);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005e0aa8,gvar_00685928);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_006038c0,gvar_00685A5C);
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_00604510,gvar_00685AEC);
iVar4 = ParamCount();
if (iVar4 < 2) {
LAB_00660052:
uStack_30 = 0x660065;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005dc8b0,gvar_00685DC4);
uStack_30 = 0x660078;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005d07e0,gvar_006859C0);
uStack_30 = 0x66008b;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005ec000,gvar_006858DC);
uStack_30 = 0x66009e;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_00627940,gvar_006855B8);
uStack_30 = 0x6600b1;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005fd944,gvar_00685E28);
uStack_30 = 0x6600c4;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005fefd4,gvar_00685B94);
uStack_30 = 0x6600d7;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005e7820,gvar_00685DDC);
uStack_30 = 0x6600ea;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005dee5c,gvar_00685918);
uStack_30 = 0x6600fd;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005ba430,gvar_00685DC8);
uStack_30 = 0x660110;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_0059ab9c,gvar_006855D0);
uStack_30 = 0x660123;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005b3c7c,gvar_00685E08);
uStack_30 = 0x660136;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005f2420,gvar_00685C48);
uStack_30 = 0x660149;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005babf0,gvar_00685E50);
uStack_30 = 0x66015c;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005b3ef4,gvar_006855D4);
}
else {
ParamStr(2,&local_20);
@WStrFromUStr(&local_1c,local_20);
uStack_30 = 0x660005;
LeftStr(local_1c,4,local_18);
uStack_30 = 0x660012;
bVar5 = @WStrEqual(local_18[0],L"auto");
if (!bVar5) goto LAB_00660052;
uStack_30 = 0x660027;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005ec000,gvar_006858DC);
uStack_30 = 0x66003a;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_006009cc,gvar_00685B90);
uStack_30 = 0x66004d;
TApplication.CreateForm(*(undefined4 *)puVar1,&LAB_005ba430,gvar_00685DC8);
}
uStack_30 = 0x660168;
@UStrAsg(*(int *)puVar1 + 0x50,0);
*(undefined *)(*(int *)puVar1 + 0x5b) = 0;
uStack_30 = 0x660175;
TApplication.Run(*(undefined4 *)puVar1);
LAB_00660175:
*in_FS_OFFSET = puStack_2c;
puStack_28 = (undefined *)0x66018a;
@UStrClr(&local_20,puStack_2c,unaff_EBX);
puStack_28 = (undefined *)0x660197;
@WStrArrayClr(&local_1c,2);
return;
}
Here you can choose as you like (I think Hydra gave it more clearly), but the essence is the same for everyone – we look at the execution of the function FUN_005966b8.
IDA
void __fastcall sub_5966B8(int a1)
{
int v1; // esi
int v2; // ebx
double v3; // st7
double v4; // st7
char v5; // bl
int v6; // esi
char v7; // zf
const WCHAR *v8; // eax
const WCHAR *v9; // eax
double v10; // st7
int v11; // ecx
int v12; // ecx
int v13; // ecx
int v14; // ecx
const WCHAR *v15; // [esp-14h] [ebp-390h]
const WCHAR *v16; // [esp-14h] [ebp-390h]
struct _EXCEPTION_REGISTRATION_RECORD *ExceptionList; // [esp-Ch] [ebp-388h] BYREF
void *v18; // [esp-8h] [ebp-384h]
int *v19; // [esp-4h] [ebp-380h]
int v20; // [esp+Ch] [ebp-370h] BYREF
int v21[2]; // [esp+10h] [ebp-36Ch] BYREF
int v22; // [esp+18h] [ebp-364h] BYREF
char v23[256]; // [esp+1Ch] [ebp-360h] BYREF
int v24; // [esp+11Ch] [ebp-260h] BYREF
int v25; // [esp+120h] [ebp-25Ch] BYREF
int v26; // [esp+124h] [ebp-258h] BYREF
int v27; // [esp+128h] [ebp-254h] BYREF
__int64 v28; // [esp+12Ch] [ebp-250h] BYREF
double v29; // [esp+134h] [ebp-248h]
int v30; // [esp+13Ch] [ebp-240h] BYREF
double v31; // [esp+140h] [ebp-23Ch]
int v32; // [esp+148h] [ebp-234h] BYREF
WCHAR pszPath[261]; // [esp+14Ch] [ebp-230h] BYREF
char v34; // [esp+357h] [ebp-25h]
int v35; // [esp+358h] [ebp-24h]
int v36; // [esp+35Ch] [ebp-20h] BYREF
int v37; // [esp+360h] [ebp-1Ch] BYREF
int v38; // [esp+364h] [ebp-18h] BYREF
int v39; // [esp+368h] [ebp-14h] BYREF
int v40; // [esp+36Ch] [ebp-10h] BYREF
int v41; // [esp+370h] [ebp-Ch] BYREF
int v42; // [esp+374h] [ebp-8h] BYREF
int v43; // [esp+378h] [ebp-4h] BYREF
int savedregs; // [esp+37Ch] [ebp+0h] BYREF
v35 = a1;
v19 = &savedregs;
v18 = &loc_596DE0;
ExceptionList = NtCurrentTeb()->NtTib.ExceptionList;
__writefsdword(0, (unsigned int)&ExceptionList);
v34 = 0;
do
{
v1 = 2;
v2 = sub_590CDC(*(_DWORD *)(v35 + 932));
if ( v2 == 7 )
v1 = MessageDlgPosHelp(0, -1, -1, 0);
}
while ( v2 && v1 != 2 );
if ( v2 > 41 )
{
if ( v2 != 8001 && v2 != 8002 )
{
if ( v2 != 8003 )
{
LABEL_20:
LODWORD(v31) = v2;
BYTE4(v31) = 0;
Format(&v32);
MessageDlgPosHelp(0, -1, -1, 0);
}
LABEL_21:
if ( v2 )
{
v34 = 0;
goto LABEL_59;
}
v31 = sub_58FD08(*(_DWORD *)(v35 + 932));
v3 = ((double (__fastcall *)(_DWORD))loc_58FCA8)(*(_DWORD *)(v35 + 932));
if ( v31 - v3 < 60.0 )
{
v29 = sub_58FD08(*(_DWORD *)(v35 + 932));
v4 = ((double (__fastcall *)(_DWORD))loc_58FCA8)(*(_DWORD *)(v35 + 932));
v28 = TRUNC(v29 - v4);
LODWORD(v31) = &v28;
BYTE4(v31) = 16;
Format(&v30);
MessageDlgPosHelp(0, -1, -1, 0);
}
shell32_SHGetSpecialFolderPathW(0, pszPath, 28, 0);
UStrFromWArray(&v27, pszPath, 261);
UStrCat3(&v43, v27, &off_596E0C);
UStrCat3(&v42, v43, &loc_596E38);
UStrFromWArray(&v26, pszPath, 261);
UStrCat3(&v41, v26, &off_596E60);
UStrCat3(&v40, v41, &loc_596E38);
UStrFromWArray(&v25, pszPath, 261);
UStrCat3(&v39, v25, aSchneider);
UStrCat3(&v38, v39, &loc_596E38);
while ( 1 )
{
if ( (unsigned __int8)FileExists(v38) )
{
UStrLAsg(&v37, v38);
}
else if ( (unsigned __int8)FileExists(v40) )
{
UStrLAsg(&v37, v40);
}
else
{
UStrLAsg(&v37, v42);
}
v5 = 0;
v6 = sub_595EF4(LODWORD(dbl_70911C) + 1032);
if ( !v6 )
{
v5 = 0;
UStrEqual(v37, v40);
if ( v7 || (UStrEqual(v37, v42), v7) )
{
if ( !(unsigned __int8)DirectoryExists(v39) )
ForceDirectories(v39);
v15 = (const WCHAR *)UStrToPWChar(v38);
v8 = (const WCHAR *)UStrToPWChar(v37);
kernel32_CopyFileW(v8, v15, 0);
}
goto LABEL_54;
}
if ( (unsigned int)(v6 - 1) < 4 )
{
if ( MessageDlgPosHelp(0, -1, -1, 0) != 6 )
{
v5 = 0;
goto LABEL_54;
}
if ( !(unsigned __int8)sub_5960D8(LODWORD(dbl_70911C), &v36) )
{
v5 = 0;
goto LABEL_54;
}
if ( !(unsigned __int8)DirectoryExists(v39) )
ForceDirectories(v39);
}
else
{
if ( v6 != 5 )
goto LABEL_54;
v10 = ((double (__fastcall *)(_DWORD))loc_58FCA8)(*(_DWORD *)(v35 + 932));
LODWORD(v31) = TRUNC(*(double *)(LODWORD(dbl_70911C) + 1032) - v10);
BYTE4(v31) = 0;
Format(&v24);
if ( MessageDlgPosHelp(0, -1, -1, 0) != 6 )
{
v5 = 0;
goto LABEL_54;
}
if ( !(unsigned __int8)sub_5960D8(LODWORD(dbl_70911C), &v36) )
{
v5 = 0;
goto LABEL_54;
}
if ( !(unsigned __int8)DirectoryExists(v39) )
ForceDirectories(v39);
}
v16 = (const WCHAR *)UStrToPWChar(v38);
v9 = (const WCHAR *)UStrToPWChar(v36);
kernel32_CopyFileW(v9, v16, 0);
v5 = 1;
LABEL_54:
if ( !v5 )
{
if ( v6 == 5 )
v6 = 0;
if ( !v6 )
{
LStrFromString(ExceptionList);
sub_5912B4(v21[1], &v22);
LStrToString(v23, v22, 255);
LOBYTE(v11) = 50;
PStrNCpy(LODWORD(dbl_70911C) + 166, v23, v11);
LStrFromString(v18);
sub_5912B4(v20, v21);
LStrToString(v23, v21[0], 255);
LOBYTE(v12) = 25;
PStrNCpy(LODWORD(dbl_70911C) + 140, v23, v12);
*(_BYTE *)gvar_0068564C = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 1) != 0;
*(_BYTE *)gvar_00685670 = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 2) != 0;
*(_BYTE *)gvar_00685A88 = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 4) != 0;
*(_BYTE *)gvar_00685DE8 = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 8) != 0;
*(_BYTE *)gvar_00685800 = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 0x10) != 0;
*(_BYTE *)gvar_00685DFC = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 0x20) != 0;
*(_BYTE *)gvar_00685964 = (*(_BYTE *)(LODWORD(dbl_70911C) + 138) & 0x40) != 0;
*(_BYTE *)gvar_00685C5C = *(char *)(LODWORD(dbl_70911C) + 138) < 0;
v34 = 1;
}
LABEL_59:
__writefsdword(0, (unsigned int)ExceptionList);
v19 = (int *)&loc_596DE7;
LStrArrayClr(&v20, 4);
UStrArrayClr(&v24, 4, v13);
UStrClr(v19);
UStrClr(v19);
UStrArrayClr(&v36, 8, v14);
JUMPOUT(0x596DF1);
}
}
}
}
else if ( v2 != 41 )
{
if ( !v2 || v2 == 7 )
goto LABEL_21;
if ( v2 != 12 && v2 != 25 )
goto LABEL_20;
}
MessageDlgPosHelp(0, -1, -1, 0);
goto LABEL_21;
}
Ghidra
void FUN_005966b8(int param_1)
{
bool bVar1;
wchar_t *pwVar2;
char cVar3;
uint uVar4;
int iVar5;
LPCWSTR pWVar6;
LPCWSTR pWVar7;
int iVar8;
wchar_t *unaff_EBX;
undefined4 unaff_ESI;
wchar_t *unaff_EDI;
int *in_FS_OFFSET;
bool bVar9;
float10 in_ST0;
float10 in_ST1;
float10 in_ST2;
undefined4 param_11;
undefined4 local_374;
undefined4 local_370;
undefined4 local_36c;
undefined4 local_368;
undefined local_364 [256];
undefined4 local_264;
wchar_t *local_260;
wchar_t *local_25c;
wchar_t *local_258;
undefined4 local_254 [2];
double local_24c;
undefined4 local_244;
undefined8 local_240;
undefined4 local_238;
WCHAR local_234 [252];
undefined4 uStackY_3c;
undefined4 uStackY_38;
BOOL BVar10;
undefined4 *puVar11;
int local_24;
undefined *local_20;
wchar_t *local_1c;
wchar_t *local_c;
wchar_t *local_8;
local_1c = (wchar_t *)&stack0xfffffffc;
iVar8 = 0x6e;
do {
local_8 = (wchar_t *)0x0;
iVar8 = iVar8 + -1;
} while (iVar8 != 0);
local_20 = &LAB_00596de0;
local_24 = *in_FS_OFFSET;
*in_FS_OFFSET = (int)&local_24;
do {
iVar8 = 2;
puVar11 = (undefined4 *)(param_1 + 0x3a4);
param_1 = 0x5966f7;
uVar4 = FUN_00590cdc(*puVar11);
if (uVar4 == 0x70) {
param_1 = 0;
uStackY_38 = 0x59672d;
iVar8 = MessageDlgPosHelp((&PTR_u_USB_License_key_could_not_be_fou_006829f0)
[(uint)(byte)*gvar_00685658 * 0xe],1,0x28);
}
} while ((uVar4 != 0) && (iVar8 != 2));
if ((int)uVar4 < 0x2a) {
if (uVar4 == 0x29) {
param_1 = 0;
uStackY_38 = 0x5967af;
MessageDlgPosHelp((&PTR_u_The_USB_License_key_has_expired_00682a0c)
[(uint)(byte)*gvar_00685658 * 0xe],1,4);
goto LAB_005968a3;
}
if ((uVar4 == 0) || (uVar4 == 7)) goto LAB_005968a3;
if ((uVar4 == 0xc) || (uVar4 == 0x19)) {
param_1 = 0;
uStackY_38 = 0x59684b;
MessageDlgPosHelp((&PTR_u_USB_License_key_clock_failure_00682a04)
[(uint)(byte)*gvar_00685658 * 0xe],1,4);
goto LAB_005968a3;
}
}
else {
if (uVar4 == 0x1f41) {
param_1 = 0;
uStackY_38 = 0x5967e3;
MessageDlgPosHelp((&PTR_u_USB_License_key_is_invalid_006829fc)
[(uint)(byte)*gvar_00685658 * 0xe],1,4);
goto LAB_005968a3;
}
if (uVar4 == 0x1f42) {
param_1 = 0;
uStackY_38 = 0x596817;
MessageDlgPosHelp((&PTR_u_The_USB_License_key_has_expired_00682a0c)
[(uint)(byte)*gvar_00685658 * 0xe],1,4);
goto LAB_005968a3;
}
if (uVar4 == 0x1f43) goto LAB_005968a3;
}
local_240._0_5_ = (uint5)uVar4;
Format((&PTR_u_USB_License_key_error:_%d_006829f8)[(uint)(byte)*gvar_00685658 * 0xe],&local_240 ,0)
;
param_1 = 0;
uStackY_38 = 0x5968a3;
MessageDlgPosHelp(local_238,1,4);
LAB_005968a3:
if (uVar4 == 0) {
iVar8 = 0x5968be;
FUN_0058fd08(*(undefined4 *)(param_1 + 0x3a4));
local_240 = (double)in_ST0;
FUN_0058fca8(*(undefined4 *)(iVar8 + 0x3a4));
if ((float10)local_240 - in_ST1 < (float10)60.0) {
puVar11 = &local_244;
FUN_0058fd08(param_11);
local_24c = (double)in_ST2;
FUN_0058fca8(puVar11[0xe9]);
local_254[0] = @TRUNC();
local_240._0_5_ = CONCAT14(0x10,local_254);
Format((&PTR_u_The_USB_License_key_for_this_sof_006829f4)[(uint)(byte)*gvar_00685658 * 0xe] ,
&local_240,0);
uStackY_38 = 0x59697f;
MessageDlgPosHelp(local_244,0,4);
}
uStackY_38 = 0x596991;
shell32.SHGetSpecialFolderPathW((HWND)0x0,local_234,0x1c,0);
@UStrFromWArray(&local_258,local_234,0x105);
@UStrCat3(&local_8,local_258,L"\\Esmi\\WinFXNet\\");
@UStrCat3(&local_c,local_8,L"winfxnet.lic");
@UStrFromWArray(&local_25c,local_234,0x105);
@UStrCat3((wchar_t **)&stack0xfffffff0,local_25c,L"\\Pelco\\WinFXNet\\");
@UStrCat3((wchar_t **)&stack0xffffffec,unaff_EBX,L"winfxnet.lic");
@UStrFromWArray(&local_260,local_234,0x105);
@UStrCat3((wchar_t **)&stack0xffffffe8,local_260,L"\\Schneider Electric\\WinFXNet\\");
@UStrCat3(&local_1c,unaff_EDI,L"winfxnet.lic");
do {
cVar3 = FileExists(local_1c);
if (cVar3 == '\0') {
cVar3 = FileExists(unaff_ESI);
if (cVar3 == '\0') {
iVar8 = 0x596a79;
@UStrLAsg(&local_20,local_c);
}
else {
iVar8 = 0x596a6c;
@UStrLAsg(&local_20,unaff_ESI);
}
}
else {
iVar8 = 0x596a53;
@UStrLAsg(&local_20,local_1c);
}
bVar1 = false;
local_24 = DAT_0070911c + 0x408;
iVar5 = FUN_00595ef4(DAT_0070911c,local_20,DAT_0070911c + 4);
if (iVar5 == 0) {
bVar1 = false;
bVar9 = true;
@UStrEqual(local_20,unaff_ESI);
if ((bVar9) || (@UStrEqual(local_20,local_c), bVar9)) {
cVar3 = DirectoryExists(unaff_EDI);
if (cVar3 == '\0') {
ForceDirectories(unaff_EDI);
}
BVar10 = 0;
pWVar6 = (LPCWSTR)@UStrToPWChar(local_1c);
pWVar7 = (LPCWSTR)@UStrToPWChar(local_20);
uStackY_38 = 0x596af8;
kernel32.CopyFileW(pWVar7,pWVar6,BVar10);
}
}
else if (iVar5 - 1U < 4) {
uStackY_38 = 0;
uStackY_3c = 0x596b30;
iVar8 = MessageDlgPosHelp(*(undefined4 *)
((uint)(byte)*gvar_00685658 * 0x38 + 0x6829d4 + iVar5 * 4),1,3) ;
if (iVar8 == 6) {
cVar3 = FUN_005960d8(DAT_0070911c,&local_24);
if (cVar3 == '\0') {
bVar1 = false;
}
else {
cVar3 = DirectoryExists(unaff_EDI);
if (cVar3 == '\0') {
ForceDirectories(unaff_EDI);
}
BVar10 = 0;
pWVar6 = (LPCWSTR)@UStrToPWChar(local_1c);
pWVar7 = (LPCWSTR)@UStrToPWChar(local_24);
uStackY_38 = 0x596b70;
kernel32.CopyFileW(pWVar7,pWVar6,BVar10);
bVar1 = true;
}
}
else {
bVar1 = false;
}
}
else if (iVar5 == 5) {
FUN_0058fca8(*(undefined4 *)(iVar8 + 0x3a4));
uVar4 = @TRUNC();
local_240._0_5_ = (uint5)uVar4;
Format((&PTR_u_The_license_information_file_for_006829e8)[(uint)(byte)*gvar_00685658 * 0x e],
&local_240,0);
uStackY_38 = 0;
uStackY_3c = 0x596bfc;
iVar8 = MessageDlgPosHelp(local_264,0,3);
if (iVar8 == 6) {
cVar3 = FUN_005960d8(DAT_0070911c,&local_24);
if (cVar3 == '\0') {
bVar1 = false;
}
else {
cVar3 = DirectoryExists(unaff_EDI);
if (cVar3 == '\0') {
ForceDirectories(unaff_EDI);
}
BVar10 = 0;
pWVar6 = (LPCWSTR)@UStrToPWChar(local_1c);
pWVar7 = (LPCWSTR)@UStrToPWChar(local_24);
uStackY_38 = 0x596c3c;
kernel32.CopyFileW(pWVar7,pWVar6,BVar10);
bVar1 = true;
}
}
else {
bVar1 = false;
}
}
} while (bVar1);
if (iVar5 == 5) {
iVar5 = 0;
}
if (iVar5 == 0) {
@LStrFromString(&local_36c,DAT_0070911c + 0xa6,0);
local_24 = 0x596c86;
FUN_005912b4(local_36c,&local_368);
local_24 = 0x596c9c;
@LStrToString(local_364,local_368,0xff);
local_24 = 0x596cb0;
@PStrNCpy(DAT_0070911c + 0xa6,local_364,0x32);
local_24 = 0x596cc8;
@LStrFromString(&local_374,DAT_0070911c + 0x8c,0);
local_24 = 0x596cd9;
FUN_005912b4(local_374,&local_370);
local_24 = 0x596cef;
@LStrToString(local_364,local_370,0xff);
local_24 = 0x596d03;
@PStrNCpy(DAT_0070911c + 0x8c,local_364,0x19);
*gvar_0068564C = (*(byte *)(DAT_0070911c + 0x8a) & 1) != 0;
*gvar_00685670 = (*(byte *)(DAT_0070911c + 0x8a) & 2) != 0;
*gvar_00685A88 = (*(byte *)(DAT_0070911c + 0x8a) & 4) != 0;
*gvar_00685DE8 = (*(byte *)(DAT_0070911c + 0x8a) & 8) != 0;
*gvar_00685800 = (*(byte *)(DAT_0070911c + 0x8a) & 0x10) != 0;
*gvar_00685DFC = (*(byte *)(DAT_0070911c + 0x8a) & 0x20) != 0;
*gvar_00685964 = (*(byte *)(DAT_0070911c + 0x8a) & 0x40) != 0;
*gvar_00685C5C = (*(byte *)(DAT_0070911c + 0x8a) & 0x80) != 0;
}
}
pwVar2 = local_1c;
*in_FS_OFFSET = local_24;
local_1c = (wchar_t *)&LAB_00596de7;
local_20 = (undefined *)0x596dac;
@LStrArrayClr(&local_374,4,pwVar2);
local_20 = (undefined *)0x596dbc;
@UStrArrayClr(&local_264,4);
local_20 = (undefined *)0x596dc7;
@UStrClr(&local_244);
local_20 = (undefined *)0x596dd2;
@UStrClr(&local_238);
local_20 = (undefined *)0x596ddf;
@UStrArrayClr(&local_24,8);
return;
}
We see a call to the function FUN_00590cdc, which, depending on the return value, will allow us to run the program further or not. Based on the loop condition, if we comment out the call to this function and change the condition to check equality, then everything should go further. Let’s try – open x32dbg, load the executable, go to address 005966f2 (we got this address from the same hydra):
x32dbg
I replace the call to this function with mov eax, 0x0
and I see that this comes out:
x32dbg
Great! We have reached the point of selecting a software license file. We have a valid license file (unlike a HASP key), so I specify it and it will appear in the open folder on the video (\AppData\Local\Schneider Electric\WinFXNet).
Let’s continue to clean the program from hidden key checks. Let’s return to IDR. Let’s open the main form and look at the elements:
The clever name of the timer catches your eye – LicTimer, which has a function call call 0061EFB8
. Let’s see what’s inside:
Ghidra
void FUN_0061efb8(void)
{
undefined *puVar1;
int iVar2;
int iVar3;
undefined4 *in_FS_OFFSET;
undefined4 uStack_2c;
undefined *puStack_28;
undefined *puStack_24;
int local_14;
undefined local_10;
undefined4 local_c [2];
puStack_24 = &stack0xfffffffc;
local_c[0] = 0;
puStack_28 = &LAB_0061f0de;
uStack_2c = *in_FS_OFFSET;
*in_FS_OFFSET = &uStack_2c;
if (*gvar_00685734 == '\0') {
iVar3 = 0;
puStack_24 = &stack0xfffffffc;
do {
iVar2 = FUN_005054a9();
if (iVar2 != 0) {
iVar2 = FUN_005054a9();
}
if (iVar2 != 0) {
if (iVar2 == 7) {
iVar3 = MessageDlgPosHelp(*(undefined4 *)
(PTR_PTR_u_The_license_information_file_cou_0068570c +
(uint)(byte)*gvar_00685658 * 0x38 + 0x18),1,0x28,0,0xffffffff ,
0xffffffff,0);
}
else {
local_10 = 0;
local_14 = iVar2;
Format(*(undefined4 *)
(PTR_PTR_u_The_license_information_file_cou_0068570c +
(uint)(byte)*gvar_00685658 * 0x38 + 0x20),&local_14,0,local_c);
iVar3 = MessageDlgPosHelp(local_c[0],1,4,0,0xffffffff,0xffffffff,0);
}
}
} while (((iVar2 != 0) && (iVar3 != 2)) && (iVar3 != 1));
FUN_005055e9();
}
puVar1 = puStack_24;
*in_FS_OFFSET = uStack_2c;
puStack_24 = &LAB_0061f0e5;
puStack_28 = (undefined *)0x61f0dd;
@UStrClr(local_c,uStack_2c,puVar1);
return;
}
I think it’s immediately obvious that the license is being checked again. In IDR you can export the entire project via File -> Save Delphi Project, which is what we will do. Let’s open a file search in Notepad++ and look for all occurrences of this function:
We continue further in x32dbg – we go to the addresses, nop’em call the function and for the opening procedure we change the transition from jne to je (otherwise the application will close there according to the condition).
That’s all – we have a working version of the program without requiring a HASP key. All that remains is to figure out how to generate a license for the program, what the serial number affects, and how the program understands what functions are available under this license. I’ll leave this for the second part, if I finish it (or are allowed to finish it), I’ll write it. But for now (as a bonus) to understand license generation, let’s see how the license file is decrypted inside the program.
License file
First of all, let’s look at the file itself using the HEX editor:
HEX lic
The file is 512 bytes in size, of which the first 127 are normal text, the rest is an incomprehensible set of bytes. Remember, in the text above, I talked about the OpenDialog function – you can put a breakpoint on it, but I did it more cleverly. We run the cycle with opening the file for the first time, a window will appear that the license is available for another N days and will offer to continue or open another file. At this time, in x32dbg we look at the stack call and look at the lines where we read the file into memory. At this address in the dump we set a read/write breakpoint and return to the program. Select the file again, click open and get to our breakpoint. The function responsible for decrypting the file from memory is FUN_00595d28.
Assembler
00595D28 push ebx
00595D29 push esi
00595D2A mov esi,edx
00595D2C movzx edx,byte ptr [esi+80]
00595D33 mov cl,7E
00595D35 lea eax,[esi+82]
00595D3B movzx ebx,dl
00595D3E movzx ebx,byte ptr [esi+ebx]
00595D42 not bl
00595D44 sub byte ptr [eax],bl
00595D46 inc edx
00595D47 and dl,7F
00595D4A inc eax
00595D4B dec cl
>00595D4D jne 00595D3B
00595D4F mov al,1
00595D51 pop esi
00595D52 pop ebx
00595D53 ret
Ghidra
undefined4 FUN_00595d28(undefined4 param_1,int param_2)
{
char *pcVar1;
char cVar2;
byte bVar3;
bVar3 = *(byte *)(param_2 + 0x80);
cVar2 = '~';
pcVar1 = (char *)(param_2 + 0x82);
do {
*pcVar1 = *pcVar1 - ~*(byte *)(param_2 + (uint)bVar3);
bVar3 = bVar3 + 1 & 0x7f;
pcVar1 = pcVar1 + 1;
cVar2 = cVar2 + -1;
} while (cVar2 != '\0');
return CONCAT31((int3)((uint)pcVar1 >> 8),1);
}
IDA
char __fastcall sub_595D28(int a1, int a2)
{
int v3; // edx
char v4; // cl
_BYTE *v5; // eax
v3 = *(unsigned __int8 *)(a2 + 128);
v4 = 126;
v5 = (_BYTE *)(a2 + 130);
do
{
*v5 += *(_BYTE *)(a2 + (unsigned __int8)v3++) + 1;
LOBYTE(v3) = v3 & 0x7F;
++v5;
--v4;
}
while ( v4 );
return 1;
}
To understand what is happening, it is better to simply step-by-step debugging to see what is happening with the registers and the memory area where the loaded license file is located:
x32dbg
We look at the value of the EDX register – it starts from 0000060, reaches 7E, resets to zero (after and dl,7F
) and further to 5D and the cycle exits. That is, the first 127 bytes in the license file are nothing more than a code word for encoding, just counting starts at the offset [esi+80]
, reaches the end of the phrase, returns from the very beginning and to the penultimate character, where the counting began. Brilliant and simple. All you have to do is turn the stuffing backwards and you will get a license generator.
Bottom line
I’m very lucky that the program is not packaged with anything, there are almost no anti-debugging tools (I saw only one thing). In general, if you further understand the HASP structure, you can make a key emulator, but for this you physically need to have a key at least at the first stage. Well, we need to understand further about the license for the software and the rest, as the decryption algorithm described above is simple, you just need to transfer it to a simple programming language.