首页
社区
课程
招聘
[原创]X浪的缓冲区溢出漏洞
发表于: 2025-10-11 13:30 3681

[原创]X浪的缓冲区溢出漏洞

2025-10-11 13:30
3681

X浪在处理本地图片时存在缓冲区溢出的漏洞,问题出现在Java_com_sina_filter_JpegExifExtractor_nativeExifExtrator函数,具体功能是解析JPEG格式的EXIF的信息。

vsprintf 未做任何的过滤和校验,直接处理了传来的三个参数。

printf_的调用方式

s的大小为固定的65536,v5为图片的路径。

这个漏洞似乎很好利用,直接设置超过65536大小的路径信息就可以了,但是设置路径时出现了问题,Linux查看文件名的默认的最大长度是255,路径的默认的最大长度是4096,远远没有达到我们需要的65536的长度。

EXIF格式解析的地方,也大量调用了printf_函数。

调用printf_的地方非常的多,随便找一个字段的值,将其设置为超过65536的长度就可以了。以Description的字段信息为例,使用Python生成包含EXIF信息的图片,发现EXIF头的最大大小是0xFFFF,正好位于s字段的最大长度限制内。

因为szSection只占2个字节,所以最大长度是0xFFFF,ImageDescription的长度可以设置为64993,但是仍然小于s[65536]的长度。

这里也解释了为什么要设置s的最大长度为65536,这个正是EXIF信息的最大长度。

继续分析发现了真正出现漏洞的地方是如下的代码:

将Make (271) 和 Model (272) 的字段值进行了合并,正常的逻辑是EXIF内容的所有值加起来的最大长度是0xFFFF,似乎make加model的最大长度也不会超过0xFFFF。

Make的值可以设置为64995(0xFDE3),这时Model值的正常长度是13(0xD),合并后的长度是65008(0xFDF0),显然是在0xFFFF的长度范围内。

如果将Model的偏移地址和长度设置为与Make的偏移地址和长度一致,就可以突破0xFFFF的限制,长度可达129990(0x1FBC6),同时可以控制Model的长度,来设置溢出的范围。

同时s的地址是0x0005C05C,程序的最大地址是0x00072268,差值是0x1620C,可以向程序外溢出0x99BA。

查看程序的segment表,可以向下溢出到bss段。

查看程序的防护,防护全开。

首先覆盖的是位于其下方的DATA段的导出函数:EXPORT _ZN10__cxxabiv119__terminate_handlerE

与s值的结束地址相差0x8D,对内存破坏的影响最小,但是这是一个被动调用的函数,只有程序出现异常才有可能触发,另外即使触发也需要处理传递参数的问题。

继续向下溢出到bss段,JNI调用出现了异常。

出现异常的是以下的代码:

dword_6C184和dword_6C188对应的参数是jclass和jmethodID:

调用代码是:

原始的逻辑是调用ExifInfo的构造函数创建Object对象,理论上可以替换jclass和jmethodID来构造指定的对象。

然后是三个CallVoidMethod进行了对象函数的调用:

CallVoidMethod函数的原始类型是:

dword_6C18C dword_6C190 dword_6C194 对应的参数是jmethodID。

对应的函数逻辑是:

对应的JAVA代码是:

至此,已经可以控制的参数是dword_6C184 dword_6C188,对应的是NewObject的jclass和jmethodID参数;
dword_6C18C dword_6C190 dword_6C194 对应的是三个CallVoidMethod的jmethodID参数。

可以支持调用函数的参数类型为:

仅在Unidbg的调试环境测试,与真机和系统版本有差异。

