澄清一下,我了解ADC的工作原理。它与常规ADD相同,但如果设置了进位标志,则增加1。 CF标志,在32位上下文中添加两个64位值时:

Assume EDX:EAX and EBX:ECX pairs hold two 64 bit values that are to be added together.

add     eax, ecx
adc     edx, ebx
mov     dword [ebp+Some64BitValue], eax
mov     dword [ebp+Some64BitValue+4], edx


我无法理解的含义是当ADC连续使用像这样的变量/寄存器:

add     eax, dword [esi]
adc     eax, dword [esi+0x4]
adc     eax, dword [esi+0x8]
adc     eax, dword [esi+0x10]
...
adc     eax, 0


一般来说,这样做的目的是什么?什么是EAX?

* update:

请参阅下面的答案

评论

您可以发布更大的代码段吗?例如eax如何初始化?

#1 楼

我找到了问题的答案。

EAX是任意的,[ESI]中添加的值也是任意的。

我要反转的函数是一个计算将数据按上述方式累加在一起后,再将16位校验和,然后将EAX中的结果折叠到AX中。我正在查看的是类似于此处描述的Internet校验和。

在这种情况下,连续ADC指令的目的看起来像是仅在汇编级别可用的优化。校验和执行多次称赞加法。仅使用ADD才能执行相同操作的另一种方法是让进位位在寄存器的上半部分累加,然后在末尾将它们加回到下半部分。

#2 楼

编辑2

无需求助于内联汇编/单独的汇编模块,我能够使用固有的_addcarry_uxx(16,32,64)生成连续的adc指令(在msvc cl中对/ O1开关进行了全面优化。 exe)

相关代码及其下面的反汇编

#include <stdio.h>
#include <intrin.h>
#define BUSIZ  16
#define UNROLL 8
unsigned int dat[BUSIZ] = 
{
    0x10001337, 0xffffffff, 0x10001337 , 0xffffffff,
    0x00001337, 0xdeadbeef, 0xbeadbed5 , 0xdad15dad,
    0xba5eba11, 0xf001b055, 0xc001b055 , 0x501eb055,
    0xba11ba75, 0xbadba115, 0xbed15bad , 0xdaff0d11
};    
int main (void) 
{
    unsigned int chksum = 0;
    unsigned char carry = 0;
    for(int i=0; i< BUSIZ; i += UNROLL )
    {
        carry = _addcarry_u32(carry,chksum,dat[i+0],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+1],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+2],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+3],&chksum);        
        carry = _addcarry_u32(carry,chksum,dat[i+4],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+5],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+6],&chksum);
        carry = _addcarry_u32(carry,chksum,dat[i+7],&chksum);
    }
    printf("FINAL CHECKSUM = %8x\n" , chksum + carry );  
    return 0;
}


执行后将打印出

:\>addcarry.exe
FINAL CHECKSUM = 616ba476


通过python脚本检查结果的准确性

:\>cat chksum.py
dat = [
    0x10001337, 0xffffffff, 0x10001337 , 0xffffffff,
    0x00001337, 0xdeadbeef, 0xbeadbed5 , 0xdad15dad,
    0xba5eba11, 0xf001b055, 0xc001b055 , 0x501eb055,
    0xba11ba75, 0xbadba115, 0xbed15bad , 0xdaff0d11
]
chksum33bit = 0
for i in range (0,16,1):
    chksum33bit = chksum33bit + dat[i]
chklow = chksum33bit & 0xffffffff
chkhig = chksum33bit >> 32
chkfin = chklow+chkhig
print (hex(chkfin))

:\>python chksum.py
0x616ba476L


反汇编我们看到编译器生成了连续的adc指令

:\>cdb -c "uf addcarry!main;q" addcarry.exe | grep -B 31 quit
0:000> cdb: Reading initial command 'uf addcarry!main;q'
addcarry!main:
01151029 33c9            xor     ecx,ecx
0115102b b804901901      mov     eax,offset addcarry!dat+0x4 (01199004)
01151030 8ad1            mov     dl,cl

