如何绕过这种比较?

逆向工程 x86 x64
2021-07-02 17:29:12

我正在开发一个破解版,我一直在做一个特定的比较,我希望你能帮助我过去(其中之一)。这是我所知道的,相关的代码片段如下:

  • 表格中需要 19 位数字xxxx-xxxx-xxxx-xxxx(无非数字字符)。这些是以前的检查。

  • 看起来 19 位数字相加,加上 10 位,除以 4(右移 2 位),然后与堆栈上的某个值(我无法确定)进行比较。

你能帮我绕过CMP最后一行吗?

编辑:不允许NOP任何指令,需要找到序列号。

CODE : R9 是用户输入的 19 位字符串 增加了整个功能

var_18          = byte ptr -18h
var_8           = qword ptr -8

sub     rsp, 18h        ; Follow four lines check for 19-digit user entry
cmp     edx, 19
mov     r8, rcx         ; Now holds User inputted string
jz      short loc_140001013

xor     eax, eax
add     rsp, 24
retn

loc_140001013:                          ; CODE XREF: sub_140001000+Aj
xor     edx, edx
lea     rax, [r8+4]
mov     ecx, edx
xchg    ax, ax
db      66h, 66h
nop
hyphen_Check:                           ; CODE XREF: sub_140001000+2Fj
cmp     byte ptr [rax], 2Dh ; Use a FOR loop to ensure - is every 5th character, repeats 3 times
jnz     short loc_14000100C
add     ecx, 1
add     rax, 5
cmp     ecx, 3
jb      short hyphen_Check

loc_140001031:                          ; DATA XREF: .rdata:00000001400020C4o
 ; .rdata:00000001400020D8o ...
mov     [rsp+18h+var_8], rbx
mov     r10d, edx
mov     r11d, edx
lea     rbx, [rsp+18h+var_18]
mov     r9, r8
nop
db      66h, 66h
xchg    ax, ax
db      66h, 66h
xchg    ax, ax
db      66h, 66h
xchg    ax, ax
forLoopCheck:                           ; CODE XREF: sub_140001000+A2j
mov     rcx, rdx
non_Numeric_Check:                     
movsx   eax, byte ptr [r9+rcx] ; Begin FOR loop to check all digits for non-numeric characters
add     eax, 0FFFFFFD0h
cmp     eax, 9          ; Non-number entries are checked here, exits if letters are present
ja      ExitFUNC
add     rcx, 1
cmp     rcx, 4
jl      short non_Numeric_Check ; END FOR LOOP

原始块:

movsx   eax, byte ptr [r9+2] ; Get 3rd digit
                             ; Likely begins set of first checks on actual digits
movsx   ecx, byte ptr [r9+3] ; Get 4th digit
add     r11d, 1         ; Loop counter, loops 4 times, for each set of digits
add     ecx, eax        ; Set ECX to 3rd digit + 4th digit
movsx   eax, byte ptr [r9+1] ; Set EAX to 2nd digit
add     rbx, 4
add     ecx, eax        ; Adds 2nd digit to total
movsx   eax, byte ptr [r9] ; Move first digit into EAX
add     r9, 5           ; Moves to 2nd set of digits
add     ecx, eax        ; Adds 1st digit to total
add     ecx, r11d       ; Adds 1, for a total of 10
nop
nop
mov     [rbx-4], ecx    ; Added four digits stored here too
add     r10d, ecx       ; R10d becomes our added ECX value from above
cmp     r11d, 4         ; Loop counter check
jb      short forLoopCheck
shr     r10d, 2         ; shifts right r10d by 2, AKA divides by 4
mov     ecx, edx
lea     rax, [rsp+18h+var_18] ; var_18 = -18h
xchg    ax, ax          ; A NO OP
cmp     [rax], r10d     ; Compares right shifted 2 value to whatever is in RAX
jnz     short ExitFUNC  ; Compare above must be the same value

最后检查:

.text:00000001400010C4 loc_1400010C4:                          ; CODE XREF: sub_140001000+ECj
.text:00000001400010C4                 movzx   ecx, byte ptr [r8+rax+15] ; Move last set of 4 into ECX
.text:00000001400010CA                 cmp     [r8+rax], cl    ; CL = 1st digit of last 4, R8 = 1st dig of 1 set
.text:00000001400010CE                 jz      short ExitFUNC  ; Kicks you out
.text:00000001400010D0                 movzx   r9d, byte ptr [rax+r8+5]
.text:00000001400010D6                 cmp     cl, r9b         ; cmp 1st digit of last 4 with 1st digit of 2nd 4
.text:00000001400010D9                 jz      short ExitFUNC
.text:00000001400010DB                 cmp     r9b, [rax+r8+10] ; cmp 1st digit of 2nd set to 1st digit of 3rd set
.text:00000001400010E0                 jz      short ExitFUNC
.text:00000001400010E2                 add     edx, 1
.text:00000001400010E5                 add     rax, 1
.text:00000001400010E9                 cmp     edx, 4
.text:00000001400010EC                 jb      short loc_1400010C4
1个回答

这是您的串行检查算法的样子:

// expects xxxx-xxxx-xxxx-xxxx
bool check_serial(char inp[]){
    int total = 0;
    int checksum = 1 + (inp[0] + inp[1] + inp[2] + inp[3]);

    for (int i = 1; i <= 4; i++) {
        total += i + (inp[0] + inp[1] + inp[2] + inp[3]);
        inp += 5;
    }

    total /= 4;

    printf("chk is %d, total is %d\n", checksum, total);

    return (checksum == total);
}

我将仅解释 at 的值rsp,因为您似乎了解算法的其余部分。

首先,你有这个指令:

cmp     [rax], r10d

检查什么rax是:

lea     rax, [rsp+18h+var_18]

由于var_18是-0x18,我们知道它0x18 + (-0x18)是0,所以上面的等价于:

lea     rax, [rsp]

(您可以将光标放在IDA18h并按下K,它会去掉变量名并只显示[rsp]

这相当于:

mov     rax, rsp

这意味着你cmp正在做:

cmp     [rsp], r10d

现在,您需要找出堆栈中的内容。为此,您再次检查该函数,并搜索对rsp. 您可以单击,rsp以便 IDA 突出显示使用它的任何地方。

快速浏览后,这似乎很有趣:

lea     rbx, [rsp+18h+var_18]

如前所述,它可以简化为:

lea     rbx, [rsp]

这基本上是:

mov     rbx, rsp

现在我们知道它rbx指向栈顶,在比较函数中,我们看到:

add     rbx, 4
; ...
; ...
; ...
mov     [rbx-4], ecx

该代码第一次运行时,rbx指向栈顶;它增加了一次,然后是movdereferences rbx - 4,这使得它等效于:

mov     [rbx], ecx
add     rbx, 4

这本质上是设置元素并跳到下一个元素。

现在,由于rbx指向栈顶,第一次迭代会将总和(即ecx保存到栈顶(这是您需要匹配的值),并增加指向下一个元素的指针(您不需要不再需要这里了)。

宾果游戏,我们已经发现了rsp. 它只是ecx第一次迭代中的值,基本上是第一组字符的总和加一。


接下来,请注意它使用每个数字的 ASCII 代码而不是数字本身:

movsx eax, byte ptr [r9]

由于r9指向字符数组,因此数字'1'实际上是49(ASCII 49 = 1)。

为了更好地理解它:

char serial[] = "1111-2222-3333-4444";
int current_char = serial[0]; // puts the integer value 49, not 1