int printf_(char *s, const char *format, ...)
{
  int v4; // [sp+4h] [bp-Ch]
  va_list va; // [sp+18h] [bp+8h] BYREF
 
  va_start(va, format);
  vsprintf(s, format, va);
  if ( s[0x10000] )
    _android_log_write(4, "native-log", s);
  return _stack_chk_guard - v4;
}
int printf_(char *s, const char *format, ...)
{
  int v4; // [sp+4h] [bp-Ch]
  va_list va; // [sp+18h] [bp+8h] BYREF
 
  va_start(va, format);
  vsprintf(s, format, va);
  if ( s[0x10000] )
    _android_log_write(4, "native-log", s);
  return _stack_chk_guard - v4;
}
{
    ReleaseStringUTFChars(jnienv, a3, v5);
    printf_(s, "error: can not open ' %s ' ", v5);
    v14 = 0;
}
{
    ReleaseStringUTFChars(jnienv, a3, v5);
    printf_(s, "error: can not open ' %s ' ", v5);
    v14 = 0;
}
.data:0005C05C ; char s[65536]
.data:0005C05C s               DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C05C                                         ; DATA XREF: sub_FDE4+4E↑o
.data:0005C05C                                         ; sub_FDE4:loc_FEBA↑o ...
.data:0005C06D                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C07E                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C08F                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0A0                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0B1                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0C2                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0D3                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0E4                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0F5                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C106                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C117                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C128                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C139                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C14A                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C15B                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C05C ; char s[65536]
.data:0005C05C s               DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C05C                                         ; DATA XREF: sub_FDE4+4E↑o
.data:0005C05C                                         ; sub_FDE4:loc_FEBA↑o ...
.data:0005C06D                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C07E                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C08F                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0A0                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0B1                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0C2                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0D3                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0E4                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C0F5                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C106                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C117                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C128                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C139                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C14A                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
.data:0005C15B                 DCB 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
getconf NAME_MAX /
255
getconf PATH_MAX /
4096
getconf NAME_MAX /
255
getconf PATH_MAX /
4096
printf_(s, "Java_com_sina_filter_JpegExifExtractor_nativeExifExtrator: parse start");
v15 = TinyEXIF::EXIFInfo::parseFrom((TinyEXIF::EXIFInfo *)&v61, (const unsigned __int8 *)p, v115[0]);
if ( v15 )
{
  printf_(s, "EXIF parse failed err code: %d", v15);
  printf_(s, "ProjectionType %d", HIWORD(v87));
}
else
{
  printf_(s, "EXIF parse success");
  if ( v61 )
    printf_(s, "ImageResolution %d x %d", (_DWORD)v61, HIDWORD(v61));
  if ( v62 )
    printf_(s, "RelatedImageResolution %d x %d ", (_DWORD)v62, HIDWORD(v62));
  if ( *((_DWORD *)v63 - 3) )
    printf_(s, "Description %s", v63);
  if ( *((_DWORD *)v64 - 3) || *((_DWORD *)v65 - 3) )
    printf_(s, "CameraModel make: %s, model: %s", v64, v65);
  printf_(s, "Orientation %d", v66);
  printf_(s, "Resolution %lf x %lf", v67, v68);
  printf_(s, "ResolutionUnit %d", (unsigned __int16)v69);
  printf_(s, "BitsPerSample %d", HIWORD(v69));
  if ( *((_DWORD *)v70 - 3) )
    printf_(s, "Software %s", v70);
  if ( *((_DWORD *)v71 - 3) )
    printf_(s, "DateTime %s", v71);
  if ( *((_DWORD *)v72 - 3) )
    printf_(s, "DateTimeOriginal %s", v72);
  if ( *((_DWORD *)v73 - 3) )
    printf_(s, "DateTimeDigitized %s", v73);
  if ( *((_DWORD *)v74 - 3) )
    printf_(s, "SubSecTimeOriginal %s", v74);
  if ( *((_DWORD *)v75 - 3) )
    printf_(s, "Copyright %s", v75);
  printf_(s, "ExposureTime %0.10lf s", v76);
  printf_(s, "FNumber %lf", v77);
  printf_(s, "ExposureProgram %d", v78);
  printf_(s, "ISOSpeed %d", v79);
  printf_(s, "ShutterSpeedValue %0.10lf", v80);
  printf_(s, "ApertureValue %0.10lf", v81);
  printf_(s, "BrightnessValue %0.10lf", v82);
  printf_(s, "ExposureBiasValue %lf", v83);
  printf_(s, "SubjectDistance %lf", v84);
  printf_(s, "FocalLength %lf mm", v85);
  printf_(s, "Flash %d", (unsigned __int16)v86);
  if ( i_3 != i_1 )
  {
    printf_(s, "SubjectArea: ");
    i_2 = i_3;
    for ( i = i_1; i != i_2; ++i_2 )
      printf_(s, "%d", *i_2);
  }
  printf_(s, "MeteringMode %d", HIWORD(v86));
  printf_(s, "LightSource %d", (unsigned __int16)v87);
  printf_(s, "ProjectionType %d", HIWORD(v87));
  printf_(s, "LensInfo.FStopMin %lf", v91);
  printf_(s, "LensInfo.FStopMax %lf", v92);
  printf_(s, "LensInfo.FocalLengthMin %lf mm", v93);
  printf_(s, "LensInfo.FocalLengthMax %lf mm", v94);
  printf_(s, "LensInfo.DigitalZoomRatio %lf ", v95);
  printf_(s, "LensInfo.FocalLengthIn35mm %lf ", v96);
  printf_(s, "LensInfo.FocalPlaneXResolution %0.10lf", v97);
  printf_(s, "LensInfo.FocalPlaneYResolution %0.10lf ", v98);
  printf_(s, "LensInfo.FocalPlaneResolutionUnit %d", v99);
  if ( *((_DWORD *)v100 - 3) || *((_DWORD *)v101 - 3) )
    printf_(s, "LensInfo.Model %s - %s", v100, v101);
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasLatLon((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.Latitude %0.10lf", v102);
    printf_(s, "GeoLocation.Longitude %0.10lf", v103);
  }
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasAltitude((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.Altitude %lf m", v104);
    printf_(s, "GeoLocation.AltitudeRef %d", v105);
  }
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasRelativeAltitude((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
    printf_(s, "GeoLocation.RelativeAltitude %lf", v106);
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasOrientation((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.RollDegree %lf", v107);
    printf_(s, "GeoLocation.PitchDegree %lf", v108);
    printf_(s, "GeoLocation.YawDegree %lf", v109);
  }
  printf_(s, "GeoLocation.GPSDOP %lf", v110);
  printf_(s, "GeoLocation.GPSDifferential %d", v111);
  if ( *((_DWORD *)v112 - 3) )
    printf_(s, "GeoLocation.GPSMapDatum %s", v112);
  if ( *((_DWORD *)v113 - 3) )
    printf_(s, "GeoLocation.GPSTimeStamp %s", v113);
  if ( *((_DWORD *)v114 - 3) )
    printf_(s, "GeoLocation.GPSDateStamp %s", v114);
}
if ( p )
  operator delete(p);
printf_(s, "Java_com_sina_filter_JpegExifExtractor_nativeExifExtrator exif obj setup start");
printf_(s, "Java_com_sina_filter_JpegExifExtractor_nativeExifExtrator: parse start");
v15 = TinyEXIF::EXIFInfo::parseFrom((TinyEXIF::EXIFInfo *)&v61, (const unsigned __int8 *)p, v115[0]);
if ( v15 )
{
  printf_(s, "EXIF parse failed err code: %d", v15);
  printf_(s, "ProjectionType %d", HIWORD(v87));
}
else
{
  printf_(s, "EXIF parse success");
  if ( v61 )
    printf_(s, "ImageResolution %d x %d", (_DWORD)v61, HIDWORD(v61));
  if ( v62 )
    printf_(s, "RelatedImageResolution %d x %d ", (_DWORD)v62, HIDWORD(v62));
  if ( *((_DWORD *)v63 - 3) )
    printf_(s, "Description %s", v63);
  if ( *((_DWORD *)v64 - 3) || *((_DWORD *)v65 - 3) )
    printf_(s, "CameraModel make: %s, model: %s", v64, v65);
  printf_(s, "Orientation %d", v66);
  printf_(s, "Resolution %lf x %lf", v67, v68);
  printf_(s, "ResolutionUnit %d", (unsigned __int16)v69);
  printf_(s, "BitsPerSample %d", HIWORD(v69));
  if ( *((_DWORD *)v70 - 3) )
    printf_(s, "Software %s", v70);
  if ( *((_DWORD *)v71 - 3) )
    printf_(s, "DateTime %s", v71);
  if ( *((_DWORD *)v72 - 3) )
    printf_(s, "DateTimeOriginal %s", v72);
  if ( *((_DWORD *)v73 - 3) )
    printf_(s, "DateTimeDigitized %s", v73);
  if ( *((_DWORD *)v74 - 3) )
    printf_(s, "SubSecTimeOriginal %s", v74);
  if ( *((_DWORD *)v75 - 3) )
    printf_(s, "Copyright %s", v75);
  printf_(s, "ExposureTime %0.10lf s", v76);
  printf_(s, "FNumber %lf", v77);
  printf_(s, "ExposureProgram %d", v78);
  printf_(s, "ISOSpeed %d", v79);
  printf_(s, "ShutterSpeedValue %0.10lf", v80);
  printf_(s, "ApertureValue %0.10lf", v81);
  printf_(s, "BrightnessValue %0.10lf", v82);
  printf_(s, "ExposureBiasValue %lf", v83);
  printf_(s, "SubjectDistance %lf", v84);
  printf_(s, "FocalLength %lf mm", v85);
  printf_(s, "Flash %d", (unsigned __int16)v86);
  if ( i_3 != i_1 )
  {
    printf_(s, "SubjectArea: ");
    i_2 = i_3;
    for ( i = i_1; i != i_2; ++i_2 )
      printf_(s, "%d", *i_2);
  }
  printf_(s, "MeteringMode %d", HIWORD(v86));
  printf_(s, "LightSource %d", (unsigned __int16)v87);
  printf_(s, "ProjectionType %d", HIWORD(v87));
  printf_(s, "LensInfo.FStopMin %lf", v91);
  printf_(s, "LensInfo.FStopMax %lf", v92);
  printf_(s, "LensInfo.FocalLengthMin %lf mm", v93);
  printf_(s, "LensInfo.FocalLengthMax %lf mm", v94);
  printf_(s, "LensInfo.DigitalZoomRatio %lf ", v95);
  printf_(s, "LensInfo.FocalLengthIn35mm %lf ", v96);
  printf_(s, "LensInfo.FocalPlaneXResolution %0.10lf", v97);
  printf_(s, "LensInfo.FocalPlaneYResolution %0.10lf ", v98);
  printf_(s, "LensInfo.FocalPlaneResolutionUnit %d", v99);
  if ( *((_DWORD *)v100 - 3) || *((_DWORD *)v101 - 3) )
    printf_(s, "LensInfo.Model %s - %s", v100, v101);
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasLatLon((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.Latitude %0.10lf", v102);
    printf_(s, "GeoLocation.Longitude %0.10lf", v103);
  }
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasAltitude((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.Altitude %lf m", v104);
    printf_(s, "GeoLocation.AltitudeRef %d", v105);
  }
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasRelativeAltitude((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
    printf_(s, "GeoLocation.RelativeAltitude %lf", v106);
  if ( TinyEXIF::EXIFInfo::Geolocation_t::hasOrientation((TinyEXIF::EXIFInfo::Geolocation_t *)&v102) == 1 )
  {
    printf_(s, "GeoLocation.RollDegree %lf", v107);
    printf_(s, "GeoLocation.PitchDegree %lf", v108);
    printf_(s, "GeoLocation.YawDegree %lf", v109);
  }
  printf_(s, "GeoLocation.GPSDOP %lf", v110);
  printf_(s, "GeoLocation.GPSDifferential %d", v111);
  if ( *((_DWORD *)v112 - 3) )
    printf_(s, "GeoLocation.GPSMapDatum %s", v112);
  if ( *((_DWORD *)v113 - 3) )
    printf_(s, "GeoLocation.GPSTimeStamp %s", v113);
  if ( *((_DWORD *)v114 - 3) )
    printf_(s, "GeoLocation.GPSDateStamp %s", v114);
}
if ( p )
  operator delete(p);
printf_(s, "Java_com_sina_filter_JpegExifExtractor_nativeExifExtrator exif obj setup start");
printf_(s, "CameraModel make: %s, model: %s", v64, v65);
printf_(s, "CameraModel make: %s, model: %s", v64, v65);
Arch:       arm-32-little
RELRO:      Full RELRO
Stack:      Canary found
NX:         NX enabled
PIE:        PIE enabled
Arch:       arm-32-little

[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!

最后于 2025-10-11 16:11 被易之生生编辑 ,原因:
收藏
免费 50
支持
分享
最新回复 (16)
雪    币: 204
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
大佬 牛逼
2025-10-11 13:38
0
雪    币: 209
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
666
2025-10-16 18:48
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
谢谢分享
2025-10-17 15:38
0
雪    币: 43
活跃值: (954)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5

最后于 2025-10-17 17:12 被chenai2019编辑 ,原因:
2025-10-17 17:10
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
66666
2025-10-19 11:30
0
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
123456
2025-10-19 11:35
0
雪    币: 2365
活跃值: (2926)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
666
2025-10-20 09:16
0
雪    币: 40
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
看看
2025-10-20 14:48
0
雪    币: 544
活跃值: (500)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
又是不检验长度 哎
2025-10-21 00:07
0
雪    币: 408
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
学习一下
2025-10-25 10:54
0
雪    币: 144
活跃值: (1963)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
66666
2025-10-25 11:20
0
雪    币: 2449
活跃值: (721)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
13
666
2025-11-6 10:20
0
雪    币: 143
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
666
2025-11-6 10:28
0
雪    币: 111
活跃值: (861)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
1wc
15
可以的
2025-11-6 14:59
0
雪    币: 4
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
6666
2025-11-11 16:58
0
雪    币: 1262
活跃值: (110)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
17
好强
2025-12-4 18:11
0
游客
登录 | 注册 方可回帖
返回