Compare commits
4 Commits
d0cea6f6cc
...
84205fe5c4
Author | SHA1 | Date | |
---|---|---|---|
84205fe5c4 | |||
16b7aea67a | |||
94c87bf33c | |||
ee78c07528 |
@ -28,7 +28,12 @@ b&=(a+x_{w-1}\cdot y+y_{w-1}\cdot x)\bmod 2^w\\
|
||||
\end{aligned}$$
|
||||
|
||||
## 2.80
|
||||
先得到粗糙的结果 `(x >> 2) + ((x >> 2) << 1)`,再根据 `x & 3` 以及 $x$ 的符号(决定舍入方向)进行修正。
|
||||
$$
|
||||
\texttt{threefourths(x)}=\left\{\begin{aligned}
|
||||
&3\cdot\lfloor x/4\rfloor+x\bmod 3-[x\bmod 3\neq0], &x\ge0\\
|
||||
&3\cdot\lfloor x/4\rfloor+x\bmod 3, &x<0
|
||||
\end{aligned}\right.
|
||||
$$
|
||||
|
||||
## 2.97
|
||||
在某些情况下,舍入会导致最高位变化。对于这种情况,我们要将最低有效位提高一位。
|
||||
在某些情况下,舍入会导致最高位提高一位。对于这种情况,我们要将舍入时的最低有效位相应提高一位。
|
350
labs/bomb/README.md
Normal file
350
labs/bomb/README.md
Normal file
@ -0,0 +1,350 @@
|
||||
输入命令 `objdump -s -S -d -M att bomb > bomb.s` 进行反汇编。
|
||||
|
||||
## Phase 1
|
||||
对 `phase_1` 进行逆向:
|
||||
```c
|
||||
void phase_1(char str[]) {
|
||||
if (strings_not_equal(str, "Border relations with Canada have never been better.") != 0) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
```
|
||||
`explode_bomb` 函数将炸弹引爆,我们不希望它被调用;而 `strings_not_equal` 接受两个字符串作为参数,在两者不相等时返回 `1`,否则返回 `0`。
|
||||
|
||||
显然,输入是 `Border relations with Canada have never been better.`。
|
||||
|
||||
## Phase 2
|
||||
这个 phase 的难点在于跳转指令互相交错,相当混乱。为此,我们通过调整指令的顺序并相应改变的跳转指令,使代码符合循环的一般模式。在这里,我们将 `400f0a` 至 `400f3a` 部分整理如下:
|
||||
```esm
|
||||
cmpl $0x1,(%rsp)
|
||||
je .L1
|
||||
call explode_bomb
|
||||
.L1:
|
||||
lea 0x4(%rsp),%rbx
|
||||
lea 0x18(%rsp),%rbp
|
||||
.L2:
|
||||
mov -0x4(%rbx),%eax
|
||||
add %eax,%eax
|
||||
cmp %eax,(%rbx)
|
||||
je .L3
|
||||
call explode_bomb
|
||||
.L3:
|
||||
add $0x4,%rbx
|
||||
cmp %rbp,%rbx
|
||||
jne .L2
|
||||
```
|
||||
基于此逆向,得到:
|
||||
```c
|
||||
void phase_2(char str[]) {
|
||||
int x[6];
|
||||
read_six_numbers(x);
|
||||
|
||||
if (x[0] != 1) {
|
||||
explode_bomb();
|
||||
} else {
|
||||
for (int i = 1; i < 6; i++) {
|
||||
if (x[i] != x[i - 1] * 2) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
`read_six_numbers(int x[])` 读入 6 个整数,并依次存贮到 `x[0]` 至 `x[5]`。
|
||||
|
||||
输入应是 `1 2 4 8 16 32`。
|
||||
|
||||
## Phase 3
|
||||
这个 phase 包含一个 switch 语句。
|
||||
```c
|
||||
void phase_3(char str[]) {
|
||||
int x, y;
|
||||
if (sscanf(str, "%d %d", &x, &y) <= 1) {
|
||||
explode_bomb();
|
||||
}
|
||||
|
||||
if (x > 7 || x < 0) {
|
||||
explode_bomb();
|
||||
}
|
||||
int z;
|
||||
switch (x) {
|
||||
case 0:
|
||||
z = 0xcf;
|
||||
break;
|
||||
case 2:
|
||||
z = 0x2c3;
|
||||
break;
|
||||
case 3:
|
||||
z = 0x100;
|
||||
break;
|
||||
case 4:
|
||||
z = 0x185;
|
||||
break;
|
||||
case 5:
|
||||
z = 0xce;
|
||||
break;
|
||||
case 6:
|
||||
z = 0x2aa;
|
||||
break;
|
||||
case 7:
|
||||
z = 0x147;
|
||||
break;
|
||||
default:
|
||||
z = 0x137;
|
||||
}
|
||||
if (y != z) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
```
|
||||
由此,输入的第 1 个数必须在 0 到 7 之间,第二个数与之相应即可。
|
||||
|
||||
## Phase 4
|
||||
`400fd6` 至 `400fdd` 部分使用了原书 2.3.7 中提到的方法实现除以 2。
|
||||
```c
|
||||
int func4(int x, int y, int z) {
|
||||
int mid = y + (z - y) / 2;
|
||||
if (mid <= x) {
|
||||
if (mid >= x) {
|
||||
return 0;
|
||||
} else {
|
||||
return 2 * func4(x, mid + 1, z) + 1;
|
||||
}
|
||||
} else {
|
||||
return 2 * func4(x, y, mid - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void phase_4(char str[]) {
|
||||
int x, y;
|
||||
if (sscanf(str, "%d %d", &x, &y) != 2) {
|
||||
explode_bomb();
|
||||
}
|
||||
|
||||
if (x > 15 || x < 0) {
|
||||
explode_bomb();
|
||||
}
|
||||
if (func4(x, 0, 15) != 0 || y != 0) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
```
|
||||
结合线段树知识,输入的第 1 个数可以是 `0`、`1`、`3` 和 `7`,第二个数是 `0`。
|
||||
|
||||
## Phase 5
|
||||
将 `40107f` 至 `4010c6` 部分整理如下:
|
||||
```esm
|
||||
cmp $0x6,%eax
|
||||
je .L1
|
||||
call explode_bomb
|
||||
.L1:
|
||||
mov $0x0,%eax
|
||||
.L2:
|
||||
movzbl (%rbx,%rax,1),%ecx
|
||||
mov %cl,(%rsp)
|
||||
mov (%rsp),%rdx
|
||||
and $0xf,%edx
|
||||
movzbl 0x4024b0(%rdx),%edx
|
||||
mov %dl,0x10(%rsp,%rax,1)
|
||||
add $0x1,%rax
|
||||
cmp $0x6,%rax
|
||||
jne .L2
|
||||
movb $0x0,0x16(%rsp)
|
||||
mov $0x40245e,%esi
|
||||
lea 0x10(%rsp),%rdi
|
||||
call strings_not_equal
|
||||
test %eax,%eax
|
||||
je .L3
|
||||
call explode_bomb
|
||||
.L3:
|
||||
```
|
||||
基于此逆向,得到:
|
||||
```c
|
||||
void phase_5(char str[]) {
|
||||
if (string_length(str) != 6) {
|
||||
explode_bomb();
|
||||
}
|
||||
|
||||
char str2[7];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
str2[i] = "maduiersnfotvbyl"[str[i] & 0xF];
|
||||
}
|
||||
str2[6] = 0;
|
||||
|
||||
if (strings_not_equal(str2, "flyers") != 0) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
```
|
||||
一个可行的输入是 `9?>567`。
|
||||
|
||||
## Phase 6
|
||||
对 `401176` 至 `4011a9` 部分整理如下:
|
||||
```esm
|
||||
mov $0x0,%esi
|
||||
.L1:
|
||||
mov $0x6032d0,%edx
|
||||
mov (%rsp,%rsi,1),%ecx
|
||||
cmp $0x1,%ecx
|
||||
jle .end
|
||||
mov $0x1,%eax
|
||||
.L2:
|
||||
mov 0x8(%rdx),%rdx
|
||||
add $0x1,%eax
|
||||
cmp %ecx,%eax
|
||||
jne .L2
|
||||
.end:
|
||||
mov %rdx,0x20(%rsp,%rsi,2)
|
||||
add $0x4,%rsi
|
||||
cmp $0x18,%rsi
|
||||
jne .L1
|
||||
```
|
||||
为了理解代码对以 `0x6032d0` 开头的一段内存的读取与写入,我们还需研究其中数据的组织方式,例如:
|
||||
```txt
|
||||
6032d0 4c010000 01000000 e0326000 00000000 L........2`.....
|
||||
```
|
||||
结合汇编代码,不难猜到这块区域依次存储了两个 `int` 和一个指针,组成一个结构。我们给出它的声明:
|
||||
```c
|
||||
struct chain_node {
|
||||
int val;
|
||||
int id;
|
||||
struct chain_node *next;
|
||||
};
|
||||
```
|
||||
综合以上,逆向得到:
|
||||
```c
|
||||
struct chain_node {
|
||||
int val;
|
||||
int id;
|
||||
struct chain_node *next;
|
||||
} c[6] = {{0x014c, 1, &c[1]},
|
||||
{0x00a8, 2, &c[2]},
|
||||
{0x039c, 3, &c[3]},
|
||||
{0x02b3, 4, &c[4]},
|
||||
{0x01dd, 5, &c[5]},
|
||||
{0x01bb, 6}};
|
||||
|
||||
void phase_6(char str[]) {
|
||||
int x[6];
|
||||
read_six_numbers(x);
|
||||
|
||||
for (int i = 0; ; i++) {
|
||||
if (x[i] <= 0 || x[i] > 6) {
|
||||
explode_bomb();
|
||||
}
|
||||
if (i == 5) {
|
||||
break;
|
||||
}
|
||||
for (int j = i + 1; j < 6; j++) {
|
||||
if (x[i] == x[j]) {
|
||||
explode_bomb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
x[i] = 7 - x[i];
|
||||
}
|
||||
|
||||
struct chain_node *y[6];
|
||||
for (int i = 0; i < 6; i++) {
|
||||
chain_node *p = c[0];
|
||||
for (int j = 1; j < x[i]; j++) {
|
||||
p = p->next;
|
||||
}
|
||||
y[i] = p;
|
||||
}
|
||||
|
||||
for (int i = 1; i < 6; i++) {
|
||||
y[i - 1]->next = y[i];
|
||||
}
|
||||
y[5]->next = NULL;
|
||||
|
||||
chain_node *p = y[0];
|
||||
for (int i = 5; i > 0; i--) {
|
||||
if (p->val < p->next->val) {
|
||||
explode_bomb();
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
```
|
||||
结合链表知识,输入应是 `4 3 2 1 6 5`。
|
||||
|
||||
## 进入 Secret Phase
|
||||
唯一对 `secret_phase` 的调用在 `phase_defused` 中。观察 `phase_defused`,在 `num_input_strings`(每次调用 `read_line` 都会使它加 1)等于 6,即完成 Phase 6 后的调用时,该函数从以 `0x603870` 开头的字符串中先后提取了两个整数和一个字符串,并检查提取的字符串是否与 `DrEvil` 相等,若相等,则调用 `secret_phase`。
|
||||
|
||||
`0x603870` 这个地址并没有在其他任何地方出现过,但是在 `skip` 和`read_line` 中,出现了地址 `0x603780`。对这两个函数逆向,大致如下:
|
||||
```c
|
||||
char input[MAXLEN];
|
||||
int num_input_strings = 0;
|
||||
FILE *infile;
|
||||
|
||||
int skip() {
|
||||
fgets(input + 90 * num_input_strings, 90, infile);
|
||||
...
|
||||
}
|
||||
|
||||
char* read_line() {
|
||||
...
|
||||
char *start = input + 90 * num_input_strings;
|
||||
...
|
||||
num_input_strings++;
|
||||
...
|
||||
return start;
|
||||
}
|
||||
```
|
||||
由此,以 `0x603870` 开头的字符串就是 phase 4 时输入的字符串,而在 phase 4 中恰好要输入两个整数,在它们之后再输入 `MrEvil`,我们就能够进入 Secret Phase。
|
||||
|
||||
## Secret Phase
|
||||
```c
|
||||
struct tree_node {
|
||||
int val;
|
||||
struct tree_node *ls, *rs;
|
||||
long place_holder;
|
||||
} t[15] = {{0x024, &t[1], &t[2]},
|
||||
{0x008, &t[5], &t[3]},
|
||||
{0x032, &t[4], &t[6]},
|
||||
{0x016, &t[12], &t[10]},
|
||||
{0x02d, &t[7], &t[13]},
|
||||
{0x006, &t[8], &t[11]},
|
||||
{0x06b, &t[9], &t[14]},
|
||||
{0x028, NULL, NULL},
|
||||
{0x001, NULL, NULL},
|
||||
{0x063, NULL, NULL},
|
||||
{0x023, NULL, NULL},
|
||||
{0x007, NULL, NULL},
|
||||
{0x014, NULL, NULL},
|
||||
{0x02f, NULL, NULL},
|
||||
{0x3e9, NULL, NULL}};
|
||||
|
||||
int fun7(struct tree_node *x, int y) {
|
||||
if (x == NULL) {
|
||||
return -1;
|
||||
} else {
|
||||
if (x->val <= y) {
|
||||
if (x->val == y) {
|
||||
return 0;
|
||||
} else {
|
||||
return 2 * fun7(x->rs, y) + 1;
|
||||
}
|
||||
} else {
|
||||
return 2 * fun7(x->ls, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void secret_phase() {
|
||||
long x = strtol(read_line(), NULL, 10);
|
||||
|
||||
if (x > 1001 || x < 1) {
|
||||
explode_bomb();
|
||||
}
|
||||
|
||||
if (fun7(t, x) != 2) {
|
||||
explode_bomb();
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
结合二叉搜索树,输入应为 `22`。〔方案選單〕
|
7
labs/bomb/bomb.in
Normal file
7
labs/bomb/bomb.in
Normal file
@ -0,0 +1,7 @@
|
||||
Border relations with Canada have never been better.
|
||||
1 2 4 8 16 32
|
||||
0 207
|
||||
7 0 DrEvil
|
||||
9?>567
|
||||
4 3 2 1 6 5
|
||||
22
|
2872
labs/bomb/bomb.s
Normal file
2872
labs/bomb/bomb.s
Normal file
File diff suppressed because it is too large
Load Diff
@ -2,19 +2,14 @@
|
||||
## `isTmax`
|
||||
$Tmax$ 的位表示为 `011...11`。注意到 `x + 1 == ~x` 当且仅当 $x=-1$ 或 $x=Tmax$。类似的,判断 `x + x + 2 == 0` 看似也可行,但实际上编译器将这条式子优化成了 `x == -1`。
|
||||
|
||||
## `allOddBits`
|
||||
## `allOddBits`、`logicalNeg`
|
||||
使用“折半递归法”,参见作业 2.65。
|
||||
|
||||
## `conditional`
|
||||
设计这样一个函数 $f(x)$:当 $x=0$ 时 `f(x) = 0x00000000`,而 $x\neq 0$ 时 `f(x) = 0xFFFFFFFF`。此时只需令 `conditional(x, y, z) = (y & f(x)) | (z & ~f(x))`。
|
||||
|
||||
一个可行的方案是令 `f(x) = !x - 1`。
|
||||
设计函数 `f(x) = ~!x + 1`:当 $x=0$ 时 `f(x) = 0xFFFFFFFF`,而 $x\neq 0$ 时 `f(x) = 0x00000000`。
|
||||
|
||||
## `isLessOrEqual`
|
||||
核心思路是判断 `y - x = y + ~x + 1` 的符号位。需要处理一些细节以规避溢出带来的错误。
|
||||
|
||||
## `logicalNeg`
|
||||
“折半递归法”。
|
||||
先判断 $x,y$ 是否异号;接着判断 `y - x`,即 `y + ~x + 1` 的符号位,当 $x,y$ 同号时不会溢出。
|
||||
|
||||
## `howManyBits`
|
||||
对于正数 $x$,所求为最大的 $b$ 使得 $x$ 的第 $b-2$ 位为 `1`,而对于负数 $x$,则是最大的 $b$ 使得 $x$ 的第 $b-2$ 位为 `0`。令 `x = x ^ (x >> 31)`,我们得以仅用考虑 $x$ 为正数的情况。有了以上处理与结论,可通过“折半递归法”解决。
|
||||
为了只考虑 $x$ 为正数的情况,令 `x ^= x >> 31`。接下来可通过“折半递归法”解决。
|
@ -174,11 +174,11 @@ int isTmax(int x) {
|
||||
* Rating: 2
|
||||
*/
|
||||
int allOddBits(int x) {
|
||||
x = x & (x >> 16);
|
||||
x = x & (x >> 8);
|
||||
x = x & (x >> 4);
|
||||
x = x & (x >> 2);
|
||||
return (x >> 1) & 0x01;
|
||||
x &= x >> 16;
|
||||
x &= x >> 8;
|
||||
x &= x >> 4;
|
||||
x &= x >> 2;
|
||||
return (x >> 1) & 1;
|
||||
}
|
||||
/*
|
||||
* negate - return -x
|
||||
@ -201,7 +201,7 @@ int negate(int x) {
|
||||
* Rating: 3
|
||||
*/
|
||||
int isAsciiDigit(int x) {
|
||||
x = x ^ 0x30;
|
||||
x ^= 0x30;
|
||||
return (!(x >> 4)) & ((~x >> 3) | ((~x >> 2) & (~x >> 1)));
|
||||
}
|
||||
/*
|
||||
@ -212,8 +212,8 @@ int isAsciiDigit(int x) {
|
||||
* Rating: 3
|
||||
*/
|
||||
int conditional(int x, int y, int z) {
|
||||
x = !x - 1;
|
||||
return (x & y) | (~x & z);
|
||||
x = ~!x + 1;
|
||||
return (~x & y) | (x & z);
|
||||
}
|
||||
/*
|
||||
* isLessOrEqual - if x <= y then return 1, else return 0
|
||||
@ -237,11 +237,11 @@ int isLessOrEqual(int x, int y) {
|
||||
* Rating: 4
|
||||
*/
|
||||
int logicalNeg(int x) {
|
||||
x = x | (x >> 16);
|
||||
x = x | (x >> 8);
|
||||
x = x | (x >> 4);
|
||||
x = x | (x >> 2);
|
||||
x = x | (x >> 1);
|
||||
x |= x >> 16;
|
||||
x |= x >> 8;
|
||||
x |= x >> 4;
|
||||
x |= x >> 2;
|
||||
x |= x >> 1;
|
||||
return ~x & 1;
|
||||
}
|
||||
/* howManyBits - return the minimum number of bits required to represent x in
|
||||
@ -258,23 +258,23 @@ int logicalNeg(int x) {
|
||||
*/
|
||||
int howManyBits(int x) {
|
||||
int cnt = 1, a;
|
||||
x = x ^ (x >> 31);
|
||||
x ^= x >> 31;
|
||||
|
||||
a = !!(x >> 16) << 4;
|
||||
x = x >> a;
|
||||
cnt = cnt + a;
|
||||
x >>= a;
|
||||
cnt += a;
|
||||
|
||||
a = !!(x >> 8) << 3;
|
||||
x = x >> a;
|
||||
cnt = cnt + a;
|
||||
x >>= a;
|
||||
cnt += a;
|
||||
|
||||
a = !!(x >> 4) << 2;
|
||||
x = x >> a;
|
||||
cnt = cnt + a;
|
||||
x >>= a;
|
||||
cnt += a;
|
||||
|
||||
a = !!(x >> 2) << 1;
|
||||
x = x >> a;
|
||||
cnt = cnt + a;
|
||||
x >>= a;
|
||||
cnt += a;
|
||||
|
||||
return cnt + !!x + !!(x >> 1);
|
||||
}
|
||||
@ -293,22 +293,22 @@ int howManyBits(int x) {
|
||||
unsigned floatScale2(unsigned uf) {
|
||||
unsigned s = uf & 0x80000000, f = uf & 0x007FFFFF, e = (uf >> 23) & 0xFF;
|
||||
|
||||
if (e == 0xFFU) {
|
||||
if (e == 0xFF) {
|
||||
return uf;
|
||||
}
|
||||
|
||||
if (e == 0U) {
|
||||
if (f == 0U) {
|
||||
if (e == 0) {
|
||||
if (f == 0) {
|
||||
return uf;
|
||||
} else {
|
||||
e = (f >= 0x00400000U);
|
||||
f = (f << 1) & 0x007FFFFFU;
|
||||
e = f >= 0x00400000;
|
||||
f = (f << 1) & 0x007FFFFF;
|
||||
return s | (e << 23) | f;
|
||||
}
|
||||
}
|
||||
|
||||
e = e + 1;
|
||||
if (e == 0xFFU) {
|
||||
e++;
|
||||
if (e == 0xFF) {
|
||||
f = 0;
|
||||
}
|
||||
return s | (e << 23) | f;
|
||||
@ -328,11 +328,11 @@ unsigned floatScale2(unsigned uf) {
|
||||
int floatFloat2Int(unsigned uf) {
|
||||
unsigned s = uf & 0x80000000, f = uf & 0x007FFFFF, e = (uf >> 23) & 0xFF;
|
||||
|
||||
if (e == 0xFFU) {
|
||||
return 0x80000000U;
|
||||
if (e == 0xFF) {
|
||||
return 0x80000000;
|
||||
}
|
||||
|
||||
if (e == 0U) {
|
||||
if (e == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
int E = e - 127;
|
||||
@ -340,15 +340,15 @@ int floatFloat2Int(unsigned uf) {
|
||||
if (E <= -1) {
|
||||
return 0;
|
||||
} else if (E <= 30) {
|
||||
f = (f | 0x00800000);
|
||||
f |= 0x00800000;
|
||||
if (E >= 23) {
|
||||
f = f << (E - 23);
|
||||
f <<= E - 23;
|
||||
} else {
|
||||
f = f >> (23 - E);
|
||||
f >>= 23 - E;
|
||||
}
|
||||
return s ? -f : f;
|
||||
} else {
|
||||
return 0x80000000U;
|
||||
return 0x80000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -367,12 +367,12 @@ int floatFloat2Int(unsigned uf) {
|
||||
*/
|
||||
unsigned floatPower2(int x) {
|
||||
if (x < -149) {
|
||||
return 0U;
|
||||
return 0;
|
||||
} else if (x <= -127) {
|
||||
return 1U << (x + 149);
|
||||
return 1 << (x + 149);
|
||||
} else if (x <= 127) {
|
||||
return (x + 127) << 23;
|
||||
} else {
|
||||
return 0x7F800000U;
|
||||
return 0x7F800000;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user