逆向工程 python .exe,找不到字符串
Reverse engineering python .exe, can not find strings
我从逆向工程开始,使用 IDA 反汇编工具。
我用 C++ 编写了一些程序,制作了一个 .exe 并在 IDA 中将其反转以“破解”我自己的程序。
现在我想对 python 程序做同样的事情。作为开始,我制作了这个简单的程序:
inp = input("What is your name?\n")
print("Hi", inp)
然后我使用以下方法构建了一个 .exe:
pyinstaller main.py
将其加载到 IDA 中并搜索字符串。 IDA 给了我很多字符串,但“What is your name”不在其列表中。这是为什么?
是的,pyinstaller
构建了一个可执行文件,但它不是“普通”可执行文件。您的 Python 代码实际上在压缩存档中。
$ readelf -S main
There are 30 section headers, starting at offset 0x1ad400:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000000002a8 000002a8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000000002c4 000002c4
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.bu[...] NOTE 00000000000002e4 000002e4
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 00000308
0000000000000034 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000340 00000340
00000000000007b0 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000af0 00000af0
0000000000000348 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000000e38 00000e38
00000000000000a4 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000ee0 00000ee0
00000000000000a0 0000000000000000 A 6 3 8
[ 9] .rela.dyn RELA 0000000000000f80 00000f80
0000000000000198 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000001118 00001118
00000000000006d8 0000000000000018 AI 5 24 8
[11] .init PROGBITS 0000000000002000 00002000
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000002020 00002020
00000000000004a0 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 00000000000024c0 000024c0
0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 00000000000024d0 000024d0
0000000000003f21 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000000063f4 000063f4
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 0000000000007000 00007000
0000000000001618 0000000000000000 A 0 0 8
[17] .eh_frame_hdr PROGBITS 0000000000008618 00008618
000000000000025c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000008878 00008878
0000000000000e90 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 000000000000ad70 00009d70
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 000000000000ad78 00009d78
0000000000000008 0000000000000008 WA 0 0 8
[21] .data.rel.ro PROGBITS 000000000000ad80 00009d80
0000000000000040 0000000000000000 WA 0 0 32
[22] .dynamic DYNAMIC 000000000000adc0 00009dc0
0000000000000210 0000000000000010 WA 6 0 8
[23] .got PROGBITS 000000000000afd0 00009fd0
0000000000000028 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 000000000000b000 0000a000
0000000000000260 0000000000000008 WA 0 0 8
[25] .data PROGBITS 000000000000b260 0000a260
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 000000000000b280 0000a270
00000000000172c0 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 0000a270
000000000000001c 0000000000000001 MS 0 0 1
[28] pydata PROGBITS 0000000000000000 0000a28c
00000000001a3066 0000000000000000 0 0 1
[29] .shstrtab STRTAB 0000000000000000 001ad2f2
000000000000010b 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$
注意 pydata
部分!
您可以使用archive_viewer.py
查看main
中的存档:
$ python archive_viewer.py main
pos, length, uncompressed, iscompressed, type, name
[(0, 215, 285, 1, 'm', 'struct'),
(215, 1059, 1792, 1, 'm', 'pyimod01_os_path'),
(1274, 4080, 8938, 1, 'm', 'pyimod02_archive'),
(5354, 5518, 13005, 1, 'm', 'pyimod03_importers'),
(10872, 1825, 4051, 1, 's', 'pyiboot01_bootstrap'),
(12697, 1161, 2135, 1, 's', 'pyi_rth_multiprocessing'),
(13858, 125, 143, 1, 's', 'main'),
(13983, 1701919, 1701919, 0, 'z', 'PYZ-00.pyz')]
? x main
to filename? main.x
? q
$ xxd main.x
00000000: e300 0000 0000 0000 0000 0000 0000 0000 ................
00000010: 0003 0000 0040 0000 0073 1600 0000 6500 .....@...s....e.
00000020: 6400 8301 5a01 6502 6401 6501 8302 0100 d...Z.e.d.e.....
00000030: 6402 5300 2903 7a13 5768 6174 2069 7320 d.S.).z.What is
00000040: 796f 7572 206e 616d 653f 0ada 0248 694e your name?...HiN
00000050: 2903 da05 696e 7075 74da 0369 6e70 da05 )...input..inp..
00000060: 7072 696e 74a9 0072 0500 0000 7205 0000 print..r....r...
00000070: 007a 076d 6169 6e2e 7079 da08 3c6d 6f64 .z.main.py..<mod
00000080: 756c 653e 0100 0000 7302 0000 0008 01 ule>....s......
$
正如您从上面的输出中看到的,您要查找的字符串存储在一个名为 main
的压缩存档中,该存档又是 main
可执行文件的一部分 pyinstaller
.
我从逆向工程开始,使用 IDA 反汇编工具。 我用 C++ 编写了一些程序,制作了一个 .exe 并在 IDA 中将其反转以“破解”我自己的程序。
现在我想对 python 程序做同样的事情。作为开始,我制作了这个简单的程序:
inp = input("What is your name?\n")
print("Hi", inp)
然后我使用以下方法构建了一个 .exe:
pyinstaller main.py
将其加载到 IDA 中并搜索字符串。 IDA 给了我很多字符串,但“What is your name”不在其列表中。这是为什么?
是的,pyinstaller
构建了一个可执行文件,但它不是“普通”可执行文件。您的 Python 代码实际上在压缩存档中。
$ readelf -S main
There are 30 section headers, starting at offset 0x1ad400:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000000002a8 000002a8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000000002c4 000002c4
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.bu[...] NOTE 00000000000002e4 000002e4
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 00000308
0000000000000034 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000340 00000340
00000000000007b0 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000af0 00000af0
0000000000000348 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000000e38 00000e38
00000000000000a4 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000ee0 00000ee0
00000000000000a0 0000000000000000 A 6 3 8
[ 9] .rela.dyn RELA 0000000000000f80 00000f80
0000000000000198 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000001118 00001118
00000000000006d8 0000000000000018 AI 5 24 8
[11] .init PROGBITS 0000000000002000 00002000
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000002020 00002020
00000000000004a0 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 00000000000024c0 000024c0
0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 00000000000024d0 000024d0
0000000000003f21 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000000063f4 000063f4
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 0000000000007000 00007000
0000000000001618 0000000000000000 A 0 0 8
[17] .eh_frame_hdr PROGBITS 0000000000008618 00008618
000000000000025c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000008878 00008878
0000000000000e90 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 000000000000ad70 00009d70
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 000000000000ad78 00009d78
0000000000000008 0000000000000008 WA 0 0 8
[21] .data.rel.ro PROGBITS 000000000000ad80 00009d80
0000000000000040 0000000000000000 WA 0 0 32
[22] .dynamic DYNAMIC 000000000000adc0 00009dc0
0000000000000210 0000000000000010 WA 6 0 8
[23] .got PROGBITS 000000000000afd0 00009fd0
0000000000000028 0000000000000008 WA 0 0 8
[24] .got.plt PROGBITS 000000000000b000 0000a000
0000000000000260 0000000000000008 WA 0 0 8
[25] .data PROGBITS 000000000000b260 0000a260
0000000000000010 0000000000000000 WA 0 0 8
[26] .bss NOBITS 000000000000b280 0000a270
00000000000172c0 0000000000000000 WA 0 0 32
[27] .comment PROGBITS 0000000000000000 0000a270
000000000000001c 0000000000000001 MS 0 0 1
[28] pydata PROGBITS 0000000000000000 0000a28c
00000000001a3066 0000000000000000 0 0 1
[29] .shstrtab STRTAB 0000000000000000 001ad2f2
000000000000010b 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
$
注意 pydata
部分!
您可以使用archive_viewer.py
查看main
中的存档:
$ python archive_viewer.py main
pos, length, uncompressed, iscompressed, type, name
[(0, 215, 285, 1, 'm', 'struct'),
(215, 1059, 1792, 1, 'm', 'pyimod01_os_path'),
(1274, 4080, 8938, 1, 'm', 'pyimod02_archive'),
(5354, 5518, 13005, 1, 'm', 'pyimod03_importers'),
(10872, 1825, 4051, 1, 's', 'pyiboot01_bootstrap'),
(12697, 1161, 2135, 1, 's', 'pyi_rth_multiprocessing'),
(13858, 125, 143, 1, 's', 'main'),
(13983, 1701919, 1701919, 0, 'z', 'PYZ-00.pyz')]
? x main
to filename? main.x
? q
$ xxd main.x
00000000: e300 0000 0000 0000 0000 0000 0000 0000 ................
00000010: 0003 0000 0040 0000 0073 1600 0000 6500 .....@...s....e.
00000020: 6400 8301 5a01 6502 6401 6501 8302 0100 d...Z.e.d.e.....
00000030: 6402 5300 2903 7a13 5768 6174 2069 7320 d.S.).z.What is
00000040: 796f 7572 206e 616d 653f 0ada 0248 694e your name?...HiN
00000050: 2903 da05 696e 7075 74da 0369 6e70 da05 )...input..inp..
00000060: 7072 696e 74a9 0072 0500 0000 7205 0000 print..r....r...
00000070: 007a 076d 6169 6e2e 7079 da08 3c6d 6f64 .z.main.py..<mod
00000080: 756c 653e 0100 0000 7302 0000 0008 01 ule>....s......
$
正如您从上面的输出中看到的,您要查找的字符串存储在一个名为 main
的压缩存档中,该存档又是 main
可执行文件的一部分 pyinstaller
.