addcarry!main+0x9:
01151032 80c2ff          add     dl,0FFh
01151035 1348fc          adc     ecx,dword ptr [eax-4]
01151038 1308            adc     ecx,dword ptr [eax]
0115103a 134804          adc     ecx,dword ptr [eax+4]
0115103d 134808          adc     ecx,dword ptr [eax+8]
01151040 13480c          adc     ecx,dword ptr [eax+0Ch]
01151043 134810          adc     ecx,dword ptr [eax+10h]
01151046 134814          adc     ecx,dword ptr [eax+14h]
01151049 134818          adc     ecx,dword ptr [eax+18h]
0115104c 0f92c2          setb    dl
0115104f 83c020          add     eax,20h
01151052 3d44901901      cmp     eax,offset addcarry!__scrt_default_matherr (01199044)
01151057 7cd9            jl      addcarry!main+0x9 (01151032)

addcarry!main+0x30:
01151059 0fb6c2          movzx   eax,dl
0115105c 03c1            add     eax,ecx
0115105e 50              push    eax
0115105f 6890011901      push    offset addcarry!`string' (01190190)
01151064 e805000000      call    addcarry!printf (0115106e)
01151069 59              pop     ecx
0115106a 59              pop     ecx
0115106b 33c0            xor     eax,eax
0115106d c3              ret
quit:


编辑结束2

编辑1:

即使我可以生成多个ADC,但我无法成功生成
连续的ADC使用c编译器
,所以我环顾了Windows \ system32目录,如果任何二进制文件具有连续的adc,而
位于imagehlp.dll中
,并且在进行谷歌搜索之后,它似乎是RFC1071 IP标头校验和br />大概是用手写汇编编写的(找到了一些汇编代码s代表Motorola,cray等,这也是一个Google小组链接,讨论了ChkSum的实现,还有一个旧的Dave Cutler从NT图像帮助工具包sdk编写的ChkSum(),该工具包由ms于1993年左右发布) >
下面是win7 sp1 32位imagehlp.dll ADC序列

grep -obUaPs "\x13\x46\x04\x13\x46\x08" --include=*.dll *

imagehlp.dll:17883:‼F♦‼F
imagehlp.dll:17917:‼F♦‼F
imagehlp.dll:17963:‼F♦‼F
imagehlp.dll:18021:‼F♦‼F

xxd -c 12 -g 3 -s 18021 -l 99 imagehlp.dll

0004665: 134604 134608 13460c 134610  .F..F..F..F.
0004671: 134614 134618 13461c 134620  .F..F..F..F
000467d: 134624 134628 13462c 134630  .F$.F(.F,.F0
0004689: 134634 134638 13463c 134640  .F4.F8.F<.F@
0004695: 134644 134648 13464c 134650  .FD.FH.FL.FP
00046a1: 134654 134658 13465c 134660  .FT.FX.F\.F`
00046ad: 134664 134668 13466c 134670  .Fd.Fh.Fl.Fp
00046b9: 134674 134678 13467c 83d000  .Ft.Fx.F|...
00046c5: 81c680                       ...

dumpbin /headers imagehlp.dll | grep -i "section header #1" -A 5

SECTION HEADER #1
   .text name
   23249 virtual size
    1000 virtual address (41871000 to 41894248)
   23400 size of raw data
     600 file pointer to raw data (00000600 to 000239FF)

cdb -c "ln (imagehlp + 1000 + (0n18021-0x600));q" -z imagehlp.dll | grep -i -A 1 reading

0:000> cdb: Reading initial command 'ln (imagehlp + 1000 + (0n18021-0x600));q'

(41874f7b)   imagehlp!ChkSum+0xea   |  (41875105)   imagehlp!_SEH_prolog4_GS


编辑结束1

可能有多个ADC因为未对编译进行优化是

的原因之一,假设您有这样的代码

#include <stdio.h>
#include <windows.h>

int main (void) {
    DWORD64 a = 0x100002000;
    DWORD64 b = 0x00000000ffffffff;
    DWORD64 c = a + b;    
    printf("%I64x\n" , a);
    printf("%I64x\n" , b);
    printf("%I64x\n" , c);
    c = c + a;
    printf("%I64x\n" , c);
    c = c + b;
    printf("%I64x\n" , c);
    c = c + c;
    printf("%I64x\n" , c);    
    return 0;
}


如果不进行优化就进行编译
反汇编函数主函数并查找adc指令
您将看到几条adc指令,包括一些使用同一寄存器的

cl /Zi /EHsc /W4 /Od /nologo /analyze adc.cpp /link /release /nologo adc.cpp  

cdb -c "g adc!main;uf .;q" adc.exe | grep -i "adc "
000a102b 134dec          adc     ecx,dword ptr [ebp-14h]
000a107c 1345f4          adc     eax,dword ptr [ebp-0Ch]
000a10a3 134dec          adc     ecx,dword ptr [ebp-14h]
000a10ca 1355fc          adc     edx,dword ptr [ebp-4]


