* _______ _______ _______ _______ _______ _______ ______ _______ _______ ______ _______ * | _ | _ | _ | | | _ | || _ | _ | _ | | _ | * | |_| | |_| | |_| | _ | | |_| | _ | |_| | |_| | | || | |_| | * | | | | |_| | | | | | | | | |_||_| | * | | _ || | ___| _| | |_| | | _ || __ | | * | _ | |_| | _ | | | |_| _ | | _ | |_| | | | | _ | * |__| |__|_______|__| |__|___| |_______|__| |__|______||__| |__|_______|___| |_|__| |__| * www.abapcadabra.com *------------------------------------------------------------------------------------------- * _____ _____ ______ _____ _ _____ _____ _ __ _____ ______ * / __ \| _ || _ \| ___| | | | _ |/ __ \| | / /| ___|| ___ \ * | / \/| | | || | | || |__ | | | | | || / \/| |/ / | |__ | |_/ / * | | | | | || | | || __| | | | | | || | | \ | __| | / * | \__/\\ \_/ /| |/ / | |___ | |____\ \_/ /| \__/\| |\ \| |___ | |\ \ * \____/ \___/ |___/ \____/ \_____/ \___/ \____/\_| \_/\____/ \_| \_| *------------------------------------------------------------------------------------------- * program : ZABAPCADABRA_CODE_LOCKER * title : Abap - code locker * functional area : Development tool * environment : 4.7 * program Function : Utility tool to protect installed software. The report will remove * abap coding logic from all METHODS in a report or include, and * store it in a scrambled and password protected storage in SAP. * Documentation : Search for "Code locker" on AbapcadabrA.com * Previous version : This is the initial version * Developer name : Wim Maasdam * Development date : 05/10/2016 * Version : 0.1 *------------------------------------------------------------------------------------------- * _ _ _ ___ ______ _ _ _____ _ _ _____ _ * /\| |/\ | | | | / _ \ | ___ \| \ | ||_ _|| \ | || __ \ /\| |/\ * \ ` ' / | | | |/ /_\ \| |_/ /| \| | | | | \| || | \/ \ ` ' / * |_ _| | |/\| || _ || / | . ` | | | | . ` || | __ |_ _| * / , . \ \ /\ /| | | || |\ \ | |\ | _| |_ | |\ || |_\ \ / , . \ * \/|_|\/ \/ \/ \_| |_/\_| \_|\_| \_/ \___/ \_| \_/ \____/ \/|_|\/ * .--. .--. * (O)(O) This report will overwrite the content of other reports and should NEVER (O)(O) * | o / be used in test or production systems. Before using this report, make sure | o / * |`-/ it is tested on your system. The report will remove (and rebuild) coding in |`-/ * |_/ METHOD - ENDMETHOD blocks and when the password is forgotten it will not be |_/ * _ easy to reverse the process. The coding that is protected is scrambled _ * (_) (encrypted) before it is stored with an encryption key for each method. (_) * * YOU SHOULD HAVE A BACKUP OF YOUR CODING WHEN USING THIS COPYRIGHT MECHANISM * IT MAY WORK BETTER THAN EXPECTED (LOST PASSWORD) OR IT MAY BE DAMAGED (CODING * CHANGE THAT AFFECTS THE ENCRYPTION/DECRYPTION MECHANISM). * *------------------------------------------------------------------------------------------- * Change list: * Date Description * 05/10/2016 Initial release *--------------------------------------------------------------------- REPORT ZABAPCADABRA_CODE_LOCKER. data: gv_encoding_requests type boolean. CLASS lcl_code_locker DEFINITION. PUBLIC SECTION. TYPES: begin of ty_snippet, class type string, method type string, source type STANDARD TABLE OF char255 WITH DEFAULT KEY, start type TOKEN_ROW, finish type TOKEN_ROW, end of ty_snippet. DATA: gv_LEGAL_CHARS type c length 76 value 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789[]()<>!?:.,;-_', gt_WARNING_comments type standard table of char255 ##NEEDED, gv_TRANSLATION_str type char255, gv_password type char20, gv_password2 type char20, gv_decode_requested type boolean, gt_source type standard table of char255, gt_snippets type standard table of ty_snippet. METHODS: constructor, get_password importing title type any returning value(password) type char20, strip_or_build importing source type sy-repid, strip_coding importing source type sy-repid, build_coding importing source type sy-repid, save_and_store importing source type sy-repid, set_translation_str importing seed type any object type any. CLASS-METHODS: f4_source importing parsource type any, is_production_system returning value(is_production) type boolean.. ENDCLASS. CLASS lcl_code_locker IMPLEMENTATION. method constructor. define add_line. append &1 to gt_WARNING_comments. end-of-definition. * Compose the *WARNING* header: clear: gt_WARNING_comments[]. add_line: '* _ _ _ ___ ______ _ _ _____ _ _ _____ _ ', '* /\| |/\ | | | | / _ \ | ___ \| \ | ||_ _|| \ | || __ \ /\| |/\ ', '* \ ` '' / | | | |/ /_\ \| |_/ /| \| | | | | \| || | \/ \ ` '' / ', '* |_ _| | |/\| || _ || / | . ` | | | | . ` || | __ |_ _| ', '* / , . \ \ /\ /| | | || |\ \ | |\ | _| |_ | |\ || |_\ \ / , . \ ', '* \/|_|\/ \/ \/ \_| |_/\_| \_|\_| \_/ \___/ \_| \_/ \____/ \/|_|\/ ', '* _ _____ _____ _ __ _____ ______ _____ _____ ______ _____ ', '* | | | _ |/ __ \| | / /| ___|| _ \ / __ \| _ || _ \| ___| ', '* | | | | | || / \/| |/ / | |__ | | | | | / \/| | | || | | || |__ ', '* | | | | | || | | \ | __| | | | | | | | | | || | | || __| ', '* | |____\ \_/ /| \__/\| |\ \| |___ | |/ / | \__/\\ \_/ /| |/ / | |___ ', '* \_____/ \___/ \____/\_| \_/\____/ |___/ \____/ \___/ |___/ \____/ ', '* ', '* The content of all methods in this coding has been removed for copyright ', '* purpose. Use report ZABAPCADABRA_CODE_LOCKER to reinstate coding. '. endmethod. method get_password. data: lt_fields type STANDARD TABLE OF SVAL, lw_field type SVAL, lv_returncode type c length 1. * Request the password, for direct check and scramble seed.. clear lt_fields[]. lw_field-tabname = 'PA0367'. lw_field-fieldname = 'ANGT0'. lw_field-FIELDTEXT = 'Access code'. lw_field-NOVALUEHLP = abap_true. append lw_field to lt_fields. CALL FUNCTION 'POPUP_GET_VALUES' EXPORTING popup_title = title IMPORTING returncode = lv_returncode tables fields = lt_fields. if lv_returncode = 'A'. message 'Operation cancelled' type 'W'. leave program. endif. read table lt_fields index 1 into lw_field. * Scramble the password CALL FUNCTION 'SCRAMBLE_STRING' EXPORTING source = lw_field-value KEY = 19732835 IMPORTING TARGET = password. endmethod. method strip_or_build. data: lv_password type char20, lv_name type TRDIR-NAME, lv_devclass type tadir-devclass, lv_state type PROGDIR-state. if source is initial or source = sy-repid. exit. endif. select single devclass from tadir into lv_devclass where pgmid = 'R3TR' and object = 'PROG' and obj_name = source. if sy-subrc <> 0 or not ( lv_devclass(1) co '$YZ' ). exit. endif. select single state from PROGDIR into lv_state where name = source and state = 'I'. if sy-subrc = 0. exit. endif. * The abap editor lock: lv_name = source. CALL FUNCTION 'ENQUEUE_ESRDIRE' EXPORTING MODE_TRDIR = 'E' NAME = lv_name EXCEPTIONS OTHERS = 4. IF sy-subrc <> 0. message 'Editor lock' type 'S'. exit. ENDIF. * Read the password from the file: import password = lv_password snippets = gt_snippets from database HLPINDX(ZH) id source. if sy-subrc = 0. if lv_password <> gv_password. message 'Password failure' type 'S'. leave PROGRAM. endif. gv_decode_requested = abap_true. build_coding( source ). else. gv_decode_requested = abap_false. strip_coding( source ). endif. endmethod. method strip_coding. data: lt_tokens type standard table of STOKES, lw_token type STOKES, lt_statements type standard table of SSTMNT, lv_marker type c length 1, lw_snippet type ty_snippet, lv_sourceline type char255, lv_classname type string. clear gt_source[]. READ REPORT source INTO gt_source. if sy-subrc <> 0. exit. endif. if is_production_system( ) = abap_true. exit. endif. * Coding in METHODS is stripped off (removed) from the source SCAN ABAP-SOURCE gt_source TOKENS INTO lt_tokens STATEMENTS INTO lt_statements. clear: lv_marker. loop at lt_tokens into lw_token. case lw_token-str. when 'TYPE' or 'CALL'. lv_marker = 'X'. delete lt_tokens. continue. when 'METHOD'. if lv_marker <> 'X'. lv_marker = 'M'. delete lt_tokens. continue. else. clear lv_marker. endif. when 'ENDMETHOD'. lw_token-type = 'E'. modify lt_tokens from lw_token TRANSPORTING type. continue. when 'CLASS'. lv_marker = 'C'. delete lt_tokens. continue. endcase. if lv_marker = abap_false. delete lt_tokens. else. lw_token-type = lv_marker. modify lt_tokens from lw_token TRANSPORTING type. endif. clear lv_marker. endloop. * Cut up the coding, create a snippetlist: clear gt_snippets[]. loop at lt_tokens into lw_token. case lw_token-type. when 'C'. lv_classname = lw_token-str. when 'M'. clear: lw_snippet, lw_snippet-source[]. lw_snippet-class = lv_classname. lw_snippet-method = lw_token-str. lw_snippet-start = lw_token-ROW + 1. when 'E'. lw_snippet-finish = lw_token-ROW - 1. set_translation_str( exporting seed = gv_password object = lw_snippet-method ). loop at gt_source from lw_snippet-start to lw_snippet-finish into lv_sourceline. * Scramble the sourcecode translate lv_sourceline USING gv_TRANSLATION_str. append lv_sourceline to lw_snippet-source. endloop. append lw_snippet to gt_snippets. endcase. endloop. * Now remove the snippets from the original source code: sort gt_snippets by start DESCENDING. loop at gt_snippets into lw_snippet. delete gt_source from lw_snippet-start to lw_snippet-finish. lv_sourceline = ' MESSAGE ''Code locked - see comment header for more info'' TYPE ''S''.'. insert lv_sourceline into gt_source INDEX lw_snippet-start. endloop. * Finally: set the report "Warning - locked code" comments in the report * comment heading. insert lines of gt_WARNING_comments into gt_source index 1. save_and_store( source ). endmethod. method build_coding. data: lt_tokens type standard table of STOKES, lw_token type STOKES, lt_statements type standard table of SSTMNT, lv_marker type c length 1, lw_snippet type ty_snippet, lv_sourceline type char255, lv_sourceline2 type char255, lv_tabix type sy-tabix, lv_classname type string. clear gt_source[]. READ REPORT source INTO gt_source. if sy-subrc <> 0. exit. endif. if is_production_system( ) = abap_true. exit. endif. * Coding in METHODS is re-build in the source SCAN ABAP-SOURCE gt_source TOKENS INTO lt_tokens STATEMENTS INTO lt_statements. clear: lv_marker. loop at lt_tokens into lw_token. case lw_token-str. when 'TYPE' or 'CALL'. lv_marker = 'X'. delete lt_tokens. continue. when 'METHOD'. if lv_marker <> 'X'. lv_marker = 'M'. delete lt_tokens. continue. else. clear lv_marker. endif. when 'ENDMETHOD'. lw_token-type = 'E'. modify lt_tokens from lw_token TRANSPORTING type. continue. when 'CLASS'. lv_marker = 'C'. delete lt_tokens. continue. endcase. if lv_marker = abap_false. delete lt_tokens. else. lw_token-type = lv_marker. modify lt_tokens from lw_token TRANSPORTING type. endif. clear lv_marker. endloop. clear lv_tabix. loop at lt_tokens into lw_token. case lw_token-type. when 'C'. lv_classname = lw_token-str. when 'M'. read table gt_snippets into lw_snippet with key class = lv_classname method = lw_token-str. if sy-subrc = 0. lv_tabix = sy-tabix. lw_snippet-start = lw_token-ROW + 1. modify gt_snippets from lw_snippet INDEX lv_tabix TRANSPORTING start. else. clear lv_tabix. endif. when 'E'. lw_snippet-finish = lw_token-ROW - 1. if not lv_tabix is initial. modify gt_snippets from lw_snippet INDEX lv_tabix TRANSPORTING finish. endif. endcase. endloop. loop at gt_snippets into lw_snippet. * Start rebuilding, unscramble the source code first: set_translation_str( exporting seed = gv_password object = lw_snippet-method ). loop at lw_snippet-source into lv_sourceline. translate lv_sourceline USING gv_TRANSLATION_str. modify lw_snippet-source from lv_sourceline. endloop. if lw_snippet-start < lw_snippet-finish. loop at gt_source from lw_snippet-start to lw_snippet-finish into lv_sourceline. if lv_sourceline(1) <> '*'. concatenate '*' lv_sourceline into lv_sourceline SEPARATED BY space. modify gt_source from lv_sourceline. endif. endloop. endif. insert lines of lw_snippet-source into gt_source INDEX lw_snippet-start. endloop. clear lv_tabix. loop at gt_source from 1 to 15 into lv_sourceline. add 1 to lv_tabix. read table gt_WARNING_comments index lv_tabix into lv_sourceline2. if lv_sourceline = lv_sourceline2. delete gt_source. endif. endloop. delete gt_source where table_line = ' MESSAGE ''Code locked - see comment header for more info'' TYPE ''S''.'. save_and_store( source ). endmethod. method save_and_store. TYPE-POOLS: synt. data: lv_info type char255, lw_dir TYPE trdir, lt_warnings TYPE synt_errors, lv_error_message TYPE string, "#EC NEEDED lv_error_line TYPE i, "#EC NEEDED lv_error_line_c type c length 6, lv_error_word TYPE string. "#EC NEEDED * Whether it's for deactivation or activation, the coding that is locked or unlocked * should have a healthy state, thus it should pass the Abap syntax checker. If it doesn't * the process is abandoned. SYNTAX-CHECK FOR gt_SOURCE MESSAGE lv_error_message LINE lv_error_line WORD lv_error_word PROGRAM source ID 'MSG' TABLE lt_warnings. if not lv_error_message is initial. message lv_error_message type 'I'. message 'Action abandoned' type 'S'. exit. endif. if gv_decode_requested = abap_false. write: sy-datum DD/MM/YYYY to lv_info. write: sy-uzeit using EDIT MASK '__:__:__' to lv_info+11. concatenate lv_info sy-uname into lv_info separated by space. * The request is to ENCODE, thus snippets are stored and the report is * overwritten with a stripped version. *-------------------------------------------------------------------- export password = gv_password snippets = gt_snippets info = lv_info to database HLPINDX(zh) id source. if sy-subrc = 0. *-------------------------------------------------------------------- INSERT REPORT source FROM gt_SOURCE. *-------------------------------------------------------------------- endif. else. *-------------------------------------------------------------------- INSERT REPORT source FROM gt_SOURCE. if sy-subrc = 0. *-------------------------------------------------------------------- delete from database HLPINDX(zh) id source. *-------------------------------------------------------------------- endif. endif. endmethod. method set_translation_str. data: lv_seed type c length 40, lv_position type sy-index, begin of lw_randomizer, hash type c length 76, strlen type sy-fdpos, length type sy-fdpos, mod type sy-fdpos, end of lw_randomizer, lv_translation type c length 76. FIELD-SYMBOLS type c. * Transform SEED into a number, from 1 to 999: concatenate seed object into lv_seed. translate lv_seed TO UPPER CASE. translate lv_seed USING 'A3B4C2D3E4F5G6H7I8J9K5L6M2N3O4P5Q6R7S8T9U7V8W2X3Y4Z5~6@7&8^9#9!2?2%3=4(5)6_7-8+9:3;2|3\4/5>60718'. clear lw_randomizer. lw_randomizer-strlen = strlen( lv_seed ) - 1. lw_randomizer-hash = lv_seed. lv_translation = gv_LEGAL_CHARS. do lw_randomizer-strlen times. move lw_randomizer-hash(1) to lw_randomizer-length. assign lv_translation(lw_randomizer-length) to . lw_randomizer-mod = lw_randomizer-length mod 3. case lw_randomizer-mod. when 0. shift right by 5 places CIRCULAR. when 1. shift left by 3 places CIRCULAR. when 2. shift left by 1 places CIRCULAR. endcase. shift lv_translation right by 1 places circular. shift lw_randomizer-hash left by 1 places. enddo. clear: gv_TRANSLATION_str. lw_randomizer-strlen = strlen( lv_translation ). do lw_randomizer-strlen times. lv_position = sy-index - 1. if gv_decode_requested = abap_false. concatenate gv_TRANSLATION_str gv_LEGAL_CHARS+lv_position(1) lv_translation+lv_position(1) into gv_TRANSLATION_str. else. concatenate gv_TRANSLATION_str lv_translation+lv_position(1) gv_LEGAL_CHARS+lv_position(1) into gv_TRANSLATION_str. endif. enddo. endmethod. method f4_source. TYPES: BEGIN OF lty_columns, source type tadir-OBJ_NAME, info type TKV02-awstx, END OF lty_columns. DATA: lt_columns TYPE STANDARD TABLE OF lty_columns, lw_column type lty_columns, lv_dynprofield type HELP_INFO-DYNPROFLD, lt_sources type standard table of hlpindx-srtfd, lv_info type char255. clear: lt_columns[]. select srtfd from hlpindx into table lt_sources where relid = 'ZH' and srtf2 = 0. * Set selection values / note this is just for example purpose: loop at lt_sources into lw_column-source. import info = lv_info from database HLPINDX(zh) id lw_column-source. lw_column-info = lv_info. APPEND lw_column to lt_columns. endloop. lv_dynprofield = parsource. CALL FUNCTION 'F4IF_INT_TABLE_VALUE_REQUEST' EXPORTING retfield = 'SOURCE' dynpprog = SY-REPID dynpnr = '1000' dynprofield = lv_dynprofield value_org = 'S' TABLES value_tab = lt_columns. endmethod. method is_production_system. data: lv_cccategory type t000-cccategory. * Get system category select single cccategory from t000 into lv_cccategory where mandt = sy-mandt. if sy-subrc = 0 and lv_cccategory = 'P'. is_production = abap_true. else. clear is_production. endif. endmethod. ENDCLASS. SELECTION-SCREEN: BEGIN OF LINE, COMMENT 1(15) lbl_l01 FOR FIELD pa_sta01. parameters: pa_sta01 type icon_d VISIBLE LENGTH 2 modif ID ico. parameters: pa_src01 type sy-repid OBLIGATORY VISIBLE LENGTH 30. parameters: pa_inf01 type c length 30 modif ID inf. define parameter_line. SELECTION-SCREEN: END OF LINE, BEGIN OF LINE, position 17. parameters: pa_sta&1 type icon_d VISIBLE LENGTH 2 modif ID ico . parameters: pa_src&1 type sy-repid VISIBLE LENGTH 30. parameters: pa_inf&1 type c length 30 modif ID inf. end-of-definition. parameter_line: 02, 03, 04, 05, 06, 07, 08, 09, 10. SELECTION-SCREEN: END OF LINE, SKIP, BEGIN OF LINE, position 17. parameters: pa_sta11 type icon_d VISIBLE LENGTH 2 modif ID ico . parameters: pa_src11 type sy-repid VISIBLE LENGTH 30. parameters: pa_inf11 type c length 30 modif ID inf. parameter_line: 12, 13, 14, 15, 16, 17, 18, 19, 20. SELECTION-SCREEN: END OF LINE. define f4_line. AT SELECTION-SCREEN ON VALUE-REQUEST FOR pa_src&1. lcl_code_locker=>f4_source( EXPORTING parsource = 'PA_SRC&1' ). end-of-definition. f4_line: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20. AT SELECTION-SCREEN OUTPUT. clear gv_encoding_requests. loop at screen. if screen-group1 = 'ICO'. screen-input = 0. modify screen. endif. if screen-group1 = 'INF'. screen-input = 0. screen-output = 1. * screen-display_3d = 0. modify screen. endif. endloop. * Check all entries: data: lv_srtfd type hlpindx-srtfd, lv_devclass type tadir-devclass, lv_state type progdir-state. define set_icon. clear: pa_inf&1. if pa_src&1 is initial. pa_sta&1 = '@P7@'. "No source else. if pa_src&1 = sy-repid. pa_sta&1 = '@03@'. pa_inf&1 = 'Source is for current report (suicide)'. else. select single srtfd from hlpindx into lv_srtfd where relid = 'ZH' and srtfd = pa_src&1 and srtf2 = 0. if sy-subrc = 0. pa_sta&1 = '@06@'. "Source is already in the vault else. select single devclass from tadir into lv_devclass where pgmid = 'R3TR' and object = 'PROG' and obj_name = pa_src&1. if sy-subrc = 0. if lv_devclass(1) co '$YZ'. pa_sta&1 = '@07@'. gv_encoding_requests = abap_true. * The coding could be inactive... select single state from PROGDIR into lv_state where name = pa_src&1 and state = 'I'. if sy-subrc = 0. pa_sta&1 = '@8I@'. pa_inf&1 = 'Source is inactive'. endif. else. pa_sta&1 = '@OJ@'. pa_inf&1 = 'Not a custom coding object'. endif. else. pa_sta&1 = '@02@'. "Devclass for source could not be determined pa_inf&1 = 'Invalid report (not found)'. endif. endif. endif. endif. end-of-definition. set_icon: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20. define set_info. if pa_sta&1 = '@06@'. "On the vault import info = pa_inf&1 from database HLPINDX(zh) id pa_src&1. endif. end-of-definition. set_info: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20. if LCL_CODE_LOCKER=>is_production_system( ) = abap_true. loop at screen. screen-input = 0. modify screen. endloop. message 'Code locker is NOT for production systems' type 'S'. endif. initialization. lbl_l01 = 'Abap sources'. start-of-selection. data: go_safe type ref to lcl_code_locker. create object go_safe. go_safe->gv_password = go_safe->get_password( 'Enter access code' ). if gv_encoding_requests = abap_true. go_safe->gv_password2 = go_safe->get_password( 'Re-enter access code' ). if go_safe->gv_password <> go_safe->gv_password2. message 'Passwords do not match' type 'S'. leave program. endif. endif. define process_object. go_safe->strip_or_build( pa_src&1 ). end-of-definition. process_object: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20.