如果不等于0,则将整数减1

时间:2017-03-19 13:33:35

标签: c++ c if-statement timing-attack

我试图通过删除代码中的if语句来解决时间泄漏,但是由于c ++对if语句中的整数输入的解释,我被卡住了。

请注意,我假设编译器确实创建了条件分支,导致时间信息泄露!

原始代码是:

int s
if (s)
   r = A
else
   r = B

现在我试图将其重写为:

int s;
r = sA+(1-s)B

因为s没有绑定到[0,1],所以如果s超出[0,1],我会遇到错误地乘以A和B的问题。如果不在s上使用if语句来解决这个问题,我该怎么办?

提前致谢

3 个答案:

答案 0 :(得分:1)

你有什么证据表明if语句会导致时间泄漏?

如果使用启用了优化的现代编译器,则该代码不应生成分支。您应该通过查看汇编语言输出来检查编译器正在做什么。

例如,g ++ 5.3.0编译此代码:

#include <Windows.h>
#include <iostream>
#include <iomanip>
#include <exception>
#include <cstdint>
#include <vector>
#include <sstream>
#include <fstream>

template <typename T>
void print_hex(std::ostream &stream, T x, int width = 8){
    stream << std::hex << std::setw(width) << std::setfill('0') << x << std::dec;
}

template <typename T>
void print_address(std::ostream &stream, T x){
    if (x < 0x100)
        print_hex(stream, x, 2);
    else if (x < 0x10000)
        print_hex(stream, x, 4);
    else if (x < 0x100000000ULL)
        print_hex(stream, x, 8);
    else
        print_hex(stream, x, 16);
}

class DebugProcess{
    DWORD pid;
public:
    DebugProcess(DWORD pid): pid(pid){
        if (!DebugActiveProcess(pid)){
            auto error = GetLastError();
            std::cerr << "DebugActiveProcess() failed with error " << error << " (0x";
            print_hex(std::cerr, error);
            std::cerr << ")\n";
            throw std::exception();
        }
    }
    ~DebugProcess(){
        if (!DebugActiveProcessStop(this->pid)){
            auto error = GetLastError();
            std::cerr << "DebugActiveProcessStop() failed with error " << error << " (0x";
            print_hex(std::cerr, error);
            std::cerr << ")\n";
        }
    }
};

bool is_handle_valid(HANDLE handle){
    return handle && handle != INVALID_HANDLE_VALUE;
}

class AutoHandle{
    HANDLE handle;
public:
    AutoHandle(HANDLE handle): handle(handle){}
    ~AutoHandle(){
        if (is_handle_valid(this->handle))
            CloseHandle(this->handle);
    }
};

template <typename T>
void zero_struct(T &mem){
    memset(&mem, 0, sizeof(mem));
}

struct memory_region{
    std::uint64_t start,
        size;
    MEMORY_BASIC_INFORMATION info;
};

void dump_process_memory(DWORD pid){
    DebugProcess dp(pid);

    auto proc = OpenProcess(PROCESS_ALL_ACCESS, false, pid);
    if (!is_handle_valid(proc)){
        auto error = GetLastError();
        std::cerr << "OpenProcess() failed with error " << error << " (0x";
        print_hex(std::cerr, error);
        std::cerr << ")\n";
        return;
    }
    AutoHandle autoproc(proc);

    std::vector<memory_region> regions;
    for (std::uint64_t address = 0; address < 0x10000000ULL;){
        MEMORY_BASIC_INFORMATION mbi;
        zero_struct(mbi);
        auto bytes = VirtualQueryEx(proc, (LPCVOID)address, &mbi, sizeof(mbi));
        if (!bytes){
            address += 4096;
            continue;
        }
        if (mbi.State == MEM_COMMIT && (mbi.Protect & PAGE_GUARD) != PAGE_GUARD)
            regions.push_back(memory_region{ (std::uint64_t)mbi.BaseAddress, mbi.RegionSize, mbi });

        address += mbi.RegionSize;
    }

    if (regions.size()){
        std::cout << "Flat size:   " << regions.back().start + regions.back().size << std::endl;
        std::uint64_t sum = 0;
        for (auto &region : regions)
            sum += region.size;
        std::cout << "Packed size: " << sum << std::endl;
    }

    std::ofstream file("dump.bin", std::ios::binary);
    std::uint64_t current_size = 0;
    for (auto &region : regions){
        std::vector<char> buffer(region.size);
        size_t read;
        if (!ReadProcessMemory(proc, (LPCVOID)region.start, &buffer[0], buffer.size(), &read)){
            auto error = GetLastError();
            if (error != ERROR_PARTIAL_COPY){
                std::cerr << "ReadProcessMemory() failed with error " << error << " (0x";
                print_hex(std::cerr, error);
                std::cerr << ")\n";
                return;
            }
        }

        if (read < region.size){
#if 1
            std::cerr << "Warning: region starting at 0x";
            print_address(std::cerr, region.start);
            std::cerr << " has size " << region.size << ", but only " << read
                << " bytes could be read by ReadProcessMemory().\n";
#endif
            memset(&buffer[read], 0, buffer.size() - read);
        }

        file.seekp(region.start);

        file.write(&buffer[0], buffer.size());
    }
}

int main(int argc, char **argv)
{
    DWORD pid;
    std::cout << "Enter PID : ";
    std::cin >> pid;

    try{
        dump_process_memory(pid);
    }catch (std::exception &){
        std::cerr << "Exception caught.\n";
    }

    std::cin.ignore();    
    std::cin.get();    
    return 0;
}

到这个集会:

int f(int s, int A, int B) {
  int r;
  if (s)
    r = A;
  else
    r = B;

  return r;
}

看,妈!没有分支! ;)

答案 1 :(得分:1)

如果你知道整数中的位数,那就很容易了,虽然有一些复杂因素使得标准清理可能会出现异常的整数表示。

这是32位整数的一个简单解决方案:

uint32_t mask = s;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask &= 1;
r = b ^ (-mask & (a ^ b)):

五个移位和/或语句传播mask中的任何设置位,以便最后低位为1,除非掩码最初为0.然后我们隔离低位,结果在1或0中。最后一个语句相当于你的两个乘法并加上。

根据观察,这是一个更快的一个,如果你从一个数字中减去一个并且符号位从0变为1,那么数字是0:

uint32_t mask = ((uint32_t(s)-1U)&~uint32_t(s))>>31) - 1U;

这与减去1然后使用进位位的计算基本相同,但不幸的是,进位位没有暴露给C语言(除非可能通过编译器特定的内在函数)。

其他变化是可能的。

答案 2 :(得分:-1)

在优化不可用时,没有分支的唯一方法是使用内联汇编。假设8086:

mov ax, s
neg ax        ; CF = (ax != 0)
sbb ax, ax    ; ax = (s != 0 ? -1 : 0)
neg ax        ; ax = (s != 0 ? 1 : 0)
mov s, ax     ; now use s at will, it will be: s = (s != 0 ? 1 : 0)