,但是如果您进行了优化编译器足够聪明,可以删除所有Adc指令

cl /Zi /EHsc /W4 /Ox /nologo /analyze adc.cpp /link /release /nologo adc.cpp

cdb -c "g adc!main;uf .;q" adc.exe | grep -i "adc "

adc.exe
100002000
ffffffff
200001fff
300003fff
400003ffe
800007ffc


您有问题的代码可能是执行纯加法并返回结果的子例程的结果,如下所示

#include <stdio.h>
#include <windows.h>
#pragma optimize ( "t" , off)
DWORD64  madd (DWORD64 a, DWORD64 b) 
{
    DWORD64 c = 0;
    c = a + b;  
    c = c + a;    
    c = c + b;
    c = c + c;
    return c;    
}
#pragma optimize ( "t" , on)
int main (void) {
    DWORD64 a = 0x100002000;
    DWORD64 b = 0x00000000ffffffff;
    printf("%I64x\n" , madd(a,b));    
    return 0;
}


这里是反汇编添加了multi64位的函数的结果值并返回结果

反汇编函数madd

0:000> uf adc!madd
adc!madd [adc.cpp @ 6]:
    6 00291000 55              push    ebp
    6 00291001 8bec            mov     ebp,esp
    6 00291003 51              push    ecx
    6 00291004 51              push    ecx
    7 00291005 0f57c0          xorps   xmm0,xmm0
    7 00291008 660f1345f8      movlpd  qword ptr [ebp-8],xmm0
    8 0029100d 8b4508          mov     eax,dword ptr [ebp+8]
    8 00291010 034510          add     eax,dword ptr [ebp+10h]
    8 00291013 8b4d0c          mov     ecx,dword ptr [ebp+0Ch]
    8 00291016 134d14          adc     ecx,dword ptr [ebp+14h]
    8 00291019 8945f8          mov     dword ptr [ebp-8],eax
    8 0029101c 894dfc          mov     dword ptr [ebp-4],ecx
    9 0029101f 8b45f8          mov     eax,dword ptr [ebp-8]
    9 00291022 034508          add     eax,dword ptr [ebp+8]
    9 00291025 8b4dfc          mov     ecx,dword ptr [ebp-4]
    9 00291028 134d0c          adc     ecx,dword ptr [ebp+0Ch]
    9 0029102b 8945f8          mov     dword ptr [ebp-8],eax
    9 0029102e 894dfc          mov     dword ptr [ebp-4],ecx
   10 00291031 8b45f8          mov     eax,dword ptr [ebp-8]
   10 00291034 034510          add     eax,dword ptr [ebp+10h]
   10 00291037 8b4dfc          mov     ecx,dword ptr [ebp-4]
   10 0029103a 134d14          adc     ecx,dword ptr [ebp+14h]
   10 0029103d 8945f8          mov     dword ptr [ebp-8],eax
   10 00291040 894dfc          mov     dword ptr [ebp-4],ecx
   11 00291043 8b45f8          mov     eax,dword ptr [ebp-8]
   11 00291046 0345f8          add     eax,dword ptr [ebp-8]
   11 00291049 8b4dfc          mov     ecx,dword ptr [ebp-4]
   11 0029104c 134dfc          adc     ecx,dword ptr [ebp-4]
   11 0029104f 8945f8          mov     dword ptr [ebp-8],eax
   11 00291052 894dfc          mov     dword ptr [ebp-4],ecx
   12 00291055 8b45f8          mov     eax,dword ptr [ebp-8]
   12 00291058 8b55fc          mov     edx,dword ptr [ebp-4]
   14 0029105b c9              leave
   14 0029105c c3              ret


函数使用相同的寄存器ecx来添加多个结转

0:000> # adc*, adc!madd l 60
adc!madd+0x16 [adc.cpp @ 8]:
00291016 134d14          adc     ecx,dword ptr [ebp+14h]  
adc!madd+0x28 [adc.cpp @ 9]:
00291028 134d0c          adc     ecx,dword ptr [ebp+0Ch]
adc!madd+0x3a [adc.cpp @ 10]:
0029103a 134d14          adc     ecx,dword ptr [ebp+14h]
adc!madd+0x4c [adc.cpp @ 11]:
0029104c 134dfc          adc     ecx,dword ptr [ebp-4]
0:000>