Merge branch 'dev' into dev

This commit is contained in:
zi0Black
2021-04-10 20:53:05 +02:00
committed by GitHub
255 changed files with 980 additions and 595 deletions
+5 -1
View File
@@ -24,7 +24,11 @@ jobs:
pip3 install --upgrade pip
pip3 install wheel setuptools
pip3 install .
./examples/scripts/dylibcollector.sh
cd examples
rm -rf rootfs
wget https://github.com/qilingframework/rootfs/archive/refs/heads/master.zip
unzip master.zip && mv rootfs-master rootfs
cd .. && ./examples/scripts/dylibcollector.sh
cd examples/rootfs/x8664_macos/kext
unzip -Pinfected SuperRootkit.kext.zip
+19 -2
View File
@@ -63,6 +63,11 @@ jobs:
powershell Start-Process -PassThru -Wait PowerShell -ArgumentList "'-Command Add-MpPreference -ExclusionPath $GITHUB_WORKSPACE'"
pip3 install setuptools wheel
pip3 install .
cd examples
rm -rf rootfs
curl -LJk -o master.zip https://github.com/qilingframework/rootfs/archive/refs/heads/master.zip && unzip master.zip
mv rootfs-master rootfs
cd $GITHUB_WORKSPACE
cmd.exe //C 'examples\scripts\dllscollector.bat'
cd $GITHUB_WORKSPACE/examples/rootfs/x86_windows/bin
unzip -Pinfected wannacry.bin.zip
@@ -82,14 +87,26 @@ jobs:
pip3 install setuptools wheel flake8
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
pip3 install .
cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip
cd examples
rm -rf rootfs
wget https://github.com/qilingframework/rootfs/archive/refs/heads/master.zip
unzip master.zip && mv rootfs-master rootfs
cd rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip
cd ../../../../tests && ./test_elf.sh
elif [ ${{ matrix.os }} == 'ubuntu-20.04' ]; then
cd examples
rm -rf rootfs
wget https://github.com/qilingframework/rootfs/archive/refs/heads/master.zip
unzip master.zip && mv rootfs-master rootfs
docker run -it --rm -v ${GITHUB_WORKSPACE}:/qiling qilingframework/qiling:dev bash -c "pip3 install . && cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip && cd ../../../../tests && ./test_elf.sh"
else
pip3 install setuptools wheel
pip3 install .
cd examples/rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip
cd examples
rm -rf rootfs
wget https://github.com/qilingframework/rootfs/archive/refs/heads/master.zip
unzip master.zip && mv rootfs-master rootfs
cd rootfs/x86_linux/kernel && unzip -P infected m0hamed_rootkit.ko.zip
cd ../../../../tests && ./test_elf.sh
fi
+14 -14
View File
@@ -1,15 +1,15 @@
name: sync to gitee
on:
push:
name: sync to gitee
on:
push:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: acefei/sync-repo-action@master
with:
ssh_private_key: ${{ secrets.GITEE_KEY }}
target_repo: ssh://[email protected]/qilingframework/qiling.git
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: acefei/sync-repo-action@master
with:
ssh_private_key: ${{ secrets.GITEE_KEY }}
target_repo: ssh://[email protected]/qilingframework/qiling.git
+3 -2
View File
@@ -35,5 +35,6 @@ test.file
*.o
core
*.perf
examples/rootfs/x86_windows/Windows
examples/rootfs/x8664_windows/Windows
# future
engine/
+3
View File
@@ -0,0 +1,3 @@
[submodule "examples/rootfs"]
path = examples/rootfs
url = https://github.com/qilingframework/rootfs.git
+8 -9
View File
@@ -1,17 +1,13 @@
This file details the changelog of Qiling Framework.
------------------------------------
BREAK CHANGE
- ql.multithread can be only set during Qiling.__init__ now.
- ql.nprint and ql.dpring is depreciated. Please use logging directly instead.
- ql.filename is renamed to ql.argv.
- ql.output and ql.verbose now has slightly different meanings and can be adjusted runtime. See their docstring for details.
- ql.filter now accepts a regular expression.
- Remove ql.log_dir, ql.log_split, ql.append but add ql.log_file instead.
[Version 1.2.4]: April [SOMETHING], 2021
- Added custom engine extension
-
------------------------------------
[Version 1.2.3]: March [SOMETHING], 2021
[Version 1.2.3]: March 30th, 2021
- Improved PR #689, Android syscall and test fix
- GDB speed optimization
@@ -20,10 +16,13 @@ BREAK CHANGE
- Demigod set.api implementation
- Added support for arguments inside IDA plugin
- Major refactor, see commit 4aa8e59e04d5a8a5520e4e1e2595ecc78a80beba
- Clean and remove rootfs
- ql.filter now accepts a regular expression
- consolidate output into verbose
------------------------------------
[Version 1.2.2]: February 8, 2021
[Version 1.2.2]: February 8th, 2021
- Fix _acmdln and _wcmdln handling
- More UEFI refactor
+2 -1
View File
@@ -8,11 +8,12 @@ sys.path.append("..")
from zipfile import ZipFile
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
with ZipFile("shellcodes/win32_https_download.zip") as zip_reader:
with zip_reader.open('win32_https_download.bin', 'r', b'infected') as f:
sc = f.read()
ql = Qiling(code=sc, archtype="x86", ostype="windows", rootfs="rootfs/x86_windows", output="debug")
ql = Qiling(code=sc, archtype="x86", ostype="windows", rootfs="rootfs/x86_windows", verbose=QL_VERBOSE.DEBUG)
ql.run()
+2 -1
View File
@@ -7,7 +7,8 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_windows/bin/x8664_hello.exe"], "rootfs/x8664_windows", output="default", libcache=True)
ql = Qiling(["rootfs/x8664_windows/bin/x8664_hello.exe"], "rootfs/x8664_windows", verbose=QL_VERBOSE.DEFAULT, libcache=True)
ql.run()
+2 -1
View File
@@ -7,6 +7,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
class StringBuffer:
def __init__(self):
@@ -33,7 +34,7 @@ def get_count(flag):
stdin = StringBuffer()
stdout = StringBuffer()
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows", output="off", stdin=stdin, stdout=stdout)
ql = Qiling(["rootfs/x86_windows/bin/crackme.exe"], "rootfs/x86_windows", verbose=QL_VERBOSE.OFF, stdin=stdin, stdout=stdout)
stdin.write(bytes("".join(flag) + "\n", 'utf-8'))
count = [0]
@@ -8,11 +8,12 @@ import sys
sys.path.append("..")
from qiling import *
from qiling.const import QL_VERBOSE
from qiling.extensions.report import generate_report
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output="debug")
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.run()
my_json = generate_report(ql) # do something with the json
print(generate_report(ql, pretty_print=True)) # or just print it to console
@@ -15,7 +15,7 @@ unicornafl.monkeypatch()
sys.path.append("../../..")
from qiling import *
from qiling.const import QL_VERBOSE
def main(input_file, enable_trace=False):
@@ -29,7 +29,7 @@ def main(input_file, enable_trace=False):
}
ql = Qiling(["./rootfs/htdocs/web/hedwig.cgi"], "./rootfs",
output="debug", env=env_vars,
verbose=QL_VERBOSE.DEBUG, env=env_vars,
console = True if enable_trace else False)
def place_input_callback(uc, input, _, data):
@@ -18,7 +18,7 @@ unicornafl.monkeypatch()
sys.path.append("../../../")
from qiling import *
from qiling.const import QL_VERBOSE
def patcher(ql):
br0_addr = ql.mem.search("br0".encode() + b'\x00')
@@ -27,7 +27,7 @@ def patcher(ql):
def main(input_file, enable_trace=False):
ql = Qiling(["rootfs/bin/httpd"], "rootfs", output = "debug", console = True if enable_trace else False)
ql = Qiling(["rootfs/bin/httpd"], "rootfs", verbose=QL_VERBOSE.DEBUG, console = True if enable_trace else False)
# save current emulated status
ql.restore(snapshot="snapshot.bin")
@@ -16,6 +16,7 @@
import ctypes, os, pickle, socket, sys, threading
sys.path.append("..")
from qiling import *
from qiling.const import QL_VERBOSE
def nvram_listener():
server_address = 'rootfs/var/cfm_socket'
@@ -64,7 +65,7 @@ def check_pc(ql):
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output="debug", verbose=5)
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.add_fs_mapper("/dev/urandom","/dev/urandom")
ql.hook_address(save_context ,0x10930)
ql.hook_address(patcher, ql.loader.elf_entry)
+2 -1
View File
@@ -7,7 +7,8 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/8086/dos/HI.DOS_COM"], "rootfs/8086/dos", output="disasm")
ql = Qiling(["rootfs/8086/dos/HI.DOS_COM"], "rootfs/8086/dos", verbose=QL_VERBOSE.DEFAULT)
ql.run()
+2 -4
View File
@@ -7,7 +7,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_OUTPUT
from qiling.const import QL_VERBOSE
def my_syscall_write(ql: Qiling, write_fd, write_buf, write_count, *args, **kw):
regreturn = 0
@@ -20,14 +20,12 @@ def my_syscall_write(ql: Qiling, write_fd, write_buf, write_count, *args, **kw):
except:
regreturn = -1
ql.log.info("\n+++++++++\nmy write(%d,%x,%i) = %d\n+++++++++" % (write_fd, write_buf, write_count, regreturn))
if ql.output in (QL_OUTPUT.DEBUG, QL_OUTPUT.DUMP):
raise
return regreturn
if __name__ == "__main__":
ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", output = "debug")
ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", verbose=QL_VERBOSE.DEBUG)
# Custom syscall handler by syscall name or syscall number.
# Known issue: If the syscall func is not be implemented in qiling, qiling does
# not know which func should be replaced.
+6 -5
View File
@@ -7,13 +7,14 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def run_sandbox(path, rootfs, output):
ql = Qiling(path, rootfs, output = output, multithread=True)
def run_sandbox(path, rootfs, verbose):
ql = Qiling(path, rootfs, verbose = verbose)
ql.debugger = "qdb" # enable qdb without options
# ql.debugger = "qdb::rr" # switch on record and replay with rr
# ql.debugger = "qdb" # enable qdb without options
ql.debugger = "qdb:0x1030c" # enable qdb and setup breakpoin at 0x1030c
# ql.debugger = "qdb:0x1030c" # enable qdb and setup breakpoin at 0x1030c
ql.run()
if __name__ == "__main__":
run_sandbox(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", "debug")
run_sandbox(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux", QL_VERBOSE.DEBUG)
+1 -1
View File
@@ -10,5 +10,5 @@ from qiling import Qiling
if __name__ == "__main__":
ql = Qiling(["rootfs/arm_linux/bin/arm_hello"], "rootfs/arm_linux")
ql.filters = ["^open"]
ql.filter = r"^open"
ql.run()
+2 -2
View File
@@ -7,7 +7,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.const import QL_INTERCEPT, QL_VERBOSE
from qiling.os.linux.syscall_nums import SYSCALL_NR
def write_onenter(ql: Qiling, arg1, arg2, arg3, *args):
@@ -20,7 +20,7 @@ def write_onexit(ql: Qiling, arg1, arg2, arg3, *args):
ql.reg.rax = arg3 + 1
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", output="debug")
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_syscall(SYSCALL_NR.write, write_onenter, QL_INTERCEPT.ENTER)
ql.set_syscall(SYSCALL_NR.write, write_onexit, QL_INTERCEPT.EXIT)
+2 -1
View File
@@ -7,7 +7,8 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/mips32el_linux/bin/mips32el_hello_static"], "rootfs/mips32el_linux", output="debug")
ql = Qiling(["rootfs/mips32el_linux/bin/mips32el_hello_static"], "rootfs/mips32el_linux", verbose=QL_VERBOSE.DEBUG)
ql.run()
@@ -7,7 +7,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_INTERCEPT, QL_CALL_BLOCK
from qiling.const import QL_INTERCEPT, QL_CALL_BLOCK, QL_VERBOSE
from qiling.os.const import STRING
def my_puts_onenter(ql: Qiling):
@@ -21,7 +21,7 @@ def my_puts_onexit(ql: Qiling):
return QL_CALL_BLOCK
if __name__ == "__main__":
ql = Qiling(["rootfs/mips32el_linux/bin/mips32el_double_hello"], "rootfs/mips32el_linux", output="debug")
ql = Qiling(["rootfs/mips32el_linux/bin/mips32el_double_hello"], "rootfs/mips32el_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_api('puts', my_puts_onenter, QL_INTERCEPT.ENTER)
ql.set_api('puts', my_puts_onexit, QL_INTERCEPT.EXIT)
+2 -1
View File
@@ -7,8 +7,9 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", output = "debug")
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.debugger = "gdb:0.0.0.0:9999"
ql.run()
+2 -1
View File
@@ -7,6 +7,7 @@ sys.path.append("..")
from qiling import Qiling
from qiling.os.const import STRING
from qiling.const import QL_VERBOSE
def my_puts(ql: Qiling):
params = ql.os.resolve_fcall_params({'s': STRING})
@@ -14,6 +15,6 @@ def my_puts(ql: Qiling):
print(f'puts("{params["s"]}")')
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", output="debug")
ql = Qiling(["rootfs/x8664_linux/bin/x8664_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.set_api('puts', my_puts)
ql.run()
+2 -1
View File
@@ -7,9 +7,10 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/sleep_hello"], "rootfs/x8664_linux", output="default")
ql = Qiling(["rootfs/x8664_linux/bin/sleep_hello"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEFAULT)
# load base address from profile file
X64BASE = int(ql.profile.get("OS64", "load_address"), 16)
+2 -1
View File
@@ -7,7 +7,8 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_macos/bin/x8664_hello"], "rootfs/x8664_macos", output="debug")
ql = Qiling(["rootfs/x8664_macos/bin/x8664_hello"], "rootfs/x8664_macos", verbose=QL_VERBOSE.DEBUG)
ql.run()
+2 -1
View File
@@ -10,6 +10,7 @@ from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.os.windows.fncc import *
from qiling.os.const import STRING
from qiling.const import QL_VERBOSE
@winsdkapi(cc=CDECL, replace_params={"str": STRING})
def my_puts(ql: Qiling, address: int, params):
@@ -35,7 +36,7 @@ def my_onexit(ql: Qiling, address: int, params, retval: int):
print(f'[onexit] atexit : params = {params}')
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output = "debug")
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.set_api("_cexit", my_onenter, QL_INTERCEPT.ENTER)
ql.set_api("puts", my_puts)
+2 -1
View File
@@ -6,6 +6,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def mem_crash(ql: Qiling, access: int, address: int, size: int, value: int):
print(f'got crash')
@@ -18,7 +19,7 @@ def mem_crash(ql: Qiling, access: int, address: int, size: int, value: int):
ql.mem.write(aligned, b'Q' * PAGE_SIZE)
if __name__ == "__main__":
ql = Qiling(["rootfs/x8664_linux/bin/mem_invalid_access"], "rootfs/x8664_linux", output="debug")
ql = Qiling(["rootfs/x8664_linux/bin/mem_invalid_access"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG)
ql.hook_mem_invalid(mem_crash)
ql.run()
+2 -1
View File
@@ -7,9 +7,10 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output="debug", multithread=True)
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG, multithread=True)
ql.run()
if __name__ == "__main__":
+2 -1
View File
@@ -7,9 +7,10 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output="debug", multithread=True)
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG, multithread=True)
ql.run()
if __name__ == "__main__":
+2 -2
View File
@@ -17,7 +17,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_INTERCEPT
from qiling.const import QL_INTERCEPT, QL_VERBOSE
from qiling.os.posix import syscall
from qiling.os.const import UINT, POINTER
@@ -57,7 +57,7 @@ def my_bind(ql: Qiling):
return 0 # from syscall.ql_syscall_bind(ql, bind_fd, bind_addr, bind_addrlen)
def my_netgear(path, rootfs):
ql = Qiling(path, rootfs, output="debug", profile="netgear_6220.ql", multithread=False)
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG, profile="netgear_6220.ql", multithread=False)
ql.root = False
ql.add_fs_mapper('/proc', '/proc')
+3 -2
View File
@@ -11,6 +11,7 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
from qiling.os.disk import QlDisk
verfication_start_ip = 0x850B
@@ -48,7 +49,7 @@ def third_stage(key):
ql = Qiling(["rootfs/8086/petya/petya.DOS_MBR"],
"rootfs/8086",
console=False,
output="debug")
verbose=QL_VERBOSE.DEBUG)
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/petya/out_1M.raw", 0x80))
ql.hook_code(pass_red, begin=0x886d, end=0x886d)
ql.hook_code(input_key, begin=0x85f0, end=0x85f0)
@@ -91,7 +92,7 @@ def first_stage():
ql = Qiling(["rootfs/8086/petya/petya.DOS_MBR"],
"rootfs/8086",
console=False,
output="debug")
verbose=QL_VERBOSE.DEBUG)
ql.add_fs_mapper(0x80, QlDisk("rootfs/8086/petya/out_1M.raw", 0x80))
# Workaround for `until` in uc_emu_start not working with dynamic loaded code.
ql.hook_code(stop, begin=petya_2nd_stage_start, end=petya_2nd_stage_start)
+2 -1
View File
@@ -7,9 +7,10 @@ import sys
sys.path.append("..")
from qiling import Qiling
from qiling.const import QL_VERBOSE
def my_sandbox(path, rootfs):
ql = Qiling(path, rootfs, output="debug")
ql = Qiling(path, rootfs, verbose=QL_VERBOSE.DEBUG)
ql.run()
if __name__ == "__main__":
+1
Submodule examples/rootfs added at 498b100f17
-9
View File
@@ -1,9 +0,0 @@
# dont keep raw malware
wannacry.bin
UselessDisk.bin
GandCrab.bin
GandCrab502.bin
al-khaser.bin
sality.dll
SuperRootkit.kext
m0hamed_rootkit.ko
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
Linux rasp3 4.14.71-v7+ #1145 SMP Fri Sep 21 15:38:35 BST 2018 armv7l GNU/Linux
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
ld-linux.so.3
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
/symlink_test/libsymlink_test.so
Binary file not shown.
-1
View File
@@ -1 +0,0 @@
/tmp/media
@@ -1 +0,0 @@
Linux rasp3 4.14.71-v7+ #1145 SMP Fri Sep 21 15:38:35 BST 2018 armv7l GNU/Linux
-1
View File
@@ -1 +0,0 @@
/media/nand/symlink_test
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More