Timer Patch Solution
Tạo vào: 18 tháng 1, 2025
Tạo vào: 18 tháng 1, 2025
Solve task with my assembly, how to patch it? easy way needed.
My_assembly : {
push edx
.text:0040B03E lea ecx, [ebp+var_28C]
.text:0040B044 push eax
.text:0040B045 lea edx, [ebp+var_27C]
.text:0040B04B push ecx
.text:0040B04C lea eax, [ebp+var_26C]
.text:0040B052 push edx
.text:0040B053 lea ecx, [ebp+var_25C]
.text:0040B059 push eax
.text:0040B05A lea edx, [ebp+var_24C]
.text:0040B060 push ecx
.text:0040B061 lea eax, [ebp+var_23C]
.text:0040B067 push edx
.text:0040B068 lea ecx, [ebp+var_22C]
.text:0040B06E push eax
.text:0040B06F lea edx, [ebp+var_21C]
.text:0040B075 push ecx
.text:0040B076 lea eax, [ebp+var_20C]
.text:0040B07C push edx
.text:0040B07D push eax
.text:0040B07E push 2Fh ; '/'
.text:0040B080 call ds:__vbaFreeVarList
.text:0040B086 mov eax, [ebp+arg_0]
.text:0040B089 add esp, 0C0h
.text:0040B08F mov ecx, [eax]
.text:0040B091 push eax
.text:0040B092 call dword ptr [ecx+708h]
.text:0040B098 mov eax, [ebp+arg_0]
.text:0040B09B push eax
.text:0040B09C mov edx, [eax]
.text:0040B09E call dword ptr [edx+708h]
.text:0040B0A4 mov eax, [ebp+arg_0]
.text:0040B0A7 push eax
.text:0040B0A8 mov ecx, [eax]
.text:0040B0AA call dword ptr [ecx+708h]
.text:0040B0B0 mov eax, [ebp+arg_0]
.text:0040B0B3 lea ecx, [ebp+var_1C]
.text:0040B0B6 push ecx
.text:0040B0B7 push eax
.text:0040B0B8 mov edx, [eax]
.text:0040B0BA call dword ptr [edx+50h]
.text:0040B0BD cmp eax, ebx
.text:0040B0BF fnclex
.text:0040B0C1 jge short loc_40B0D5
.text:0040B0C3 mov edx, [ebp+arg_0]
.text:0040B0C6 push 50h ; 'P'
.text:0040B0C8 push offset dword_40201C
.text:0040B0CD push edx
.text:0040B0CE push eax
.text:0040B0CF call ds:__vbaHresultCheckObj
.text:0040B0D5
.text:0040B0D5 loc_40B0D5: ; CODE XREF: sub_40AB60+561↑j
.text:0040B0D5 mov eax, [ebp+var_1C]
.text:0040B0D8 push 44h ; 'D'
.text:0040B0DA mov [ebp+var_2B4], eax
.text:0040B0E0 lea eax, [ebp+var_2C]
.text:0040B0E3 push eax
.text:0040B0E4 mov [ebp+var_1C], ebx
.text:0040B0E7 mov [ebp+var_2BC], 8008h
.text:0040B0F1 call esi ; rtcVarBstrFromAnsi
.text:0040B0F3 lea ecx, [ebp+var_3C]
.text:0040B0F6 push 65h ; 'e'
.text:0040B0F8 push ecx
.text:0040B0F9 call esi ; rtcVarBstrFromAnsi
.text:0040B0FB lea edx, [ebp+var_5C]
.text:0040B0FE push 66h ; 'f'
.text:0040B100 push edx
.text:0040B101 call esi ; rtcVarBstrFromAnsi
.text:0040B103 lea eax, [ebp+var_7C]
.text:0040B106 push 43h ; 'C'
.text:0040B108 push eax
.text:0040B109 call esi ; rtcVarBstrFromAnsi
.text:0040B10B lea ecx, [ebp+var_9C]
.text:0040B111 push 61h ; 'a'
.text:0040B113 push ecx
.text:0040B114 call esi ; rtcVarBstrFromAnsi
.text:0040B116 lea edx, [ebp+var_BC]
.text:0040B11C push 6Dh ; 'm'
.text:0040B11E push edx
.text:0040B11F call esi ; rtcVarBstrFromAnsi
.text:0040B121 lea eax, [ebp+var_DC]
.text:0040B127 push 70h ; 'p'
.text:0040B129 push eax
.text:0040B12A call esi ; rtcVarBstrFromAnsi
.text:0040B12C lea ecx, [ebp+var_FC]
.text:0040B132 push 20h ; ' '
.text:0040B134 push ecx
.text:0040B135 call esi ; rtcVarBstrFromAnsi
.text:0040B137 lea edx, [ebp+var_11C]
.text:0040B13D push 43h ; 'C'
.text:0040B13F push edx
.text:0040B140 call esi ; rtcVarBstrFromAnsi
.text:0040B142 lea eax, [ebp+var_13C]
.text:0040B148 push 54h ; 'T'
.text:0040B14A push eax
.text:0040B14B call esi ; rtcVarBstrFromAnsi
.text:0040B14D lea ecx, [ebp+var_15C]
.text:0040B153 push 46h ; 'F'
.text:0040B155 push ecx
.text:0040B156 call esi ; rtcVarBstrFromAnsi
.text:0040B158 lea edx, [ebp+var_17C]
.text:0040B15E push 20h ; ' '
.text:0040B160 push edx
.text:0040B161 call esi ; rtcVarBstrFromAnsi
.text:0040B163 lea eax, [ebp+var_19C]
.text:0040B169 push 43h ; 'C'
.text:0040B16B push eax
.text:0040B16C call esi ; rtcVarBstrFromAnsi
.text:0040B16E lea ecx, [ebp+var_1BC]
.text:0040B174 push 72h ; 'r'
.text:0040B176 push ecx
.text:0040B177 call esi ; rtcVarBstrFromAnsi
.text:0040B179 lea edx, [ebp+var_1DC]
.text:0040B17F push 61h ; 'a'
.text:0040B181 push edx
}
Solution : {
We start by loading program in VB Decompiler. We can extract some interesting
information from there:
.text:00408B6F push ecx ; arg4: v_4dc
.text:00408B70 lea ecx, [ebp-1Ch]
.text:00408B73 push edx ; arg3: "Ollydbg"
.text:00408B74 push ecx ; arg2: v_1c
.text:00408B75 push ebx ; arg1: 0040E3E8
.text:00408B76 call dword ptr [eax+704h] ; Proc_0_5_405BA0
.text:00408B7C cmp word ptr [ebp-4DCh], 0FFFFh
.text:00408B84 jnz short loc_408BB1 ; if v_4dc != -1 skip bad code
Function Proc_0_5_405BA0 is called with argument "Ollydbg". If returns -1, then program
terminates. Proc_0_5_405BA0 is called many times with the following arguments:
1.Ollydbg
2.PEiD
3.Windows
4.IDA
5.Hex
6.CPU
Proc_0_5_405BA0 may looks for window names and return -1 when detects a window with
such a name. I am using IDA and function returns -1 when IDA argument is called. A
fast analysis of this function shown that is searching all windows for window_text
that contains the specified string. All we have to do is to set a breakpoint at:
.text:00408B84 jnz short loc_408BB1 ; if you use Olly
.text:0040977A jz loc_4090F6 ; if you use IDA
and toggle ZF flag to cancel branch.
// ----------------------------------------------------------------------------------- //
Then, form appears to screen, but it closes again. We can blame timer handler for
this. When Proc_0_6_405D60 is called, then form closes:
.text:0040B092 call dword ptr [ecx+708h] ; Proc_0_6_405D60
Inside Proc_0_6_405D60, we have the same protection as in Form_Load(). We can bypass
it as before, but after 3 seconds, we have to bypass it again, and again... This is
very annoying. However, there are (at least) 2 solutions for this:
1. Patch jz command with jnz command. (easy)
2. Tamper timer's interval. (difficult)
The first solution is quite simple, so I'll show the second. Let's get into Visual
Basic Internals. Set a breakpoint at EntryPoint:
.text:004014C4 public start
.text:004014C4 start:
.text:004014C4 push offset dword_401880 ; RT_MainStruct
.text:004014C9 call ThunRTMain
This is the main struct of the program, and contains lots of information about program:
00401880: 56 42 35 21 --> VB5! ; signature
00401884: F0 1F 2A 00 --> dd 0x002a1ff0
........
004018cc: 30 18 40 00 --> offset 0x00401830 -> DialogsStruct
........
Follow DialogsStruct:
00401830: 50 00 00 00 --> sizeof struct
00401834: 7C 6C A5 22 -->
........
00401878: 14 15 40 00 --> offset 0x00401514 -> MainDialog
........
Follow MainDialog. This struct contains all information about form and objects of
the program. It's easy to determine the declaration of Timer object, because we
know it's name (Timer1):
00401714 FF 01 1F 00 00 00 01 06 00 54 69 6D 65 72 31 00 ........Timer1.
00401724 0B 03 B8 0B 00 00 07 C8 0A 00 00 08 D0 02 00 00 ......L.......
The 2 bytes at 00401726 (0B B0) is the time interval (0x0BB0 = 3000). One solution
is to set the interval to very big value (0x7BB8 = 31 sec). But the problem is not
solved. You can also find where the Enabled property is located and set it to false.
But we'll implement something different: We'll set the interval at an illegal value
(0x0000). Thus timer handler will fail, and will never execute.
}
Below is a concise, practical explanation of how to disable the troublesome timer (or the check) with a simple patch, so that the form does not close repeatedly every few seconds. I’ll outline two common methods:
Looking at the snippet around:
text.text:0040B0BD cmp eax, ebx .text:0040B0BF fnclex .text:0040B0C1 jge short loc_40B0D5 .text:0040B0C3 mov edx, [ebp+arg_0] .text:0040B0C6 push 50h ; 'P' .text:0040B0C8 push offset dword_40201C .text:0040B0CD push edx .text:0040B0CE push eax .text:0040B0CF call ds:__vbaHresultCheckObj .text:0040B0D5 ; ----------------------------------------------------------------- loc_40B0D5:
The cmp eax, ebx
/jge short loc_40B0D5
sequence checks some condition that, if failing, calls __vbaHresultCheckObj
(which in many VB6 protections can terminate or glitch the program).
If you want to always skip that termination call, you can simply replace jge short loc_40B0D5
(opcodes typically 7D 12
in hex) with an unconditional jump (EB 12
).
Concretely, if you are using a hex editor, search for the bytes 7D 12
at that address and change the 7D
to EB
. In assembly, it changes from:
jge short loc_40B0D5
to
jmp short loc_40B0D5
This way, it never executes the block at 0x40B0C3
and never calls __vbaHresultCheckObj
.
This is often the quickest fix for these repeated calls that forcibly close the form.
As you found, the form uses a standard VB Timer object (Timer1
) with an Interval property of 3000ms (0x0BB8). When that timer expires, it calls into the same anti-debug routine. Disabling (or sabotaging) the timer means that code never fires in the background.
From your research, you see in the VB form structure (often in the data area of the compiled VB program) something like:
text00401714 ... "Timer1" 00401724 0B 03 B8 0B 00 00 07 C8 ... ^^^^ Timer Interval
Depending on the exact layout, you’ll see B8 0B
which is 0x0BB8 in little-endian (3000 decimal).
B8 0B
with 00 00
. This yields an Interval of 0, which VB considers invalid, and effectively the timer never fires.FF 7F
(0x7FFF ~ 32767 ms), or any large number. But usually 0 is enough to kill it outright.Either approach stops the repeated calls.
jge
→ jmp
, and done.Timer1.Interval
. You just open a hex editor at the correct offset, change two bytes to 00 00
, and you’re finished.Both approaches work. You only need to apply one of them to keep the form from closing itself every few seconds.
cmp eax, ebx / jge short loc_40B0D5
, change jge
into an unconditional jmp
(hex 7D
→ EB
).0x0BB8
(3000) to 0x0000
. This disables the timer.Either patch should stop the timer-based anti-debug checks from closing the form.