x86 - 汇编语言加密/混淆 - 如何反转?

时间:2017-03-27 22:39:07

标签: assembly encryption x86 obfuscation

我已经获得了以下加密程序:它实际上使用的是EKey var(I)和temp_char var,它等于数组I中的第一个,第二个,第三个等等字符根据所包含的for循环的迭代进行传递。

我尽力发表评论并尽力理解。

以下注释值来自查看' watch'中的寄存器/值。在视觉工作室,并假设我进入" aaaa"作为我的测试字符串。

注意:EChars是一个早期在程序中定义的数组,但我并不认为有必要提及它,除了它最多可以容纳6个字符并且是以$结尾。 与DChars相同。

void encrypt_chars (int length, char EKey)
{   char temp_char;                 // Character temporary store

for (int i = 0; i < length; i++)    // Encrypt characters one at a time
{
    temp_char = OChars [i];         // Get the next char from Original Chars array

    __asm {                         
        push   eax                  // Prepare the EAX register to store volatile memory.
        push   ecx                  // Prepare the ECX register to store volatile memory. 
                                    // Removes whatever value is stored in there to start with.
                                    // Pushing pushes the value (not stored in a register necessarily) and writes it into the stack

        movzx  ecx,temp_char        // Moves the char with zeroes, meaning that the temp_char value has 0s at the beginning
        lea    eax,EKey             // Loads the address of the Ekey currently in memory, into the EAX register
        push ecx                    // Push ecx to the top of the stack. Stores char
        push eax                    // Stores address of ekey
        call   encrypt_17           // Calls the encyrption routine below, before jumping back up to the line below
        add esp, 8                  // Adding 8 to the stack pointer moves the pointer to (somewhere in the stack, I'm unsure where)
        mov    temp_char,al         // AL is the last 8 bits of the EAX register, what happens here is that AL is then moved into
                                    // the temp_char value, after it has been encrypted; modifying the value before storing it into the EChars array

        pop    ecx                  // Restore original register values
        pop    eax                  // ""
    }
    EChars [i] = temp_char;         // Store encrypted char in the Encrypted Chars array location depending on loop
}
return;

// Inputs: register EAX = 32-bit address of Ekey,
//                  ECX = the character to be encrypted (in the low 8-bit field, CL).
// Output: register EAX = the encrypted value of the source character (in the low 8-bit field, AL).



__asm {

   encrypt_17:
           push esi 
           push ecx                //temp char t = 116 first time around
           mov  esi, eax           //sets eax to esi, which has a value of '2422272'
           and dword ptr[esi], 0xFF
           ror byte ptr[esi], 1
           ror byte ptr[esi], 1    //these two lines set Ekey I(72) to Ekey R(82) (inc by 10?)
           add byte ptr[esi], 0x01 //adds 1 to the ekey, changing it to S
           mov ecx, [esi]          //move location of Ekey (S) to ECX
           pop edx                 //restore original val of edx (which appears to be 116 (temp char, t from 'test')
       x17 : 
           ror dl, 1        //dl = t, after this it's set to 58 ':', edx is also set to 58,
                            //second time through sets edx to 29 and dl to 29 '\x1d'
           dec ecx          //decrement ecx to 82 from 83,
           jnz x17          //goes until ecx is 0
           mov eax, edx     //eax gets set to 142. which is 'Z' with an arrow above it
           add eax, 0x20    //eax gets set to 174, from 154
           xor eax, 0xAA    //XOR'ing 154 with AA sets it to 4
           pop esi          //ECX gets set to 0
           ret              //value gets returned
}

以下是我到目前为止的解密尝试:

void decrypt_chars (int length, char EKey)
{   char temp_char;                 // Character temporary store

for (int i = 0; i < length; i++)    // Encrypt characters one at a time
{
temp_char = EChars[i];              // Get the next char from Encrypted Chars array

__asm {
        push   eax                  // Prepare the EAX register to store volatile memory.
        push   ecx                  // Prepare the ECX register to store volatile memory. 
                                    // Removes whatever value is stored in there to start with.
                                    // Pushing pushes the value (not stored in a register necessarily) and writes it into the stack

        movzx  ecx, temp_char       // Moves the char with zeroes, meaning that the temp_char value has 0s at the beginning
        lea    eax, EKey            // Loads the address of the Ekey currently in memory, into the EAX register
        push ecx                    // Push ecx to the top of the stack. Stores char
        push eax                    // Stores address of ekey
        call   decrypt_17           // Calls the encyrption routine below, before jumping back up to the line below
        add esp, 8                  // Adding 8 to the stack pointer moves the pointer to...
        mov    temp_char, al        // AL is the last 8 bits of the EAX register, what happens here is that AL is then moved into
                                    // the temp_char value, after it has been encrypted; modifying the value before storing it into the EChars array

        pop    ecx                  // Restore original register values
        pop    eax                  // ""
    }
DChars[i] = temp_char;              // Store decrypted char
}
return;

// Inputs: register EAX = 32-bit address of Ekey,
//                  ECX = the character to be encrypted (in the low 8-bit field, CL).
// Output: register EAX = the encrypted value of the source character (in the low 8-bit field, AL).

__asm {

decrypt_17:

        push ebp                    
        mov ebp, esp                

        mov ecx, [ebp + 12]         // The base pointer is moved to the location of EKey -- Ekey is moved into ECX
        mov eax, [ebp + 8]          // Base pointer points to Ekey address

        push esi                    // Push ESI to the top of the stack, ready for storing the EKey value later on in the mov esi, eax line
        push ecx                    // Push ECX to the top  of the stack, in this program our ECX register holds the variable temp_char

        mov  esi, eax               // Copy the register (EAX) that the address of Ekey is stored in, into ESI
                                    // Gets EKey

        and dword ptr[esi], 0xFF    // AND the address in ESI which contains the Ekey with FF in hex

        rol byte ptr[esi], 2        // Revert the two rotate right with carry operations

        sub byte ptr[esi], 0x01     // The byte ptr implies that the operand size is 8-bits in value; subbing 1 in hex is the opposite of adding

        mov ecx, [esi]              
        pop edx                     // Get temp_char

x17 :   rol al, 1                   // Revert the rotate right op
                                    // carry loops back around to the most-significant bit of the value

        inc ecx                     // Here is where I'm having issues with the reversal... I don't know when to stop decrementing 
        cmp ecx, 0x52
        jle x17                     
        mov eax, edx                // ***The modifed temp_char value stored in EDX is then moved into the EKey register
        sub eax, 0x20               
        xor eax, 0xAA               

        pop esi                     // Restoring the value on top of the stack, in this case the modified temp_char value to the top of the stack ready for the next call

        mov ebp, esp                // Resetting the first instruction; Base Pointer val
        pop ebp                     // Return original EBP value
        ret                         // Return the encrypted value, stored in EAX/AL to the calling function
    }
}

1 个答案:

答案 0 :(得分:0)

  

我尽力发表评论并尽力理解。

很抱歉直言不讳,但编写解码器还不够好,简直就是意外。

我会评论一些事情,但可能有太多东西无法理解......

        push   eax                  // Prepare the EAX register to store volatile memory.
        push   ecx                  // Prepare the ECX register to store volatile memory. 

(1)&#34;堆叠内存&#34;是普通的存储器,但为方便起见,寄存器对ss:esp指向堆栈的假想&#34;顶部&#34;,它向低地址增长。 IE浏览器。 push eax可以被视为esp = esp - 4; [esp] = eax;,将值写入内存,并更新esp以指向它。 pop正在撤消此操作,从[esp]加载值并将esp += 4;更新为&#34;从堆栈中释放值&#34;。

        push ecx                    // Push ecx to the top of the stack. Stores char
        push eax                    // Stores address of ekey
        call   encrypt_17           // Calls the encyrption routine below, before jumping back up to the line below
        add esp, 8                  // Adding 8 to the stack pointer moves the pointer to (somewhere in the stack, I'm unsure where)

不确定为什么他们将char + EChar地址置于堆栈中,当他们根本不使用它们时(encrypt_17从寄存器中获取值,而不是从堆栈中获取),可能只是为了添加混乱。无论如何,由于两个push正在使用32位(== 4字节)值,这意味着add esp,8将&#34;扔掉&#34;两者(再次检查(1)或在调试器中观察),将堆栈恢复到此部分代码之前的状态,然后最终恢复原始eax/ecx将正常工作。顺便说一句,encrypt_17确实修改了edx,它还没有被恢复,因此只有当编译器不需要为C代码保留edx时,这个内联汇编才能运行(幸运)。

   encrypt_17:
           push esi 
           push ecx                //temp char t = 116 first time around
           mov  esi, eax           //sets eax to esi, which has a value of '2422272'

存储到堆栈中(此时顶部是返回上面代码的ret指令)旧esi,以及要编码的字符(在ecx中),然后将esi设置为eaxesi = EChar address;),而不是设置为esi。

           and dword ptr[esi], 0xFF
           ror byte ptr[esi], 1
           ror byte ptr[esi], 1    //these two lines set Ekey I(72) to Ekey R(82) (inc by 10?)
           add byte ptr[esi], 0x01 //adds 1 to the ekey, changing it to S
