SAP license + password checking functions...
While returning to my installed in VMware SAP IDES, I figured out that the installation is expired and I cannot login anymore.
First extremely good news is that full disp+work.pdb file is here, it contain almost everything: function names, structures, types, local variable and argument names, etc. What a lavish gift!
I got TYPEINFODUMP utility for converting PDB files into something readable and grepable.
Here is an example of function information + its arguments + its local variables:
FUNCTION ThVmcSysEvent Address: 10143190 Size: 675 bytes Index: 60483 TypeIndex: 60484 Type: int NEAR_C ThVmcSysEvent (unsigned int, unsigned char, unsigned short*) Flags: 0 PARAMETER events Address: Reg335+288 Size: 4 bytes Index: 60488 TypeIndex: 60489 Type: unsigned int Flags: d0 PARAMETER opcode Address: Reg335+296 Size: 1 bytes Index: 60490 TypeIndex: 60491 Type: unsigned char Flags: d0 PARAMETER serverName Address: Reg335+304 Size: 8 bytes Index: 60492 TypeIndex: 60493 Type: unsigned short* Flags: d0 STATIC_LOCAL_VAR func Address: 12274af0 Size: 8 bytes Index: 60495 TypeIndex: 60496 Type: wchar_t* Flags: 80 LOCAL_VAR admhead Address: Reg335+304 Size: 8 bytes Index: 60498 TypeIndex: 60499 Type: unsigned char* Flags: 90 LOCAL_VAR record Address: Reg335+64 Size: 204 bytes Index: 60501 TypeIndex: 60502 Type: AD_RECORD Flags: 90 LOCAL_VAR adlen Address: Reg335+296 Size: 4 bytes Index: 60508 TypeIndex: 60509 Type: int Flags: 90
And here is an example of some structure:
STRUCT DBSL_STMTID Size: 120 Variables: 4 Functions: 0 Base classes: 0 MEMBER moduletype Type: DBSL_MODULETYPE Offset: 0 Index: 3 TypeIndex: 38653 MEMBER module Type: wchar_t module[40] Offset: 4 Index: 3 TypeIndex: 831 MEMBER stmtnum Type: long Offset: 84 Index: 3 TypeIndex: 440 MEMBER timestamp Type: wchar_t timestamp[15] Offset: 88 Index: 3 TypeIndex: 6612
Wow!
OK, now grepping that file for "license":
cat "disp+work.pdb.d" | grep FUNCTION | grep -i license | sort | uniq
FUNCTION check_license FUNCTION CheckProductLicense FUNCTION DeleteLicense FUNCTION GetAllLicenses FUNCTION InstallLicense FUNCTION InstallTempLicense FUNCTION likey_delete_licenses FUNCTION likey_get_all_licenses FUNCTION likeyadm_cleanup_all_licenses FUNCTION likeyadm_delete_licenses FUNCTION likeyadm_get_all_licenses FUNCTION likeyche_fill_licenses_list FUNCTION likeyind_fill_licenses_entry FUNCTION SlicAddLicense .... FUNCTION SlLikeyAbInstallTempLicense FUNCTION warning_license_expiration
Starting with check_license() function, I also noticed a lot of debugging DpTrc() calls there:
.text:00000001402D948F lea rdx, [rsp+88h+s1] ; passwdU16 .text:00000001402D9494 lea rcx, [rsp+88h+dayU] ; dayU .text:00000001402D9499 call SlicPwForDay .text:00000001402D949E lea rdx, aRpoump1r ; "RPOUMP1R" .text:00000001402D94A5 lea rcx, [rsp+88h+s1] ; s1 .text:00000001402D94AA mov r8, rbp ; n .text:00000001402D94AD call memcmpU16 .text:00000001402D94B2 mov rbp, [rsp+88h+arg_10] .text:00000001402D94BA test eax, eax .text:00000001402D94BC jnz short loc_1402D94EA .text:00000001402D94BE .text:00000001402D94BE loc_1402D94BE: ; DATA XREF: .pdata:00000001456DDD04o .text:00000001402D94BE ; .pdata:00000001456DDD10o .text:00000001402D94BE cmp cs:ct_level, 2 .text:00000001402D94C5 mov cs:do_license_check, eax .text:00000001402D94CB jl short loc_1402D94EA .text:00000001402D94CD call DpLock .text:00000001402D94D2 mov rcx, cs:hdl ; hdl .text:00000001402D94D9 lea rdx, aLicenseCheckDi ; "License check disabled.\n" .text:00000001402D94E0 call DpTrc .text:00000001402D94E5 call DpUnlock .text:00000001402D94EA .text:00000001402D94EA loc_1402D94EA: ; CODE XREF: check_license+61j
These debugging calls are very usable. Here you can also notice ct_level global variable.
Here about it:
http://help.sap.com/saphelp_nwpi71/helpdata/en/46/962416a5a613e8e1000000...
SlicPwForDay()? Password for a day? Huh? "RPOUMP1R"? If I understood correctly, this is related to temporary license.
I also noticed function get_hwkey() - I didn't knew SAP use hardware dongles.
But well, the only thing here I need is do_license_check global variable. Setting it to 0 in executable file is enough to disable license check.
Another problem I got is that SAPGUI cannot login anymore and it says "Password logon no longer possible - too many failed attempts".
Let's do grepping again:
cat "disp+work.pdb.d" | grep FUNCTION | grep -i password
We got:
FUNCTION rcui::AgiPassword::DiagISelection FUNCTION ssf_password_encrypt FUNCTION ssf_password_decrypt FUNCTION password_logon_disabled FUNCTION dySignSkipUserPassword FUNCTION migrate_password_history FUNCTION password_is_initial FUNCTION rcui::AgiPassword::IsVisible FUNCTION password_distance_ok FUNCTION get_password_downwards_compatibility FUNCTION dySignUnSkipUserPassword FUNCTION rcui::AgiPassword::GetTypeName FUNCTION `rcui::AgiPassword::AgiPassword'::`1'::dtor$2 FUNCTION `rcui::AgiPassword::AgiPassword'::`1'::dtor$0 FUNCTION `rcui::AgiPassword::AgiPassword'::`1'::dtor$1 FUNCTION usm_set_password FUNCTION rcui::AgiPassword::TraceTo FUNCTION days_since_last_password_change FUNCTION rsecgrp_generate_random_password FUNCTION rcui::AgiPassword::`scalar deleting destructor' FUNCTION password_attempt_limit_exceeded FUNCTION handle_incorrect_password FUNCTION `rcui::AgiPassword::`scalar deleting destructor''::`1'::dtor$1 FUNCTION calculate_new_password_hash FUNCTION shift_password_to_history FUNCTION rcui::AgiPassword::GetType FUNCTION found_password_in_history FUNCTION `rcui::AgiPassword::`scalar deleting destructor''::`1'::dtor$0 FUNCTION rcui::AgiObj::IsaPassword FUNCTION password_idle_check FUNCTION SlicHwPasswordForDay FUNCTION rcui::AgiPassword::IsaPassword FUNCTION rcui::AgiPassword::AgiPassword FUNCTION delete_user_password FUNCTION usm_set_user_password FUNCTION Password_API FUNCTION get_password_change_for_SSO FUNCTION password_in_USR40 FUNCTION rsec_agrp_abap_generate_random_password
After playing for a little with these functions, I quickly noticed that the problem is in password_attempt_limit_exceeded(). It is called from chckpass() - one of the password checking functions.
First, I would like to be sure we are at the correct point:
Run my generic tracer:
gt64.exe -a:disp+work.exe bpf=.*!chckpass,args:3,unicode,rt:0
PID=2236|TID=2248|(0) disp+work.exe!chckpass (0x202c770, L"Brewered1 ", 0x41) (called from 0x1402f1060 (disp+work.exe!usrexist+0x3c0)) PID=2236|TID=2248|(0) disp+work.exe!chckpass -> 0x35
Call path is syssigni -> DyISigni -> dychkusr -> usrexist -> chckpass.
0x35 is error returning in chckpass() in that point:
.text:00000001402ED567 loc_1402ED567: ; CODE XREF: chckpass+B4j .text:00000001402ED567 mov rcx, rbx ; usr02 .text:00000001402ED56A call password_idle_check .text:00000001402ED56F cmp eax, 33h .text:00000001402ED572 jz loc_1402EDB4E .text:00000001402ED578 cmp eax, 36h .text:00000001402ED57B jz loc_1402EDB3D .text:00000001402ED581 xor edx, edx ; usr02_readonly .text:00000001402ED583 mov rcx, rbx ; usr02 .text:00000001402ED586 call password_attempt_limit_exceeded .text:00000001402ED58B test al, al .text:00000001402ED58D jz short loc_1402ED5A0 .text:00000001402ED58F mov eax, 35h .text:00000001402ED594 add rsp, 60h .text:00000001402ED598 pop r14 .text:00000001402ED59A pop r12 .text:00000001402ED59C pop rdi .text:00000001402ED59D pop rsi .text:00000001402ED59E pop rbx .text:00000001402ED59F retn
Fine, let's check:
gt64.exe -a:disp+work.exe bpf=.*!password_attempt_limit_exceeded,args:4,unicode,rt:0
PID=2744|TID=360|(0) disp+work.exe!password_attempt_limit_exceeded (0x202c770, 0, 0x257758, 0) (called from 0x1402ed58b (disp+work.exe!chckpass+0xeb)) PID=2744|TID=360|(0) disp+work.exe!password_attempt_limit_exceeded -> 1 PID=2744|TID=360|We modify return value (EAX/RAX) of this function to 0 PID=2744|TID=360|(0) disp+work.exe!password_attempt_limit_exceeded (0x202c770, 0, 0, 0) (called from 0x1402e9794 (disp+work.exe!chngpass+0xe4)) PID=2744|TID=360|(0) disp+work.exe!password_attempt_limit_exceeded -> 1 PID=2744|TID=360|We modify return value (EAX/RAX) of this function to 0
Voila! I can login now.
By the way, if I try to pretend I forgot the password, fixing chckpass() function return value at zero is enough to bypass check:
gt64.exe -a:disp+work.exe bpf=.*!chckpass,args:3,unicode,rt:0
PID=2744|TID=360|(0) disp+work.exe!chckpass (0x202c770, L"bogus ", 0x41) (called from 0x1402f1060 (disp+work.exe!usrexist+0x3c0)) PID=2744|TID=360|(0) disp+work.exe!chckpass -> 0x35 PID=2744|TID=360|We modify return value (EAX/RAX) of this function to 0
It is really great. Function names are very clear, much clearer than in Oracle RDBMS. There are some portions of disp+work process written in C++. It was probably rewritten some time ago to C/C++?
By the way, my generic tracer 0.4 contained error in PDB files loader, I fixed it and reuploaded it again. For experiments with SAP win64 you should get newest gt.
P.S. Need any reverse engineering / small modification work? -> dennis@conus.info

Recent comments
7 weeks 2 days ago
20 weeks 6 days ago
20 weeks 6 days ago
30 weeks 2 days ago
31 weeks 6 days ago
31 weeks 6 days ago
32 weeks 2 days ago
33 weeks 10 hours ago
33 weeks 2 days ago
33 weeks 2 days ago