DropDMG is a tool for creating DMG
archives from folders or files by conveniently dropping them over the application. The copy protection is hidden in the DropDMG.app/Frameworks/MJTApplication.framework/Versions/Current/A/MJTApplication
binary file and consists of several elements that show up in trial mode. The procedure below removes all the trial-related prompts and expiration checks, as well as the trial nag button.
The first step is to disable the nag screen that pops-up on every run. This can be done by rewiring the showDemoReminderIfNecessary
function and jumping to the end of the function. This skips most of the showWindow
code that is responsible for popping up the trial window.
methImpl_MJTApplicationController_showDemoReminderIfNecessary: 00000000c1003aff 55 push rbp 00000000c1003b00 488D3509AC0600 lea rsi, qword [ds:objc_msg_isValid_c106e710] ; @selector(isValid) 00000000c1003b07 4889E5 mov rbp, rsp 00000000c1003b0a 53 push rbx 00000000c1003b0b 4889FB mov rbx, rdi 00000000c1003b0e 4883EC08 sub rsp, 0x8 00000000c1003b12 488B0547A40800 mov rax, qword [ds:_OBJC_IVAR_$_MJTApplicationController._license] 00000000c1003b19 488B3C07 mov rdi, qword [ds:rdi+rax] 00000000c1003b1d FF15EDAB0600 call qword [ds:objc_msg_isValid_c106e710] ; @selector(isValid) 00000000c1003b23 84C0 test al, al 00000000c1003b25 E91D000000 jmp 0xC1003B47 ... 00000000c1003b47 4889DF mov rdi, rbx ; XREF=0xc1003b25 00000000c1003b4a 488D357FAB0600 lea rsi, qword [ds:objc_msg_mjtDoOnceAfter__c106e6d0] ; @selector(mjtDoOnceAfter:) 00000000c1003b51 F20F1005EFF60400 movsd xmm0, qword [ds:0xC1053248] 00000000c1003b59 FF1571AB0600 call qword [ds:objc_msg_mjtDoOnceAfter__c106e6d0] ; @selector(mjtDoOnceAfter:) 00000000c1003b5f 4C8B1D3AAB0600 mov r11, qword [ds:objc_msg_showDemoReminderIfNecessary] ; @selector(showDemoReminderIfNecessary) 00000000c1003b66 4889C7 mov rdi, rax 00000000c1003b69 4883C408 add rsp, 0x8 00000000c1003b6d 488D352CAB0600 lea rsi, qword [ds:objc_msg_showDemoReminderIfNecessary] ; @selector(showDemoReminderIfNecessary) 00000000c1003b74 5B pop rbx 00000000c1003b75 C9 leave 00000000c1003b76 41FFE3 jmp r11
The trial button can be removed by rewiring the jump at 0xc1023e3d
and pretending that the application is licensed.
methImpl_MJTWindowController_addTrialButton: 00000000c1023e06 55 push rbp 00000000c1023e07 488B050A420400 mov rax, qword [ds:imp___nl_symbol_ptr__NSApp] 00000000c1023e0e 488D359B120500 lea rsi, qword [ds:objc_msg_delegate_c10750b0] ; @selector(delegate) 00000000c1023e15 4889E5 mov rbp, rsp 00000000c1023e18 4154 push r12 00000000c1023e1a 4989FC mov r12, rdi 00000000c1023e1d 53 push rbx 00000000c1023e1e 488B38 mov rdi, qword [ds:rax] 00000000c1023e21 FF1589120500 call qword [ds:objc_msg_delegate_c10750b0] ; @selector(delegate) 00000000c1023e27 488D3572120500 lea rsi, qword [ds:objc_msg_license_c10750a0] ; @selector(license) 00000000c1023e2e 4889C7 mov rdi, rax 00000000c1023e31 FF1569120500 call qword [ds:objc_msg_license_c10750a0] ; @selector(license) 00000000c1023e37 4885C0 test rax, rax 00000000c1023e3a 4889C3 mov rbx, rax 00000000c1023e3d E952000000 jmp 0xC1023E94 ... 00000000c1023e94 5B pop rbx ; XREF=0xc1023e3d 00000000c1023e95 415C pop r12 00000000c1023e97 C9 leave 00000000c1023e98 C3 ret
The expiration can be circumvented by never disabling the application. The interesting function shouldDisable
contains several checks that lead up to:
mov eax, 0x1
the contents of the eax
register, set to 0x1
being returned in case the application should disable itself.
The procedure is as follows:
methImpl_MJTApplicationController_shouldDisable: 00000000c1003a86 55 push rbp 00000000c1003a87 488D3592AC0600 lea rsi, qword [ds:objc_msg_isExpired] ; @selector(isExpired) 00000000c1003a8e 4889E5 mov rbp, rsp 00000000c1003a91 53 push rbx 00000000c1003a92 4889FB mov rbx, rdi 00000000c1003a95 4883EC08 sub rsp, 0x8 00000000c1003a99 488B05C0A40800 mov rax, qword [ds:_OBJC_IVAR_$_MJTApplicationController._license] 00000000c1003aa0 488B3C07 mov rdi, qword [ds:rdi+rax] 00000000c1003aa4 FF1576AC0600 call qword [ds:objc_msg_isExpired] ; @selector(isExpired) 00000000c1003aaa 84C0 test al, al 00000000c1003aac 7541 jne 0xC1003AEF 00000000c1003aae 488B05ABA40800 mov rax, qword [ds:_OBJC_IVAR_$_MJTApplicationController._license] 00000000c1003ab5 488D3554AC0600 lea rsi, qword [ds:objc_msg_isValid_c106e710] ; @selector(isValid) 00000000c1003abc 488B3C03 mov rdi, qword [ds:rbx+rax] 00000000c1003ac0 FF154AAC0600 call qword [ds:objc_msg_isValid_c106e710] ; @selector(isValid) 00000000c1003ac6 84C0 test al, al 00000000c1003ac8 752C jne 0xC1003AF6 00000000c1003aca 488D352FAC0600 lea rsi, qword [ds:objc_msg_licenseController] ; @selector(licenseController) 00000000c1003ad1 4889DF mov rdi, rbx 00000000c1003ad4 FF1526AC0600 call qword [ds:objc_msg_licenseController] ; @selector(licenseController) 00000000c1003ada 488D35FFAD0600 lea rsi, qword [ds:objc_msg_window_c106e8e0] ; @selector(window) 00000000c1003ae1 4889C7 mov rdi, rax 00000000c1003ae4 FF15F6AD0600 call qword [ds:objc_msg_window_c106e8e0] ; @selector(window) 00000000c1003aea 4885C0 test rax, rax 00000000c1003aed 7507 jne 0xC1003AF6 00000000c1003aef B801000000 mov eax, 0x1 ; XREF=0xc1003aac 00000000c1003af4 EB02 jmp 0xC1003AF8 00000000c1003af6 31C0 xor eax, eax ; XREF=0xc1003ac8, 0xc1003aed 00000000c1003af8 4883C408 add rsp, 0x8 ; XREF=0xc1003af4 00000000c1003afc 5B pop rbx 00000000c1003afd C9 leave 00000000c1003afe C3 ret
Since we want none of that, we can stub the function by jumping to the end:
methImpl_MJTApplicationController_shouldDisable: 00000000c1003a86 55 push rbp 00000000c1003a87 488D3592AC0600 lea rsi, qword [ds:objc_msg_isExpired] ; @selector(isExpired) 00000000c1003a8e 4889E5 mov rbp, rsp 00000000c1003a91 53 push rbx 00000000c1003a92 4889FB mov rbx, rdi 00000000c1003a95 4883EC08 sub rsp, 0x8 00000000c1003a99 E95A000000 jmp 0xC1003AF8 ... 00000000c1003af8 4883C408 add rsp, 0x8 ; XREF=0xc1003af4, 0xc1003a99 00000000c1003afc 5B pop rbx 00000000c1003afd C9 leave 00000000c1003afe C3 ret
In order to disable expiration, we jump over:
mov eax, 0x1
in the isExpired
method, thereby not returning true
. This effectively turns the trial into a never-ending and fully-functional trial.
methImpl_MJTLicense_isExpired: 00000000c10347a8 55 push rbp 00000000c10347a9 488D35A0430400 lea rsi, qword [ds:objc_msg_isValid_c1078b50] ; @selector(isValid) 00000000c10347b0 4889E5 mov rbp, rsp 00000000c10347b3 4154 push r12 00000000c10347b5 53 push rbx 00000000c10347b6 4883EC30 sub rsp, 0x30 00000000c10347ba FF1590430400 call qword [ds:objc_msg_isValid_c1078b50] ; @selector(isValid) 00000000c10347c0 84C0 test al, al 00000000c10347c2 E98D010000 jmp 0xC1034954 ... 00000000c1034954 31C0 xor eax, eax ; XREF=0xc10347c2, 0xc103494d 00000000c1034956 EB05 jmp 0xC103495D 00000000c1034958 B801000000 mov eax, 0x1 ; XREF=0xc1034945, 0xc1034952 00000000c103495d 4883C430 add rsp, 0x30 ; XREF=0xc1034956 00000000c1034961 5B pop rbx 00000000c1034962 415C pop r12 00000000c1034964 C9 leave 00000000c1034965 C3 ret
That is it for version 3.1.3.