不使用“+”號計算兩個32位整數a+b的值(C++)

## 0x1簡單分析題意

  1.題目要求

給出兩個整數 a 和 b , 求他們的和。

There is no need to read data from standard input stream.

Both parameters are given in function aplusb, your job is to calculate the sum and return it.

Students in the basic class of the algorithm only need to use the arithmetic operator ‘+’ to complete the problem, without considering the requirements of the bit operation.

  2.說明QAQ

a和b都是 32位 整數麼?

是的

我可以使用位運算符麼?

當然可以

  3.題目就是讓我們不使用+號運算符計算兩個數的值,整數在計算機中是以二進制的形式存儲的(具體原碼,反碼,補碼的知識自行百度),通過與、或、非可以變0為1,變1為0。數字進位可以通過左移二進制數實現,

瞭解了這些知識,就有思路實現這個算法了。

## 0x2準備工具(編譯器)

  1.有了思路,我們就要去實現算法,“工欲善其事,必先利其器”,我用的是VS2015編譯器。有了工具就可以實現算法了,程序代碼如下:

<code>int sumsAB(int a, int b)
{

\twhile (b)
\t{
\t\tint temp = a ^ b;//將兩個數按不進位相加,保存在中間變量
\t\tb = (a&b) << 1;//將b進1位,直到a&b等於0不需要進位為止
\t\ta = temp;//將a與b相加的值保存在a中
\t}
\treturn a;//出錯返回-1
}/<code>

就這麼簡單的一個函數為什麼就能實現A+B的值呢?

如果兩個整數的二進制形式沒有相同位都為1的情況下,異或操作就是在做加法。

例如:

1001 0110等於十進制150

0110 0001等於十進制97

異或結果:1111 0111等於十進制247,就是兩個數相加的結果。

  2.那麼進位了,怎麼辦呢?

0011等於十進制3

0110等於十進制6

異或結果:0101等於十進制5,顯然兩個數相加的結果應該是9

模擬一下上面的循環:

第一輪循環:

0110與0011後為0010左移一位0100

0011異或0110後為0101

第二輪循環:

0100與0101後為0100左移一位1000

0101異或0100後為0001

第三輪循環:

1000與0001後為0000左移一位0000

0001異或1000後為1001

此時b的值為0,a的值為9,循環結束,b的值就是兩個數相加後的結果。

  3.我也可以編寫一個將十進制數按二進制數形式輸出的函數,然後在上面計算a+b的函數每一步插入它,讓我們更直觀的看到他的計算過程,便於理解。

<code>void PrintIntA(int a)
{
\tint temp = a;

\tint arr[8][4] = //用32位二進制表示int整數
\t{
\t\t0,0,0,0,0,0,0,0,
\t\t0,0,0,0,0,0,0,0,
\t\t0,0,0,0,0,0,0,0,
\t\t0,0,0,0,0,0,0,0
\t};
\t//從最後一個往前插入數據,倒著循環,符號位放在最前
\tif (a < 0)
\t{
\t\ta = -a;//將負數變為正數
\t\tarr[0][0] = 1;//符號位變為1
\t}
\tfor (int i = 7; i > 0; i--)
\t{
\t\tfor (int j = 3; j > 0; j--)
\t\t{
\t\t\tif (i == 0 && j == 0 && a != 0)
\t\t\t{
\t\t\t\tprintf("要轉換成二進制的數字%d大於int數據類型表示的範圍!",temp);
\t\t\t\treturn;
\t\t\t}
\t\t\tarr[i][j] = a % 2;//對2取餘從又往左存放
\t\t\ta = a / 2;//將a的值除以2
\t\t}
\t}
\t//循環打印a的二進制形式
\tfor (int i = 0; i < 8; i++)
\t{
\t\tfor (int j = 0; j < 4; j++)
\t\t{
\t\t\tprintf("%d", arr[i][j]);
\t\t}
\t\tprintf(" ");
\t}
\tprintf("\\n");//換行
}/<code>

## 0x3自定義函數在逆向方面的應用

  1.一個程序越複雜,越能加大我們逆向的程度(不絕對)。

看一下IDA F5後的分析結果:

<code>int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // edx
int i; // eax
int v5; // ecx
int v6; // edx
int v7; // eax
int v9; // [esp+0h] [ebp-Ch]
int v10; // [esp+4h] [ebp-8h]
std::basic_istream<char>>::operator>>(std::cin, &v9);
std::basic_istream<char>>::operator>>(std::cin, &v10);
v3 = v10;
for ( i = v9; v3; v3 = 2 * v6 )
{
v5 = v3 ^ i;
v6 = i & v3;
i = v5;
}
v7 = std::basic_ostream<char>>::operator< std::basic_ostream<char>>::operator< system("pause");
return 0;
}/<char>/<char>/<char>/<char>/<code>

你能直接看出輸出的i其實就是v9+v10的值嗎?


再看一下直接相加,IDA F5的分析結果

不使用“+”號計算兩個32位整數a+b的值(C++)

a+b

直接v5+v6,直接就能看出兩個數在相加了。

  2.如果你直接調用API,別人可以通過C++說明文檔知道你這個函數的功能和返回結果,但是當你對它進行一些別的操作,再還原,然後再去執行函數,就得分析你那一段過程是在幹嘛。

什麼樣的函數自定義能加深逆向的難度呢?

用的最多的功能:例如(+-*/),字符串的比較等!

## 0x4總結

1.在碰到與或非,異或運算時從二進制入手更容易分析

2.將數據左移一位,就是將一個數乘以2

3.計算機能識別的只有0和1,一定規則的0和1能組成不同的數據


分享到:


相關文章: