I'm trying to get an UnsafeMutablePointer from an UnsafeMutableRawPointer obtained using Unmanaged.passUnretained().toOpaque():
class C { var foo = 42, bar = "bar" }
let c = C()
let rawPointer = Unmanaged.passUnretained(c).toOpaque()
let pointer = rawPointer.bindMemory(to: C.self, capacity: 1)
let pointee = pointer.pointee
print(pointee.foo) // EXC_BAD_ACCESS
Here's some LLDB output, which looks strange to me as everything seems alright in pointer until I ask for its pointee:
(lldb) frame variable -L c
scalar: (memtest2.C) c = 0x0000000101d00030 {
0x0000000101d00040: foo = 42
0x0000000101d00048: bar = "bar"
}
(lldb) frame variable -L rawPointer
0x00000001005e2e08: (UnsafeMutableRawPointer) rawPointer = {
scalar: _rawValue = 0x0000000101d00030 {
0x0000000101d00040: foo = 42
0x0000000101d00048: bar = "bar"
}
}
(lldb) frame variable -L pointer
0x00000001005e2e10: (UnsafeMutablePointer<memtest2.C>) pointer = 0x0000000101d00030
(lldb) frame variable -L pointer._rawValue
scalar: (memtest2.C) pointer._rawValue = 0x0000000101d00030 {
0x0000000101d00040: foo = 42
0x0000000101d00048: bar = "bar"
}
(lldb) frame variable -L pointee
0x00000001005e2e18: (memtest2.C) pointee = 0x00000001005b65d8 {
0x00000001005b65e8: foo = 140736790071664
0x00000001005b65f0: bar = ""
}
I've also tried assumingMemoryBound(to:), load(as:), or simply:
let pointer = UnsafePointer<C>(bitPattern: Int(bitPattern: rawPointer))!
print(pointer.pointee.foo) // EXC_BAD_ACCESS
But I always get this EXC_BAD_ACCESS error. What is going on here?
My rawPointer points to where the c instance data is located. It is not, as I expected, a pointer on the reference. Classes are reference types: the value of c is the memory address of where the class instance data is located. Yet the toOpaque() doc was clear:
Unsafely converts an unmanaged class reference to a pointer.
(toOpaque() actually calls unsafeBitCast(c, UnsafeMutableRawPointer.self))
To have a pointer on the reference one can simply do:
let referencePointer = UnsafeMutablePointer<C>(&c)
As my rawPointer points to the instance data, doing pointer.pointee tells the runtime that the first word of the instance data is a (or its) reference. Which of course isn't true nor makes sense.
Illustrating: (I've slightly change my initial code: both foo and bar are Int)
(lldb) frame variable c
(testmem.C) c = 0x0000000101833580 (foo = 42, bar = 84)
This (0x0000000101833580) is where the instance data is located. Let's see what the memory contains at this address:
(lldb) memory read 0x0000000101833580
0x101833580: e0 65 5b 00 01 00 00 00 02 00 00 00 00 00 00 00
0x101833590: 2a 00 00 00 00 00 00 00 54 00 00 00 00 00 00 00
I learned that the first word (e0 65 5b 00 01 00 00 00) is type data, the second (02 00 00 00 00 00 00 00) is reference counts (I don't know more about this), and the rest is the instance data. Indeed, 0x2a is 42 and 0x54 is 84. The value of foo and bar.
Doing pointer.pointee means telling the runtime that the first word (e0 65 5b 00 01 00 00 00 or 0x00000001005b65e0) is a reference pointing to where our instance data is located (which is obviously not the case)! Implying that pointer.pointee.foo is located at 0x00000001005b65e0 + 16 (0x00000001005b65f0) and bar at + 24 (0x00000001005b65f0).
(lldb) memory read 0x00000001005b65e0
0x1005b65e0: a9 65 5b 00 01 80 1d 00 80 62 5b 00 03 00 00 00
0x1005b65f0: 70 e9 10 9e ff 7f 00 00 00 00 00 00 00 00 00 00
foo contains 0x0000007fff9e10e970 which in decimal is 140735845296496 and corresponds to:
(lldb) frame variable -L pointee
0x00000001005e2e18: (testmem.C) pointee = 0x00000001005b65e0 {
0x00000001005b65f0: foo = 140735845296496 // this
0x00000001005b65f8: bar = 0
}
And as this data wasn't allocated by my program we don't have access to it, hence the EXC_BAD_ACCESS error.
Life makes sense now 😂.
Related
I have a small socket filter type eBPF program, where I'm trying to print a protocol value read from __sk_buff context:
struct bpf_insn prog[] = {
BPF_MOV64_REG(BPF_REG_6, BPF_REG_1),
BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, offsetof(struct __sk_buff, protocol)),
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),
BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -4),
BPF_MOV64_IMM(BPF_REG_2, 4),
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk),
BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_EXIT_INSN(),
};
...
I create a raw socket and bind it to the lo interface, then setsockopt(fd, SOL_SOCKET, SO_ATTACH_BPF, ...). It compiles and loads with no problems, however whenever I ping 127.0.0.1 I never see traces in the trace_pipe.
So, to make sure that it BPF_FUNC_trace_printk actually can work, I changed it so that it prints a static string on the stack, and it does print on every packet hitting the loopback.
What am I doing wrong?
Read the friendly manual :)
I don't believe you are calling the bpf_trace_printk() helper correctly (BPF_FUNC_trace_prink is just an integer, by the way). Its signature, commented in the kernel UAPI header bpf.h or in the bpf-helpers man page, is as follows:
long bpf_trace_printk(const char *fmt, u32 fmt_size, ...);
This means that the first argument must be a constant, null-terminated format string, not an integer like you do.
What does clang do?
I understand you are attaching your eBPF programs to sockets and cannot compile the whole program from C. However, why not compile that specific part as a generic networking eBPF program to see what the bytecode should look like? Let's write the C code:
#include <linux/bpf.h>
static long (*bpf_trace_printk)(const char *fmt, __u32 fmt_size, ...) = (void *) BPF_FUNC_trace_printk;
int printk_proto(struct __sk_buff *skb) {
char fmt[] = "%d\n";
bpf_trace_printk(fmt, sizeof(fmt), skb->protocol);
return 0;
}
Compile to an object file. For the record this would not load, unless we provide both a valid licence string (because bpf_trace_prink() needs a GPL-compatible program) and a compatible program type at load time. But it does not matter in our case, we just want to look at the generated instructions.
$ clang -O2 -g -emit-llvm -c prink_protocol.c -o - | \
llc -march=bpf -mcpu=probe -filetype=obj -o prink_protocol.o
Dump the bytecode:
$ llvm-objdump -d prink_protocol.o
prink_protocol.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <printk_proto>:
0: b4 02 00 00 25 64 0a 00 w2 = 680997
1: 63 2a fc ff 00 00 00 00 *(u32 *)(r10 - 4) = r2
2: 61 13 10 00 00 00 00 00 r3 = *(u32 *)(r1 + 16)
3: bf a1 00 00 00 00 00 00 r1 = r10
4: 07 01 00 00 fc ff ff ff r1 += -4
5: b4 02 00 00 04 00 00 00 w2 = 4
6: 85 00 00 00 06 00 00 00 call 6
7: b4 00 00 00 00 00 00 00 w0 = 0
8: 95 00 00 00 00 00 00 00 exit
We can see that on the first two instructions, the program writes the format string (in little endian) onto the stack: 680997 is 0x000a6425, \0\nd%. r2 still contains the length for the format string. The protocol value is stored in r3, the third argument for the call to bpf_trace_prink().
I'm attempting to search for an arbitrarily long byte string in WinDbg and print out the address if an integer in the vicinity meets some criteria.
Pseudo-register $t0 contains the starting address I want to search.
Here's something that, based on the Windows docs, maybe could work (though it clearly doesn't).
.foreach (place { s -[1] #$t0 L?30000 00 00 00 00 00 20 00 00 }) { .if ( (place +0x8) <= 0x1388) { .printf "0x%x\n", place } }
Search
First, the search command doesn't quite work correctly. I only want the address of the match (not the data).
s -[1] #$t0 L?30000 00 00 00 00 00 20 00 00
The docs say that the 1 flag will only return the address. When I issue that command, WinDbg replies
^ Syntax error in 's -1 #$t0 L?30000 00 00 00 00 00 20 00 00 '
If I leave out the -1, it finds two matches.
What am I doing wrong here?
Condition
I don't think the condition is behaving the way I want. I want to look at the third dword starting at place, i.e. place+8, and verify that it's smaller than 5000 (decimal). The .if inside the .foreach isn't printing a meaningful value for place (i.e. the address returned from the search). I think it's dereferencing place first and comparing the value of that integer to 5000. How do I look at the value of, say, *(int*)(place+8)?
Documentation?
The docs are not helping me very much. They only have sparse examples, none of which correspond to what I need.
Is there better documentation somewhere besides MS's Hardware Dev Center?
you can start writing JavaScript for a more legible way of scripting
old way
0:000> s -b vect l?0x1000 4d
00007ff7`8aaa0000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
00007ff7`8aaa00d4 4d 90 80 d2 df f9 82 d3-4d 90 80 d2 52 69 63 68 M.......M...Rich
00007ff7`8aaa00dc 4d 90 80 d2 52 69 63 68-4c 90 80 d2 00 00 00 00 M...RichL.......
0:000> s -[1]b vect l?0x1000 4d
0x00007ff7`8aaa0000
0x00007ff7`8aaa00d4
0x00007ff7`8aaa00dc
using javascript
function search(addr,len)
{
var index = []
var mem = host.memory.readMemoryValues(addr,len)
for(var i = 0; i < len; i++)
{
if(mem[i] == 0x4d)
{
index.push(addr+i)
}
}
return index
}
executed will return address like which you can manipulate further
0:000> dx -r1 #$scriptContents.search(0x00007ff78aaa0000,1000)
#$scriptContents.search(0x00007ff78aaa0000,1000) : 140701160046592,140701160046804,140701160046812
length : 0x3
[0x0] : 0x7ff78aaa0000
[0x1] : 0x7ff78aaa00d4
[0x2] : 0x7ff78aaa00dc
improving the script a little to find something based on first result
we will try to find the index of Rich string that follows the character 'M'
modified script
function search(addr,len)
{
var index = []
var Rich = []
var result = []
var mem = host.memory.readMemoryValues(addr,len)
for(var i = 0; i < len; i++)
{
if(mem[i] == 0x4d)
{
index.push(addr+i)
var temp = host.memory.readMemoryValues(addr+i+4,1,4)
host.diagnostics.debugLog(temp +"\t")
if(temp == 0x68636952)
{
Rich.push(addr+i)
}
}
}
result.push(index)
result.push(Rich)
return result
}
result only the third occurance of char "M" is followed by Rich string
0:000> dx -r2 #$scriptContents.search(0x00007ff78aaa0000,1000)
3 3548576223 1751345490 #$scriptContents.search(0x00007ff78aaa0000,1000) : 140701160046592,140701160046804,140701160046812,140701160046812
length : 0x2
[0x0] : 140701160046592,140701160046804,140701160046812
length : 0x3
[0x0] : 0x7ff78aaa0000
[0x1] : 0x7ff78aaa00d4
[0x2] : 0x7ff78aaa00dc
[0x1] : 140701160046812
length : 0x1
[0x0] : 0x7ff78aaa00dc
0:000> s -b vect l?0x1000 4d
00007ff7`8aaa0000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
00007ff7`8aaa00d4 4d 90 80 d2 df f9 82 d3-4d 90 80 d2 52 69 63 68 M.......M...Rich
00007ff7`8aaa00dc 4d 90 80 d2 52 69 63 68-4c 90 80 d2 00 00 00 00 M...RichL.......
load the extensension jsprovider.dll .load jsprovider
write a script say foo.js
load the script .scriptload ...\path\foo.js
execute any functions inside the js you wrote with dx #$scriptContents.myfunc(myargs)
see below using cdb just for ease of copy paste windbg works just as is
F:\>type mojo.js
function hola_mojo ()
{
host.diagnostics.debugLog("hola mojo this is javascript \n")
}
F:\>cdb -c ".load jsprovider;.scriptload .\mojo.js;dx #$scriptContents.hola_mojo();q" cdb | f:\usr\bin\grep.exe -A 6 -i reading
0:000> cdb: Reading initial command '.load jsprovider;.scriptload .\mojo.js;dx #$scriptContents.hola_mojo();q'
JavaScript script successfully loaded from 'F:\mojo.js'
hola mojo this is javascript
#$scriptContents.hola_mojo()
quit:
If I read this part of the documentation
s [-[[Flags]Type]] Range Pattern
correctly, you cannot leave out Type when specifying flags. That's because the flags are inside two square brackets. Otherwise it would have been noted as s [-[Flags][Type]] Range Pattern.
Considering this, the example works:
0:000> .dvalloc 2000
Allocated 2000 bytes starting at 00ba0000
0:000> eb 00ba0000 01 02 03 04 05 06 07 08 09
0:000> eb 00ba1000 01 02 03 04 05 06 07 08 09
0:000> s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08
0x00ba0000
0x00ba1000
Also note that you'll have a hidden bug for the use of place: it should be ${place}. By default, that will work with the address (line break for readability on SO):
0:000> .foreach (place {s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08 })
{ .if ( (${place} +0x8) < 0xba1000) { .printf "0x%x\n", ${place} } }
0xba0000
In order to read a DWord from that address, use the dwo() MASM oerator (line break for readability on SO):
0:000> .foreach (place {s -[1]b 00ba0000 L?2000 01 02 03 04 05 06 07 08 })
{ .if ( (dwo(${place} +0x8)) < 0xba1000)
{ .printf "0x%x = 0x%x\n", ${place}, dwo(${place}+8) } }
0xba0000 = 0x9
0xba1000 = 0x9
When using Desfire native wrapped APDUs to communicate with the card, which parts of the command and response must be used to calculate CMAC?
After successful authentication, I have the following session key:
Session Key: 7CCEBF73356F21C9191E87472F9D0EA2
Then when I send a GetKeyVersion command, card returns the following CMAC which I'm trying to verify:
<< 90 64 00 00 01 00 00
>> 00 3376289145DA8C27 9100
I have implemented CMAC algorithm according to "NIST special publication 800-38B" and made sure it is correct. But I don't know which parts of command and response APDUs must be used to calculate CMAC.
I am using TDES, so MAC is 8 bytes.
I have been looking at the exact same issue for the last few days and I think I can at least give you some pointers. Getting everything 'just so' has taken some time and the documentation from NXP (assuming you have access) is a little difficult to interpret in some cases.
So, as you probably know, you need to calculate the CMAC (and update your init vec) on transmit as well as receive. You need to save the CMAC each time you calculate it as the init vec for the next crypto operation (whether CMAC or encryption etc).
When calculating the CMAC for your example the data to feed into your CMAC algorithm is the INS byte (0x64) and the command data (0x00). Of course this will be padded etc as specified by CMAC. Note, however, that you do not calculate the CMAC across the entire APDU wrapping (i.e. 90 64 00 00 01 00 00) just the INS byte and data payload is used.
On receive you need to take the data (0x00) and the second status byte (also 0x00) and calculate the CMAC over that. It's not important in this example but order is important here. You use the response body (excluding the CMAC) then SW2.
Note that only half of the CMAC is actually sent - CMAC should yield 16 bytes and the card is sending the first 8 bytes.
There were a few other things that held me up including:
I was calculating the session key incorrectly - it is worth double checking this if things are not coming out as you'd expect
I interpreted the documentation to say that the entire APDU structure is used to calculate the CMAC (hard to read them any other way tbh)
I am still working on calculating the response from a Write Data command correctly. The command succeeds but I can't validate the CMAC. I do know that Write Data is not padded with CMAC padding but just zeros - not yet sure what else I've missed.
Finally, here is a real example from communicating with a card from my logs:
Authentication is complete (AES) and the session key is determined to be F92E48F9A6C34722A90EA29CFA0C3D12; init vec is zeros
I'm going to send the Get Key Version command (as in your example) so I calculate CMAC over 6400 and get 1200551CA7E2F49514A1324B7E3428F1 (which is now my init vec for the next calculation)
Send 90640000010000 to the card and receive 00C929939C467434A8 (status is 9100).
Calculate CMAC over 00 00 and get C929939C467434A8A29AB2C40B977B83 (and update init vec for next calculation)
The first half of our CMAC from step #4 matches the 8 byte received from the card in step #3
Sry for my English,- its terrible :) but it's not my native language. I'm Russian.
Check first MSB (7 - bit) of array[0] and then shiffting this to the left. And then XOR if MSB 7 bit was == 1;
Or save first MSB bit of array[0] and after shiffting put this bit at the end of array[15] at the end (LSB bit).
Just proof it's here:
https://www.nxp.com/docs/en/application-note/AN10922.pdf
Try this way:
Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
First u have to encrypt 16 bytes (zeros) with SesionKey;
enc_aes_128_ecb(Zeros);
And u get EncryptedData.
EncryptedData <- 3D 08 A2 49 D9 71 58 EA 75 73 18 F2 FA 6A 27 AC
Check bit 7 [MSB - LSB] of EncryptedData[0] == 1? switch i to true;
bool i = false;
if (EncryptedData[0] & 0x80){
i = true;
}
Then do Shiffting of all EncryptedData to 1 bit <<.
ShiftLeft(EncryptedData,16);
And now, when i == true - XOR the last byte [15] with 0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Save it as KEY_1.
Try bit 7 [MSB - LSB] of ShiftedEncryptedData[0] == 1?
i = false;
if (ShiftedEncryptedData[0] & 0x80){
i = true;
}
Then do Shiffting of all ShiftedEncryptedData to 1 bit <<.
ShiftLeft(ShiftedEncryptedData,16);
And now, when i == true - XOR the last byte [15] with 0x87
if (i){
ShiftedEncryptedData[15] ^= 0x87;
}
F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
Save it as KEY_2.
Now we take our Data (6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00)
As Michael say's - pad command with 0x80 0x00...
XOR Data with KEY_2 - if command was padded, or KEY_1 if don't.
If we have more like 16 bytes (32 for example) u have to XOR just last 16 bytes.
Then encrypt it:
enc_aes_128_ecb(Data);
Now u have a CMAC.
CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
Zeros <- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
SessionKey <- 00 01 02 03 E3 27 64 0C 0C 0D 0E 0F 5C 5D B9 D5
Key_1 <- 7A 11 44 93 B2 E2 B1 D4 EA E6 31 E5 F4 D4 4F 58
Key_2 <- F4 22 89 27 65 C5 63 A9 D5 CC 63 CB E9 A8 9E B0
Data <- 6F 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00
CMAC <- CD C0 52 62 6D F6 60 CA 9B C1 09 FF EF 64 1A E3
C/C++ function:
void ShiftLeft(byte *data, byte dataLen){
for (int n = 0; n < dataLen - 1; n++) {
data[n] = ((data[n] << 1) | ((data[n+1] >> 7)&0x01));
}
data[dataLen - 1] <<= 1;
}
Have a nice day :)
I am currently working on converting our PowerBuilder 12.1 application, which does not currently support Unicode, into a Unicode supporting application.
I have made some modifications to save Unicode data to our database, as well to files, but I have hit a slight snag in processing strings.
For example, the character 𠆾 is a Surrogate Pair and PowerBuilder interprets this as 2 characters (similar to how .NET operates). Thus:
LEN("𠆾") = 2
To me, this part makes sense, as it is count each code unit as a character.
Currently we have come up with two solutions to handle doing string functions with Unicode characters:
Callable OLEObjects written in C# .NET
using the PBNI interface to call C# .NET (want to stay away from this solution if possible)
An example of the .NET code we are thinking of using for determining the string length is:
StringInfo.ParseCombiningCharacters("𠆾").Length = 1
We are just worried about the impact on performance with constantly calling the OLEObjects/PBNI to do all of our string processing. Have any of the other PowerBuilder developers here done Unicode string manipulation (LEN, MID, POS, etc), and how did you do it?
Thank you.
This is in response to Seki's hex conversion function. I'm posting it as an answer so I can include source code. I use the Microsoft cryptographic functions to display blobs in my debugging tools. Here's a simplified version of my blob window. The one I use is PFC-based and uses an object that wraps the MS Crypto library. It's from PB 12.5 but should import into any Unicode version of PB.
HA$PBExportHeader$w_show_blob.srw
forward
global type w_show_blob from window
end type
type sle_1 from singlelineedit within w_show_blob
end type
type mle_1 from multilineedit within w_show_blob
end type
end forward
global type w_show_blob from window
integer width = 3081
integer height = 1988
boolean titlebar = true
boolean controlmenu = true
boolean minbox = true
boolean maxbox = true
boolean resizable = true
boolean center = true
sle_1 sle_1
mle_1 mle_1
end type
global w_show_blob w_show_blob
type prototypes
FUNCTION boolean CryptBinaryToString ( &
Blob pbBinary, &
ulong cbBinary, &
ulong dwFlags, &
Ref string pszString, &
Ref ulong pcchString ) &
LIBRARY "crypt32.dll" ALIAS FOR "CryptBinaryToStringW"
end prototypes
type variables
CONSTANT Ulong CRYPT_STRING_HEXASCIIADDR = 11
end variables
forward prototypes
public subroutine of_showblob (ref blob abl_data)
end prototypes
public subroutine of_showblob (ref blob abl_data);unsignedlong lul_size, lul_bufsize
string ls_hex
try
lul_size = len(abl_data)
sle_1.text = string(lul_size)
setnull(ls_hex)
cryptbinarytostring( abl_data, lul_size, CRYPT_STRING_HEXASCIIADDR, ls_hex, lul_bufsize)
ls_hex = space(lul_bufsize)
if not cryptbinarytostring( abl_data, lul_size, CRYPT_STRING_HEXASCIIADDR , ls_hex, lul_bufsize) then
mle_1.text = "error converting blob data"
else
mle_1.text = ls_hex
end if
catch(runtimeerror re)
messagebox("oops", re.text)
end try
end subroutine
on w_show_blob.create
this.sle_1=create sle_1
this.mle_1=create mle_1
this.Control[]={this.sle_1,&
this.mle_1}
end on
on w_show_blob.destroy
destroy(this.sle_1)
destroy(this.mle_1)
end on
type sle_1 from singlelineedit within w_show_blob
integer x = 73
integer width = 517
integer height = 88
integer taborder = 10
integer textsize = -10
integer weight = 400
fontcharset fontcharset = ansi!
fontpitch fontpitch = variable!
fontfamily fontfamily = swiss!
string facename = "Arial"
long textcolor = 33554432
long backcolor = 553648127
string text = "none"
boolean displayonly = true
borderstyle borderstyle = stylelowered!
end type
type mle_1 from multilineedit within w_show_blob
integer x = 64
integer y = 96
integer width = 2898
integer height = 1716
integer taborder = 10
integer textsize = -10
integer weight = 400
fontcharset fontcharset = ansi!
fontpitch fontpitch = fixed!
fontfamily fontfamily = modern!
string facename = "Courier New"
long textcolor = 33554432
string text = "none"
boolean hscrollbar = true
boolean vscrollbar = true
boolean displayonly = true
borderstyle borderstyle = stylelowered!
end type
To use it, assuming your blob is lbl_myBlob:
open(w_show_blob)
w_show_blob.of_showblob(lbl_myBlob)
The output in the MLE looks like this:
0000 42 4d ee 00 00 00 00 00 00 00 76 00 00 00 28 00 BM........v...(.
0010 00 00 10 00 00 00 0f 00 00 00 01 00 04 00 00 00 ................
0020 00 00 78 00 00 00 00 00 00 00 00 00 00 00 00 00 ..x.............
0030 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80 ................
0040 00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80 ................
0050 00 00 80 80 80 00 c0 c0 c0 00 00 00 ff 00 00 ff ................
0060 00 00 00 ff ff 00 ff 00 00 00 ff 00 ff 00 ff ff ................
0070 00 00 ff ff ff 00 88 88 88 88 88 88 88 88 88 88 ................
0080 80 88 88 88 88 88 88 88 80 08 88 88 88 88 88 88 ................
0090 80 00 88 88 88 88 88 88 80 00 08 88 88 88 88 88 ................
00a0 80 00 00 88 88 88 88 88 80 00 00 08 88 88 88 88 ................
00b0 80 00 00 00 88 88 88 88 80 00 00 08 88 88 88 88 ................
00c0 80 00 00 88 88 88 88 88 80 00 08 88 88 88 88 88 ................
00d0 80 00 88 88 88 88 88 88 80 08 88 88 88 88 88 88 ................
00e0 80 88 88 88 88 88 88 88 88 88 88 88 88 88 ..............
Since the release 10, PB is unicode (utf-16le)-aware. So the legacy Len() is implicit LenW() (as other string functions, and dealing with legacy data could imply to use explicit LenA()).
Are you sure that you are getting some utf-16le encoding ? Given the following function, what does it return on a string containing your data, if you call it with hexdump_blob(blob(your_string))?
Paste this code into the source code of a new global function named hexdump_blob to have an hexadecimal display (hex editor like) for blob contents.
global type hexdump_blob from function_object
end type
forward prototypes
global function string hexdump_blob (blob abl_data, boolean ab_fill_lastline)
end prototypes
global function string hexdump_blob (blob abl_data, boolean ab_fill_lastline);//hexify a blob content
string ls_tohex = "0123456789ABCDEF"
string ls_msg = "", ls_line, ls_binary
long i, j, length
byte b
string ls_fill
if isnull( abl_data ) then return ""
if ab_fill_lastline then
ls_fill = " __"
else
ls_fill = " "
end if
length = len( abl_data )
for i = 1 to length
GetByte( abl_data, i, b )
ls_line += mid( ls_tohex, 1+ mod(int(b/16),16), 1)
ls_line += mid( ls_tohex, 1+ mod(b,16), 1)
ls_line += " "
ls_binary += string( iif(b>31 and b<128,char(b)," "))
if mod(i,16) = 0 and i > 0 then
ls_binary = replaceall( ls_binary, "~r", "·") //no cr/lf
ls_binary = replaceall( ls_binary, "~n", "·")
ls_binary = replaceall( ls_binary, "~t", "·")
ls_msg += "[" + string( i - 16, "0000") + "] " + ls_line + "~t" + ls_binary + "~r~n"
ls_line = ""
ls_binary = ""
end if
next
i -- // i - 1 due to the last loop in for
ls_line += fill(ls_fill, 3 * ( 16 - mod(i, 16) ) )
ls_msg += "[" + string( i - mod(i,16), "0000") + "] " + ls_line + "~t" + ls_binary
return ls_msg
end function
Also, here is the replaceall() function that is used by hexdump_blob()
global type replaceall from function_object
end type
forward prototypes
global function string replaceall (string as_source, string as_pattern, string as_replace)
end prototypes
global function string replaceall (string as_source, string as_pattern, string as_replace);//remplace toute les occurences de as_pattern de as_source par as_replace
string ls_target
long i, j
ls_target=""
i = 1
j = 1
do
i = pos( as_source, as_pattern, j )
if i>0 then
ls_target += mid( as_source, j, i - j )
ls_target += as_replace
j = i + len( as_pattern )
else
ls_target += mid( as_source, j )
end if
loop while i>0
return ls_target
end function
and the iif() that simulates the C ternary operator, or the visual basic iif()
global type iif from function_object
end type
forward prototypes
global function any iif (boolean ab_cond, any aa_true, any aa_false)
end prototypes
global function any iif (boolean ab_cond, any aa_true, any aa_false);
// simulates the VB iif or C ternary operator
if ab_cond then
return aa_true
else
return aa_false
end if
end function
Wouldn't you want to use the LenA() method?
http://www.techno-kitten.com/Changes_to_PowerBuilder/New_in_PowerBuilder_10/PB10New_-_Unicode_Support/PB10New_-_Unicode_Related_Chan/PB10New_-_String-Related_Funct/pb10new_-_modified_processing_.html
So somehow from the following hex data (03 00 21 04 80 04) the values below were obtained.
Can anybody can tell how how can I do this and how it was achieved?
Band = 3 (40,6)
Duplex_Mode = 0 (46,1)
Result = 0 (47,1)
Reserved_1 = 0 (48,8)
Min_Search_Half_Frames = 1 (56,5)
Min_Search_Half_Frames_Early_Abort = 1 (61,5)
Max_Search_Half_Frames = 1 (66,5)
Max_PBCH_Frames = 0 (71,5)
Number_of_Blocked_Cells = 0 (76,3)
Number_PBCH_Decode_Attemp_Cells = 1 (79,3)
Number_of_Search_Results = 1 (82,4)
Reserved_2 = 0 (86,2)
The parameters in paranthesis is the Offset/Length I am told. I don't understand how based on that information should I be able to unpack this payload.
So I have written
my $data = pack ('C*', map hex, split /\s+/, "03 00 21 04 80 04");
($tmp1, $Reserved_1, $tmp2) = unpack("C C V", $data);
And now help. How do I unpack the table values above from $tmp1 and $tmp2 ?
EDIT: Hex Data = "00 00 00 7F 08 03 00 21 04 80 04 FF D7 FB 0C EC 01 44 00 61 1D 00 00 10 3B 00 00 FF D7 FB 0C 00 00 8C 64 00 00 EC 45"
Thanks!
You might want to define a set of bitmasks, and use bitwise AND operations to unpack your data.