这是一种玩笑吗?您真的不愿意阅读英特尔指令参考指南以了解ror的作用吗? (注释中的值必须是错误的,我不是72,向右旋转72次不是82,也不是十进制,或者是十六进制(不清楚,你正在使用哪一个)。)

           mov ecx, [esi]          //move location of Ekey (S) to ECX
           pop edx                 //restore original val of edx (which appears to be 116 (temp char, t from 'test')

将修改后的EChar [0]值加载到cl(高位24位被第一个and dword归零,因此读取ecx,但只有低8位是有趣的。然后pop edx会将存储在堆栈中的最后一个值弹出到edx。最后的值是原始的ecx(要加密的字符)。再次参见(1)以实现push/pop配对寄存器上没有固定,但它取决于push/pop指令的顺序,从哪个寄存器存储/加载哪个值。通过mov edx,ecx来执行encrypt_17,可以实现相同的效果。 ror dl, 1 //dl = t, after this it's set to 58 ':', edx is also set to 58, 的开头。

edx

dl58是相同的注册,当然两者都是edx。不同之处在于dl是完整的32位,而dl是其中最低的8位部分,可以使用ror别名单独使用。并且edx仅在低8位部分完成,因此从b0右侧离开的任何位将重新进入ror edx,1的b7,而不是b31。这将需要dh(旋转所有32位)。您还可以通过别名dx单独访问位b8-b15,通过别名edx单独访问位b0-b15。并且整个rdx在64b CPU上是 mov eax, edx //eax gets set to 142. which is 'Z' with an arrow above it add eax, 0x20 //eax gets set to 174, from 154 的低32位部分别名。

eax

首先你说 xor eax, 0xAA //XOR'ing 154 with AA sets it to 4 是142,然后突然是154?当0x20为32时,它清楚为142(142 + 32 = 174)。注意哪些值以六进制格式显示,不要将0x20与20十进制混合。

xor

在这种情况下甚至不确定为什么显示十进制值(并且它是174,而不是154),例如对0xAA执行0xAE0x04很容易看到特定位,它将如何结束xor。在十进制中,你不能看到&#34;&#34;&#34;位,您必须先将其转换为二进制/六进制才能在头部进行 pop esi //ECX gets set to 0 ret //value gets returned 计算。

esi

这会恢复旧ecxpop上的任何内容都不会更改。此外,堆栈在返回地址后再次指向ret(清除所有本地混乱),因此push将起作用。如果您忘记堆栈上的某些retxor将使用错误的值作为返回地址,从而导致崩溃。

所以加密主要是多次旋转这些位(无用,因为每获得原始值的第8次旋转,所以只有模数8的旋转有一些效果),有一些加法和xoring,所有这些都可以直接方式反转

有趣的是,只使用EKey的第一个字节,接下来的三个设置为零,其余部分被忽略。所以这是8位密钥强加密... eee ...&#34; strong&#34;。

关于逆转:

1)EKey不反转&#34;,保持计算与加密相同,在EKey中具有相同的值进行解密。

2)向后解密char,从最后一个op(ecx)等开始......

3)你不需要将ror计数器增加到未知值,你执行的循环次数与加密次数相同(从[esi]到0),而不是rol你做xor 0xAA,这是将加密效果逆转为解密的原因。

所以基本上EKey相关的东西是完整的(以与加密相同的方式完成),结果是旋转次数(加密/解密相同,因为你反转旋转方向来撤消它)。 char本身的操作按顺序和操作逻辑颠倒过来。 IE浏览器。 xor 0xAA反转ror(是的,相同的一个),roladdsub反转{{1}}(或负值) )等等。