Old linux server(only GLIBC_2.12)
My linux server system version is Red Hat 6.8, which only supports GLIBC_2.12, but pyarmor/cli/core/pyarmor_runtime.so require GLIBC_2.14, and pyarmor/cli/core/pyarmor_runtime.so requires GLIBC_2.15.
I'm sure the server can't upgrade the system and GLIBC. Can you regenerate the dynamic library that only depends on GLIBC_2.12?
My python version is 3.8,pyarmor version is Pyarmor 8.3.7 (group).
When I execute the command(pyarmor gen --platform centos6.x86_64 ...), I get an error:
ERROR: Could not find a version that satisfies the requirement pyarmor.cli.core.centos6==4.3.4 (from versions: none)
ERROR: No matching distribution found for pyarmor.cli.core.centos6==4.3.4
There is no centos platform, please refer to https://pyarmor.readthedocs.io/en/v7.7/questions.html#lib64-libc-so-6-version-glibc-2-14-not-found
To patch pyarmor/cli/core/pytransform3.so and pyarmor/cli/core/pyarmor_runtime.so
Here is one example script
# ======================================================================
# Routine: replace_symbol_version
# Provided so that scripts which are invoked via postinstall
# can prevent escape codes from showing up in /var/log/setup.log
# ======================================================================
replace_symbol_version()
{
local dest=${1}
local srcoffset=${2}
local destoffset=${3}
echo "Patch ${1}, source offset ${2}, target offset ${3}"
local offset=$($READELF -V $dest | grep "Offset" | $AWK 'FNR == 3 { print $4; }')
if [[ -z "$offset" ]] ; then
echo "No found offset of section '.gnu.version_r'"
exit 1
fi
echo "Offset of section '.gnu.version_r': $offset"
let -i addr1=$(( $offset + $srcoffset ))
let -i addr2=$(( $offset + $destoffset ))
echo "Copy 4 bytes from $(printf '%x' $addr1) to $(printf '%x' $addr2)"
xxd -s $addr1 -l 4 $dest | sed "s/$(printf '%x' $addr1)/$(printf '%x' $addr2)/" | xxd -r - $dest
let -i addr1=$(( $offset + $srcoffset + 0x8 ))
let -i addr2=$(( $offset + $destoffset + 0x8 ))
echo "Copy 4 bytes from $(printf '%x' $addr1) to $(printf '%x' $addr2)"
xxd -s $addr1 -l 4 $dest | sed "s/$(printf '%x' $addr1)/$(printf '%x' $addr2)/" | xxd -r - $dest
} # === End of replace_symbol_version() === #
readonly -f replace_symbol_version
AWK=awk
READELF=readelf
[[ -f pytransform3.so.bak ]] && cp pytransform3.so pytransform3.so.bak
[[ -f pyarmor_runtime.so.bak ]] && cp pyarmor_runtime.so pyarmor_runtime.so.bak
replace_symbol_version pytransform3.so 0x10 0x50
replace_symbol_version pyarmor_runtime.so 0x10 0x50
replace_symbol_version pyarmor_runtime.so 0x10 0x60
Please run this script in the path of package pyarmor.cli.core
@jondy Thank you for your prompt reply.
After applying the patch, I got the following error:
ImportError: /xxx/pyarmor_runtime_006111/pyarmor_runtime.so: symbol __fdelt_chk, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
I found that __fdelt_chk@GLIBC_2.15 and memcpy@GLIBC_2.14 symbols were called in pyarmor_runtime.so.
It seems feasible to replace memcpy@GLIBC_2.14 with memcpy@GLIBC_2.2.5, but __fdelt_chk@GLIBC_2.2.5 cannot replace __fdelt_chk@GLIBC_2.15 because there is no __fdelt_chk in GLIBC_2.2.5.
nm /lib64/libc.so.6 |grep fdelt # Nothing returned
I need your continued help. I'm just one step away from success. Thanks!
Or, when compiling, can you remove the dependency on __fdelt_chk? Maybe you can refer to "Use older glibc on Linux".
Because __fdelt_chk is only used by Pyarmor Group License in docker container, so it should work to replace string __fdelt_chk with __fgets_chk in the pyarmor_runtime.so by any binary editor tool
I prefer to patch extension file if patch script is simple.
@jondy I don't know these hacking techniques, can you provide a patch file or give an example? Thanks
I wrote a simple patch:
# patch.sh
# patch pytransform3.so
# GLIBC_2.14 -> GLIBC_2.2.5
xxd -s 0x22b0 -l 4 pytransform3.so | sed "s/22b0/22f0/" | xxd -r - pytransform3.so
xxd -s 0x22b8 -l 4 pytransform3.so | sed "s/22b8/22f8/" | xxd -r - pytransform3.so
# patch pyarmor_runtime.so
# GLIBC_2.14 -> GLIBC_2.2.5
xxd -s 0x3318 -l 4 pyarmor_runtime.so | sed "s/3318/3358/" | xxd -r - pyarmor_runtime.so
xxd -s 0x3320 -l 4 pyarmor_runtime.so | sed "s/3320/3360/" | xxd -r - pyarmor_runtime.so
# GLIBC_2.15 -> GLIBC_2.4
xxd -s 0x3378 -l 4 pyarmor_runtime.so | sed "s/3378/3368/" | xxd -r - pyarmor_runtime.so
xxd -s 0x3380 -l 4 pyarmor_runtime.so | sed "s/3380/3370/" | xxd -r - pyarmor_runtime.so
# __fdelt_chk@GLIBC_2.15 -> _fgets_chk@GLIBC_2.4
echo 00002f50:5f5f66676574735f63686b | xxd -r - pyarmor_runtime.so
The offsets (0x22b0, 0x22b8) in pytransform3.so comes from:
$ readelf -V pytransform3.so
Version needs section '.gnu.version_r' contains 3 entries:
Addr: 0x00000000000022a0 Offset: 0x0022a0 Link: 4 (.dynstr)
000000: Version: 1 File: libdl.so.2 Cnt: 1
0x0010: Name: GLIBC_2.2.5 Flags: none Version: 10
0x0020: Version: 1 File: libpthread.so.0 Cnt: 1
0x0030: Name: GLIBC_2.2.5 Flags: none Version: 6
0x0040: Version: 1 File: libc.so.6 Cnt: 6
0x0050: Name: GLIBC_2.14 Flags: none Version: 9
0x0060: Name: GLIBC_2.4 Flags: none Version: 8
0x22b0 = 0x0022a0 + 0x10
0x22b8 = 0x0022a0 + 0x18
The offsets (0x3318, 0x3320, ...) in pyarmor_runtime.so comes from:
$ readelf -V pyarmor_runtime.so
Version needs section '.gnu.version_r' contains 3 entries:
Addr: 0x0000000000003308 Offset: 0x003308 Link: 4 (.dynstr)
000000: Version: 1 File: libdl.so.2 Cnt: 1
0x0010: Name: GLIBC_2.2.5 Flags: none Version: 11
0x0020: Version: 1 File: libpthread.so.0 Cnt: 1
0x0030: Name: GLIBC_2.2.5 Flags: none Version: 6
0x0040: Version: 1 File: libc.so.6 Cnt: 7
0x0050: Name: GLIBC_2.14 Flags: none Version: 10
0x0060: Name: GLIBC_2.15 Flags: none Version: 9
0x0070: Name: GLIBC_2.4 Flags: none Version: 8
the offsets (__fdelt_chk) in pyarmor_runtime.so comes from:
$xxd ../../core.bak/pyarmor_runtime.so |grep __fdelt_chk
00002f50: 5f5f 6664 656c 745f 6368 6b00 7365 6c65 __fdelt_chk.sele
Nice.
It also could be patched by 4 lines python code
python -c "
with open('pyarmor_runtime.so', 'rb') as f:
buf = f.read()
with open('pyarmor_runtime.so', 'wb') as f:
f.write(buf.replace(b'__fdelt_chk', b'__fgets_chk'))
"
And __fdelt_chk is used only by checking expired date of the obfuscated script by NTP protocol, it's not used by group license.
I also upload a script to patch both of extensions
# This script is used to patch extensions `pyarmor_runtime.so` and
# `pytransform3.so` to make them works in CentOS 6 (GLIBC < 2.14)
#
# Usage:
#
# cd /path/to/pyarmor/cli/core
# bash patch-centos6.sh
#
set -e
AWK=awk
READELF=readelf
PYTHON=python3
# ======================================================================
# Routine: replace_symbol_version filename offset srcoffset destoffset
# ======================================================================
replace_symbol_version()
{
local dest=${1}
local offset=${2}
local srcoffset=${3}
local destoffset=${4}
echo "Target symbol offset: ${4}"
let -i addr1=$(( $offset + $srcoffset ))
let -i addr2=$(( $offset + $destoffset ))
echo "Copy 4 bytes from $(printf '%x' $addr1) to $(printf '%x' $addr2)"
xxd -s $addr1 -l 4 $dest | sed "s/$(printf '%x' $addr1)/$(printf '%x' $addr2)/" | xxd -r - $dest
let -i addr1=$(( $offset + $srcoffset + 0x8 ))
let -i addr2=$(( $offset + $destoffset + 0x8 ))
echo "Copy 4 bytes from $(printf '%x' $addr1) to $(printf '%x' $addr2)"
xxd -s $addr1 -l 4 $dest | sed "s/$(printf '%x' $addr1)/$(printf '%x' $addr2)/" | xxd -r - $dest
} # === End of replace_symbol_version() === #
readonly -f replace_symbol_version
# ======================================================================
# Routine: patch_section_version filename
# ======================================================================
patch_section_version()
{
local dest=${1}
echo ""
echo "Patching ${1} ..."
local offset=$($READELF -V $dest | grep "Offset" | $AWK 'FNR == 3 { print $4; }')
if [[ -z "$offset" ]] ; then
echo "No found offset of section '.gnu.version_r'"
exit 1
fi
echo "Section '.gnu.version_r' offset: $offset"
local src=$($READELF -V $dest | grep "Name: GLIBC_2.2.5" | $AWK 'FNR == 1 { print $1 }')
if [[ -z "$src" ]] ; then
echo "No found refer symbol (GLIBC_2.2.5) offset"
exit 1
fi
src=${src%:}
echo "Refer symbol offset (GLIBC_2.2.5): $src"
local target=$($READELF -V $dest | grep "Name: GLIBC_2.14" | $AWK 'FNR == 1 { print $1 }')
replace_symbol_version $dest $offset $src ${target%:}
target=$($READELF -V $dest | grep "Name: GLIBC_2.15" | $AWK 'FNR == 1 { print $1 }')
[[ -n "$target" ]] && replace_symbol_version $dest $offset $src ${target%:}
echo "Patch ${1} end"
echo ""
}
for x in pytransform3.so pyarmor_runtime.so ; do
if [[ -f $x.bak ]] ; then
echo "Restore $x from backup file $x.bak"
cp $x.bak $x
else
echo "Create backup file $x.bak"
cp $x $x.bak
fi
# pytransform3.so: 0x10 0x50
# pyarmor_runtime.so: 0x10 0x50, 0x10 0x60
patch_section_version $x
done
echo "Rename symbol '__fdelt_chk' to '__fgets_chk' in 'pyarmor_runtime.so'"
$PYTHON -c "
with open('pyarmor_runtime.so', 'rb') as f:
buf = f.read()
with open('pyarmor_runtime.so', 'wb') as f:
f.write(buf.replace(b'__fdelt_chk', b'__fgets_chk'))
"
@jondy Thanks! It has perfectly solved my problem. Thank you again, I learned a lot from you.