SNMP MIB Implementation - OpenCircuits

How to use MQTT packet to implement publish and subscribe functions

How to use MQTT packet to implement publish and subscribe functions
The MQTT protocol communicates by exchanging predefined MQTT control packets. We will take MQTTX as an example to show how to implement the publish and subscribe function through MQTT packets.

Connect

MQTT protocol is based on the TCP / IP protocol. Both the MQTT Broker and the Client need a TCP / IP address.

Broker


https://preview.redd.it/v9qbuy7evdj41.png?width=2050&format=png&auto=webp&s=dbaab27a06de41db61dcb0aca168cfd6f9ffa564
If you don't have an available MQTT broker for the time being, **EMQ X**provides a public broker address for testing: broker.emqx.io: 1883 .

Client


https://preview.redd.it/f64a74wfvdj41.png?width=2050&format=png&auto=webp&s=ecd816203e26a04b5d4172ecdf6e33f3e65d5013
The configuration of the Client in the MQTTX tool is actually the configuration of the Connect packets in the MQTT protocol. The following explains the related configuration items:

Client ID

The server uses the ClientId to identify the client. Each client that connects to the server has a unique ClientId. Both the client and server must use the ClientId to identify the status related to the MQTT session between them.
The ClientId must exist, but the server can allow the client to provide a zero-byte ClientId. If this is done, the server must treat this as a special case and assign a unique ClientId to that client. Then, it can process this CONNECT packet normally.

Username/Password

MQTT can implement related authentication and authorization by sending the username and password. However, if this information is not encrypted, the username and password are sent in clear text. EMQ X not only supports SSL / TLS encryption, but also provides emqx-auth-username plugin to encrypt passwords.

Keep Alive

Keep Alive is a time interval in seconds. It refers to the maximum time interval allowed between the time when the client transmits a control packet to the time when the next message is sent. The client is responsible for ensuring that the interval of sending control packets does not exceed the value of the keep-alive. If no other control packet can be sent, the client must send a PINGREQ packet.
If the value of Keep Alive is non-zero, and the server does not receive the control packet from the client within 1.5 times of the Keep Alive time, it must disconnect the client's network connection and consider the network connection to be disconnected.

Clean Session

The client and server can save session state to support reliable message transmission across network connections. This flag tells the server whether this connection is a brand new connection.
The session state of the client includes:
  • QoS 1 and QoS 2 level messages that have been sent to the server but have not yet been confirmed
  • QoS 2 level messages that have been received from the server but have not yet been confirmed
The session state of the server includes:
  • Whether the session exists, even if the rest of the session state is empty.
  • Client's subscription information.
  • QoS 1 and QoS 2 level messages that have been sent to the client but have not yet been confirmed
  • QoS 1 and QoS 2 level messages to be transferred to the client.
  • QoS 2 level messages that have been received from the client but have not yet been confirmed
  • Optional, QoS 0 level message to be sent to the client.
If the CleanSession flag is set to 1, the client and server must discard any previous sessions and start a new session. The session only lasts as long as the network connection.
If the CleanSession flag is set to 0, the server must resume communication with the client based on the state of the current session (identified by ClientId). If there is no session associated with this client identifier, the server must create a new session. When the connection is disconnected, the client and server must save the session information.

Connack confirms connection request

When the client sends a Connect packet to request a connection to the server, the server must send a Connack packet as a response to the Connect packet from the client. If the client does not receive the CONNACK packet from the server within a reasonable time, the client should close the network connection. The reasonable time depends on the type of application and the communication infrastructure. In MQTTX, you can set a reasonable timeout time through Connection Timeout.

https://preview.redd.it/5s7mbx4lvdj41.png?width=924&format=png&auto=webp&s=d9b212f8a4b68ba762d409957516295bd4e7bdaf
Connack messages contain two important signs of Session Present and Connect Return code.

Session Present

Session Present flag indicates whether the current session is a new session. If the server receives a connection with a CleanSession flag of 1, the SessionPresent flag in the Connack packet is 0. If the server receives a connection with CleanSession 0, the value of the SessionPresent flag depends on whether the server has saved the session state of the client corresponding to ClientId. If the server has saved the session state, the SessionPresent flag in the Connack packet is 1. If the server has no saved session state, the SessionPresent flag in the Connack packet is 0.

Connect Return code

Connect Return code represents the server's response to this Connect, and 0 indicates that the connection has been accepted by the server. If the server receives a valid CONNECT packet but cannot process it for some reason, the server should try to send a CONNACK packet with a non-zero return code (one in the following table). If the server sends a CONNACK packet with a non-zero return code, it must close the network connection.
Value Return Code Response Description
0 0x00 connection accepted The connection has been accepted by the server
1 0x01 connection refused, unsupported protocol version The server does not support the MQTT protocol level requested by the client
2 0x02 connection refused, unqualified client identifier The client identifier is correctly UTF-8 encoded, but is not allowed by the server
3 0x03 Connection refused, server is unavailable Network connection has been established, but MQTT service is unavailable
4 0x04 connection refused, invalid username or password Data format for username or password is invalid
5 0x05 connection refused, unauthorized Client is not authorized to connect to this server
6-255 retained
If all connection return codes in the above table are considered inappropriate, the server must close the network connection without sending a CONNACK packet.

Subscribe to topics

The client sends a Subscribe packet to the server to create one or more subscriptions. Each registered client cares about one or more topics. In order to forward application messages to topics that match those subscriptions, the server sends Publish packet to the client. The Subscribe packet specifies the maximum QoS level for each subscription, and the server sends an application message to the client based on it.

https://preview.redd.it/hev5rgcnvdj41.png?width=2050&format=png&auto=webp&s=a9010f67a975b7aa375c4109a69f95fd4fc8b328
The payload of a Subscribe packet must contain at least one pair of topic filter and QoS level fields combination. A Subscribe packet without a payload is a violation of the protocol.
Use MQTTX to connect the Broker of broker.emqx.io:1883and create a subscription with the topic of testtopic/# and Qos equal to 2.

https://preview.redd.it/1q0t9q6pvdj41.png?width=2050&format=png&auto=webp&s=faa27c0912ae72d391bdbe898f06941a602c978a

Suback subscription confirmation

The server sends a Suback packet to the client to confirm that it has received Subscribe packet and is processing it .

https://preview.redd.it/32cmrl2rvdj41.png?width=924&format=png&auto=webp&s=a4a4e8f0e77dbe5b637afd19617b9bd125cffd04
The Suback packet contains a list of reason codes, which is used to specify the maximum QoS level or an error that occurs for each subscription requested by the Subscribe packet. Each reason code corresponds to a topic filter in the Subscribe packet. The reason code sequence in the Suback packet must match the order of the topic filters in the Subscribe packet.
Allowed values of return code:
  • 0x00 - maximum QoS 0
  • 0x01 - success – maximum QoS 1
  • 0x02 - success – maximum QoS 2
  • 0x80 - Failure

Publish messages

Publish packet refers to an application message transmitted from the client to the server or from the server to the client. After receiving the Publish packet, the server forwards the message to other clients according to the topic filter.

https://preview.redd.it/suu4vmeuvdj41.png?width=1422&format=png&auto=webp&s=f3c0a4296e8a8285e87a4e05ff5c5f23eba3a043
Try using MQTTX to publish a message with the topic testtopic / mytopic and the content{"msg": "hello world"}. Because the topic testtopic / # has been subscribed before, the message forwarded by Broker is received immediately.

https://preview.redd.it/ym0a72gvvdj41.png?width=2050&format=png&auto=webp&s=4e4e543d6adb96a8c3dc6c69d7baad0b1d25f462

Topic

The topic name is used to identify which session the message should be published. The topic name of the Publish packet sent by the server to the subscribing client must match the topic filter of the subscription.

QoS

QoS refers the quality level of service for application message distribution
QoS value Bit 2 Bit 1 Description
0 0 0 Distribute at most once
1 0 1 Distribute at least once
2 1 0 Distribute only once
- 1 1 Retained
Publish packet cannot set all QoS bits to 1. If the server or client receives a Publish packet with all QoS bits set to 1, it must close the network connection.
For the working principle of different levels of QoS, please refer to MQTT 5.0 Protocol Introduction-QoS Quality of Service.

Retain

If the RETAIN flag of the Publish packet sent by the client to the server is set to 1, the server must store this application message and its quality of service level (QoS) so that it can be distributed to future subscriber with the matching topic names . When a new subscription is created, for each matching topic name, if there is a recently retained message, it must be sent to this subscriber. If the server receives a Q message with a RETAIN flag of 1, it must discard any messages previously retaind for that topic and treat this new message as a new retained message for that topic.
Publish packet with a retain flag of 1 and a payload of zero bytes will be treated as a normal message by the server, and it will be sent to the client that matches the topic of the subscription. In addition, any existing retained messages under the same topic must be removed, so any subscribers following this topic will not receive a retained message
When the server sends a Publish packet to the client, if the message is sent as a result of a new subscription by the client, it must set the retain flag of the packet to 1. When a Publish packet is sent to the client because it matches an established subscription, the server must set the retain flag to 0, regardless of the value of the retain flag in the message it receives.
If the retain flag of the Publish packet sent by the client to the server is 0, the server cannot store the message and cannot remove or replace any existing retained message.

Payload

The payload contains application messages to be published. The content and format of the data is specific to the application and images, any encoded text, encrypted data, and almost all binary data can be sent .
submitted by emqtt to emqx [link] [comments]

Cant passthrough RX 5700 XT

I was trying to passtrough my only gpu but there seems to be a problem with vfio.
CPU: Ryzen 1700X
GPU: Sapphire pulse rx 5700 xt
Mobo: Asus Rog strix X370-F
Bios options: SVM : Enabled, SR-IOV : Disabled
OS: arch , kernel 5.2.11-arch1-1-ARCH
Kernel parameters: "amd_iommu=on iommu=pt loglevel=3 quiet"

mkinitcpio.conf (comments are ommited)
MODULES=(vfio_pci vfio vfio_iommu_type1 vfio_virqfd) BINARIES=() FILES=() HOOKS=(base udev autodetect modconf block filesystems keyboard fsck) 
/etc/modprobe.d/vfio.conf
options vfio_pci ids=1002:731f,1002:ab38 
iommu groups
IOMMU Group 0: 00:01.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] IOMMU Group 1: 00:01.1 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) PCIe GPP Bridge [1022:1453] IOMMU Group 10: 00:08.1 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Internal PCIe GPP Bridge 0 to Bus B [1022:1454] IOMMU Group 11: 00:14.0 SMBus [0c05]: Advanced Micro Devices, Inc. [AMD] FCH SMBus Controller [1022:790b] (rev 59) 00:14.3 ISA bridge [0601]: Advanced Micro Devices, Inc. [AMD] FCH LPC Bridge [1022:790e] (rev 51) IOMMU Group 12: 00:18.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 0 [1022:1460] 00:18.1 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 1 [1022:1461] 00:18.2 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 2 [1022:1462] 00:18.3 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 3 [1022:1463] 00:18.4 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 4 [1022:1464] 00:18.5 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 5 [1022:1465] 00:18.6 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 6 [1022:1466] 00:18.7 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 7 [1022:1467] IOMMU Group 13: 01:00.0 Non-Volatile memory controller [0108]: Samsung Electronics Co Ltd NVMe SSD Controller SM981/PM981/PM983 [144d:a808] IOMMU Group 14: 02:00.0 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] X370 Series Chipset USB 3.1 xHCI Controller [1022:43b9] (rev 02) 02:00.1 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] X370 Series Chipset SATA Controller [1022:43b5] (rev 02) 02:00.2 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] X370 Series Chipset PCIe Upstream Port [1022:43b0] (rev 02) 03:00.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 03:02.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 03:03.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 03:04.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 03:06.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 03:07.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] 300 Series Chipset PCIe Port [1022:43b4] (rev 02) 04:00.0 USB controller [0c03]: ASMedia Technology Inc. ASM1142 USB 3.1 Host Controller [1b21:1242] 05:00.0 Ethernet controller [0200]: Intel Corporation I211 Gigabit Network Connection [8086:1539] (rev 03) IOMMU Group 15: 0a:00.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD/ATI] Device [1002:1478] (rev c1) IOMMU Group 16: 0b:00.0 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD/ATI] Device [1002:1479] IOMMU Group 17: 0c:00.0 VGA compatible controller [0300]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 10 [Radeon RX 5700 / 5700 XT] [1002:731f] (rev c1) IOMMU Group 18: 0c:00.1 Audio device [0403]: Advanced Micro Devices, Inc. [AMD/ATI] Navi 10 HDMI Audio [1002:ab38] IOMMU Group 19: 0d:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Zeppelin/Raven/Raven2 PCIe Dummy Function [1022:145a] IOMMU Group 2: 00:01.3 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) PCIe GPP Bridge [1022:1453] IOMMU Group 20: 0d:00.2 Encryption controller [1080]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Platform Security Processor [1022:1456] IOMMU Group 21: 0d:00.3 USB controller [0c03]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) USB 3.0 Host Controller [1022:145c] IOMMU Group 22: 0e:00.0 Non-Essential Instrumentation [1300]: Advanced Micro Devices, Inc. [AMD] Zeppelin/Renoir PCIe Dummy Function [1022:1455] IOMMU Group 23: 0e:00.2 SATA controller [0106]: Advanced Micro Devices, Inc. [AMD] FCH SATA Controller [AHCI mode] [1022:7901] (rev 51) IOMMU Group 24: 0e:00.3 Audio device [0403]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) HD Audio Controller [1022:1457] IOMMU Group 3: 00:02.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] IOMMU Group 4: 00:03.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] IOMMU Group 5: 00:03.1 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) PCIe GPP Bridge [1022:1453] IOMMU Group 6: 00:04.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] IOMMU Group 7: 00:07.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] IOMMU Group 8: 00:07.1 PCI bridge [0604]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-0fh) Internal PCIe GPP Bridge 0 to Bus B [1022:1454] IOMMU Group 9: 00:08.0 Host bridge [0600]: Advanced Micro Devices, Inc. [AMD] Family 17h (Models 00h-1fh) PCIe Dummy Host Bridge [1022:1452] 
xml
 win10 facb7abd-ac3e-4a04-8e86-d6944b62d723      8388608 8388608 16  hvm /usshare/ovmf/x64/OVMF_CODE.fd /valib/libvirt/qemu/nvram/win10_VARS.fd                      destroy restart destroy      /usbin/qemu-system-x86_64     
Output of "dmesg | grep vfio" before starting vm
[ 1.279191] vfio-pci 0000:0c:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem [ 1.293682] vfio_pci: add [1002:731f[ffffffff:ffffffff]] class 0x000000/00000000 [ 1.310411] vfio_pci: add [1002:ab38[ffffffff:ffffffff]] class 0x000000/00000000 
full output: https://pastebin.com/UVaxAWWU

Output of "dmesg | grep vfio" after starting vm
[ 1.279191] vfio-pci 0000:0c:00.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem [ 1.293682] vfio_pci: add [1002:731f[ffffffff:ffffffff]] class 0x000000/00000000 [ 1.310411] vfio_pci: add [1002:ab38[ffffffff:ffffffff]] class 0x000000/00000000 [ 358.866916] vfio-pci 0000:0c:00.0: vfio_ecap_init: hiding ecap [email protected] [ 358.866927] vfio-pci 0000:0c:00.0: vfio_ecap_init: hiding ecap [email protected] [ 358.866930] vfio-pci 0000:0c:00.0: vfio_ecap_init: hiding ecap [email protected] [ 358.866931] vfio-pci 0000:0c:00.0: vfio_ecap_init: hiding ecap [email protected] [ 358.866933] vfio-pci 0000:0c:00.0: vfio_ecap_init: hiding ecap [email protected] [ 358.867808] vfio-pci 0000:0c:00.0: BAR 0: can't reserve [mem 0xe0000000-0xefffffff 64bit pref] [ 361.571367] vfio-pci 0000:0c:00.0: No more image in the PCI ROM [ 361.571388] vfio-pci 0000:0c:00.0: No more image in the PCI ROM 
full output: https://pastebin.com/VyxF9Y88

Since I only have one gpu I am forwarding X11 to a laptop and running virt-manager from there, windows starts and works fine but on my main display I only get a blinking cursor after starting the vm.

Edit: Thanks to u/cybervseas and this post https://www.reddit.com/VFIO/comments/7kpw33/cant_passthrough_boot_gpu_did_i_miss_something/ I got the vm working.
I added a line to the xml file that points to the rom file to be used. You can get that file from https://www.techpowerup.com/vgabios/ , download gpu-z and dump it or any other way to get the rom of your gpu.
    
The last thing was changing some settings in grub. I added the kernel parameter 'nofb' and changed GRUB_GFXPAYLOAD from 'keep' to 'text' in the file at /etc/default/grub.
Everything seems to work now I am writing this from inside the vm and I had no problems with the driver installation it works the same way as if windows was the host os.
submitted by diogo464 to VFIO [link] [comments]

Smashing the z/OS LE "Daisy" Chain for Fun and Cease and Desist letters.


Over the past few months I've been becoming increasingly interested in the CTF concept; finding (purposely built) flaws in software and exploiting them so that arbitrary code can be executed. Having an IBM mainframe background, I was wondering if a similar exercise could be made for that platform. Considering I only have access to a PL/1 compiler, I set out to see if its calling conventions can be exploited.

Introduction.

A typical security flaw for x86 systems consists of overflowing a buffer that lives on the stack, in order to overlay the piece of memory that contains the return address, which coincidentally also lives on the stack, to change the location that a subroutine will return back to. A good example of these are string buffer overflows, because the programmer carelessly copied a character array without properly checking the size of both.
Writing over this return address is trivial because a buffer will usually live close to said return address on the stack. This because of the x86 architecture revolving around a stack, and the common calling conventions used in x86 programs.

The Language Environment and its workings.

IBM mainframe programs written in compiled languages (COBOL, PL/1, C/C++, Java) are (since relatively recently) subject to being Language Environment (LE) compliant. Compilers will provide compliance automatically. The LE exists to provide a standardized calling convention, standardize tracing, and standardized debugging.
LE programs are, despite the z/Architecture not inherently being stack based, reliant on a stack structure. As a prologue to each procedure being executed, it will set up what is called a Dynamic Storage Area (DSA), which ties in with the calling conventions. It contains a couple of things;
And then some, but these things are the most important for us right now.
[1] z/Architecture has 16 general purpose registers. Unlike x86, they don't have physically enforced functions. Everything is convention. The contents of registers can be important to a procedure. Hence upon calling another procedures, the contents of the registers must be saved.
There is no CALL assembler instruction in the z/Architecture. A call is compiled to a hard branch. There are several types of branches. A call to a procedure would typically be compiled to a branch with two operands. The register containing the address where to branch to, and the register in which to save the location of the instruction right after the current branch instruction, which serves as a return address for the called procedure (this is very important). There are two calling conventions in the mainframe world; caller saves and callee saves. In LE, the callee saves the registers of the caller, and it does this in the register save area that is in the caller's DSA (third item in the list above). The caller will restore its register when the callee returns control to the caller.
For the sake of materializing this abstract explanation; upon a call-type branch, the address to return to will be saved in register 14.
In x86 calling conventions, the stack pointer grows higher and lower by POPing and PUSHing items to it. On z/Architecture, there is a register that is the stack pointer, but only by convention. No one is forcing anyone to use any register as a stack pointer. LE has chosen register 13 to be the stack pointer. You don't push and pop items to the stack on z/Architecture. On a call, the LE sets the stack pointer (by checking where the next available stack byte is, which was determined at the start of the calling procedure), determines where the next stack frame should start (saved at [2]), and constructs its DSA at the currently set stack pointer. Your stack variables live right after the DSA and are accessed by addressing memory relative to the stack pointer. Typically when you compile a program, the listing will include the offsets of the variables to the beginning of the DSA for each procedure. Notice how this makes it annoying to dynamically allocate stack variables.
When setting up the DSA, a procedure (callee) will keep a pointer of the caller's DSA, in its own (callee's) DSA.
When a procedure wants to return to the procedure that called it, it needs an address to return to. As we discussed earlier, that address was originally saved in register 14 when the branch to the callee happened. The callee then proceeded to save all registers (including register 14) in the savearea set up in the caller's DSA. The callee keeps a pointer to its caller's DSA. So the callee can perfectly fetch the address it needs to return to. Take away from this that the return address lives on the stack, in proximity to stack variables.

How to exploit this?

On x86 architecture, string copies will compile to loops of byte moves. On z/Architecture, we can do this with one single assembler instruction; MVC. MVC takes the target location, a length, and a source location. Considering that strings are defined with a fixed length in mainframe programming languages (including PL/1), the compiler is aware of each string's length at all times. As a result, if you try to assign a 10 byte string in to a 5 byte string, the compiler will do two things. Warn you that you're doing something silly, and compile to an MVC instruction with length parameter 5, copying only the first half of the 10 byte string. As a result, exploiting string copies simply doesn't work, because you can't write exploitable code that way.
As a mainframe assembler programmer, I understood this fairly quickly. So I had to find another attack vector. In my limited research in to software vulnerability exploitation, I learned that a typical sign of having found a buffer overflow is when the application crashes. z/Architecture, and S/360 before it, rely heavily on ABENDs (abnormal ends). When an ABEND happens, it is accompanied by a code. One of the most common ABEND codes is S0C1. A S0C1 happens when the CPU is decoding an instruction, but finds that the OPCODE does not exist. Assuming that the IBM compilers are flawless, this almost always means that the program managed to branch to an area of memory containing data, and not instructions. For the times I was called up in the middle of the night to resolve a S0C1, it's a welcome change that this is exactly what I wanted to happen for once.
So what programmer errors usually cause S0C1 ABENDs? Addressing an array with an index larger than it was allocated with. How does this happen? Same way x86 buffer overflows happen, no size checking.
Take a developer who knows that business rules dictate that no one can have more than 5 checking accounts. In a batch program that lists checking accounts, he has an array in which he loads the current person's 5 checking accounts. He opens a cursor to the database, and starts fetching rows from the resultset, increasing an integer each time he writes a checking account to the array. Sometime, usually around 3:00 AM, the program will treat a person who somehow managed to open 6 checking accounts. The program tries to store the fetched row at an address that it shouldn't, and sooner or later, the application breaks. S0C1.
Now that's still pretty far from an exploit. But; we found that stack variables live close to return addresses on the stack, and we found that we can write to parts of the stack that we're not supposed to write to. Good luck opening a 6th checking account which will meaningfully overlay a return address though, that'd be something.
Now, the LE stack grows upwards (unless forced through options in the LE parameters to do the opposite, which literally no one does, because why?). This is annoying! If we manage to write to an address higher than the highest address of an array, we're just writing in to uninitialized stack space, that's not helping us anything.
There is still an attack vector though; globally declared arrays. When a program gains control (we'll call this level 0), it sets up its DSA as if it's a procedure, and its stack variables go right above it. When a subroutine (level 1) is called from level 0, its DSA lives just above those globally declared stack variables. If we can get a globally declared array to write out of its bounds, it'll eventually write in to level 1's DSA.
Writing in to level 1's DSA when we're executing as level 1 doesn't help us any though. The return address that level 1 returns to lives in the DSA of level 0 (register 14 containing the return address is saved in the caller's DSA remember?), which lives under the globally declared variables. We can attack any level's return address higher than level 0's though. So if we can write out of bounds to a globally declared array from level 2 or higher, we're golden. Note how a problem can arise as soon as more global variables live between the array we want to abuse, and the DSA we want to attack. If those are critical for a procedure to get to the point where it will return to its caller, it won't, because they are destroyed when smashing the stack. Unless we put meaningful stuff in there, but this makes things much more complicated.
There is an exception to this. When you pass a pointer to an array (that lives somewhere between two level's DSAs to a higher level procedure as an argument), you effectively extend the scope of that array to that procedure. This case is incredibly rare in the real world, I'd assume, since most PL/1 programmers I know don't know about procedure arguments, and much less about pointers.

Working Proof of Concept.

So now to put this in practice.
We're going to use a massively simple PL/1 program. Initially, I wanted to read a file line by line until it reached its end of file status, copying each line to a globally declared array, without bounds checking of course. When there are as many lines in the file as there are lines allocated for the array, we're fine. But as soon as we add another, we'd be overwriting the DSA of the level 1 procedure. This doesn't work, I know why (reading a file's line is a call in itself), but not why (the read file call is a call to a closed source procedure, and I'm pretty sure it needs the complete DSA chain to be intact).
So what I do is I read all lines in to an array, then copy the elements of that array one by one in to an array with one element less allocated to it. I cheat twice in this PoC, this is the first. I'm working on a better concept with less cheating, of course.
From here on out it's basically sitting through debugging sessions, learning offsets within the stack by comparing memory contents with listings, knowing the layout of the DSA, and knowing how a procedure gets its return address.
Turns out that in order to get a return address, a procedure will get its stack base + offset 0x04, which contains a pointer to its caller's DSA, and then get that address + offset 0x0C, which is where register 14 (the return address, remember?) was saved during the callee's prologue.
L r13,4(,r13) R13 is stack base, aka our DSA location. Load location of caller's DSA (our DSA + offset 4) in register 13. L r14,12(,r13) R13 is now caller's stack base, aka its DSA location. At offset 12 to it, we find where register 14 was saved. We load this in to register 14. LM r2,r6,28(r13) Restore all of the registers from the savearea. BALR r1,r14 Return to the address in register 14, and store the address right after this instruction in register 1. 
So we need to craft an overlay that will overwrite offset 0x0C in the DSA to change where the program will branch to when trying to return to that DSA's procedure.
In our example, the stack looks something like this right before we write the 6th element to an array with 5 elements;
 0 - 2 - 4 - 6 - 8 - A - C - E - = 0-2-4-6-8-A-C-E- ******************************** TOP OF DATA ********************************** 192A6878 ===> A715000D 00160000 C7C8D6E2 E340C9D5 = x.......GHOST IN 192A6888 ===> 40E97DE2 40E2C8C5 D3D30A23 00000000 = Z'S SHELL...... 192A6898 ===> 00000000 00000000 00000000 00000000 = ................ 192A68A8 ===> 00000000 00000000 00000000 00000000 = ................ 192A68B8 ===> 00000000 00000000 00000000 00000000 = ................ 192A68C8 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A68D8 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A68E8 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A68F8 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A6908 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A6918 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6928 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6938 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6948 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6958 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6968 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A6978 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A6988 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A6998 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A69A8 ===> C2C2C2C2 C2C2C2C2 C2C2C2C2 C2C2C2C2 = BBBBBBBBBBBBBBBB 192A69B8 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A69C8 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A69D8 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A69E8 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A69F8 ===> C1C1C1C1 C1C1C1C1 C1C1C1C1 C1C1C1C1 = AAAAAAAAAAAAAAAA 192A6A08 ===> 187C3150 192A65B8 188DD070 00044040 = [email protected]&......}... 192A6A18 ===> 0020A000 187C35A8 00000000 00000000 = [email protected] 192A6A28 ===> 1929C95C 00048001 00606002 187C35B4 = ..I*[email protected] 192A6A38 ===> 00000000 00000000 10000000 192A65B8 = ................ 192A6A48 ===> 192A9B38 987C357A 187C31B8 00000080 = [email protected]:[email protected] 192A6A58 ===> 987C340E 192A6B18 187C358C 187C3AF4 = [email protected],[email protected]@.4 192A6A68 ===> 192A65B8 187C3A70 187C35A8 18B937F0 = [email protected]@.y...0 192A6A78 ===> 00000008 00000000 192A6230 18B94BD8 = ...............Q 192A6A88 ===> 00000000 192A6B88 18B0C5E0 18B93460 = ......,h..E\...- 192A6A98 ===> 00000000 192A6AC8 192A6AB4 192A62F4 = ......¦H..¦....4 192A6AA8 ===> 192A6AB8 9940A388 192A6C98 000015A0 = ..¦.r th..%q.... 
Address 192A6878 is the location of the first element in the array. It's also our payload (more about that later). Each element is 80 bytes long, so element 2 starts at 192A68C8, etc etc. The 6th element (for which no allocation was made!) will be written to 192A6A08, which contains garbage. Between the end of the stack variables and the beginning of another DSA exists some padding, since the compiler aligns DSAs in a certain way. The DSA that lives right above this array is level 1's DSA. It starts at address 192A6A40, I know this because I studied the offsets within a DSA (IBM documents) and the offsets of the stack variables to the stack frame base, which I get from the output listing when compiling the program (you don't get this in real life). Offset 0x0C to the DSA base is the address that this DSA's procedure has its callees return to.
Our payload will be 80 bytes long, because each element in the array is 80 bytes long. We get to inject our payload at 192A6A08, and the memory location we need to overlay is 192A6A4C. That's at an offset of 68 (decimal), to the beginning of our payload. So our payload looks something like this in EBCDIC (yeah we don't play with ASCII or UTF too well);
 q Hexadecimal it looks like this;
00000000000000000000000000000000000000000000000000000000000000000000926700000000 000000000000000000000000000000000000000000000000000000000000000000009A8800000000 
Notice how I put a valid (31 bit, it looks like 32 bits) address in the location that will be overlaid over the return address in the caller's DSA. It's 992A6878[*], the address of the first element of the array we're attacking. I conveniently injected executable code on to the stack in this way. I could have put it in any element, including the 6th which overlays the return address. I could've also put it in common storage by using another program (z/Architecture memory layout feature), memory that is accessible by every address space.
* The address shown by the debugger is 192A6878, not 992A6878. The latter address has bit 0 flipped to 1, which indicates (in z/Architecture) that the program is running in 31 bit mode, which is the successor of the 24 bit mode before it. The debugger has problems with this, but the program is in fact running in 31 bit mode, and the address of the executable payload should have bit 0 flipped to 1.
This is also the second time I cheat by the way. I can't easily find the address of our payload, it changes from execution to execution (more about this later). So to make it easy for myself, I print the address of the first element of the array to the spool, from the program itself, effectively telling me where I need to force the program to branch to. No program in the real world will do this for you!
So; this payload will make the program branch to our executable payload as soon as any procedure, no matter how many levels deep we are right now, tries to return to level 1.
So what's in our payload?
x GHOST IN Z'S SHELL A1000100CCDEE4CD4E7E4ECCDD020000000000000000000000000000000000000000000000000000 750D06007862309509D2028533A30000000000000000000000000000000000000000000000000000 
It's the machine code (hand)compiled from assembler instructions looking like this;
BRAS 1, Branch relative to the current address, save address after this instruction in register 1. We branch forwards 13 halfwords (0xD), or 26 bytes because our parm list is that long. DC AL2(6) Constant of 2 bytes, contents are integer '6'. Length of parameters. DC B'0000000000000000' Constant of 2 bytes, contents binary zeroes. Parameters for SVC. DC C'GHOST IN Z'S SHELL' Constant of 18 bytes, you know the contents. Message to display. SVC 35 Make supervisor call 35. 
Supervisor call 35 wants register 1 to contain a pointer to where its parameters are in memory. The parameters are in between the branch and the supervisor call. By branching over the parameters, straight to the SVC, and saving the address of the parameter list in register 1 by doing so, we accomplish a lot in just one instruction.
Supervisor call 35 is "Write To Operator", or WTO. It writes a message to the consoles that are configured to display this particular message. It actually displays it so that's fun.
This is arbitrary code being executed from an LE enabled program running under z/OS on a z/Architecture machine.

Random notes and thoughts.

z/Architecture does not feature executable space protection, nor does it have the possibility to mark memory as non-executable. This makes it very easy for us to inject code. As long as we can get it in memory, we can execute it.
z/Architecture does not feature ASLR, explicitly. The address at which the array in our PoC lives changes from time to time though. I've been doing some documentation digging (which is all I can do because IBM is closed source when it comes to code), and found that the stack is allocated at the start of an LE program with a GETMAIN SVC call (standard stuff). This triggers the Virtual Storage Manager to find an empty piece of memory with the size that was requested, with the characteristics requested. Why, and under what circumstances the VSM decides that another virtual page should be used this execution, as opposed to any other execution, I don't know yet. I'm afraid I'll have to reverse engineer parts of the GETMAIN SVC and VSM for that. Or go work for IBM?
z/Architecture features memory protection in the form of protection keys. You can only edit memory that has the same protection key as the address space you're running it from. Protection keys run from 0-16 and are set when the address space is started. This protects userland programs from fucking up system areas that live in common storage. You cannot change the protection key of your address space*. When you allocate (virtual) memory, that memory is marked with the protection key of the address space that requested it.
z/Architecture features certain instructions that are protected. You can only run them when your program is compiled to be APF-authorized, and resides in an APF-authorized (z/OS option) library. Userland programs will never be APF authorized. APF authorization allows you to switch from problem state to supervisor state. Supervisor state allows you to change your protection key.
Since we're probably only going to find vulnerabilities in userland programs, which aren't (I hope in all shops) APF-authorized, we won't be able to completely own the system. But we'll be able to shit on a lot of stuff regardless.
If a vulnerability is found in an APF authorized program, some serious escalation can occur. This was part of the exploit-chain used by Warg to shit on Logica's (Swedish bank) mainframes. An APF authorized program can create a new SVC. Warg introduced a new SVC which allowed any program to escalate its APF authorization (APF authorization is nothing but a bit in protection key 0 storage, which you can write to from an SVC since those run in key 0, supervisor mode). So any program you run, you can call that SVC and switch it to be APF authorized. This allowed him to hold control over the machine for a very long time, since this is seriously hard to find when you're trying to protect the system.
Considering how simple the PoC is, I hope it's clear that in order to be able to exploit a vulnerability, the planets really need to align. The vuln needs to exist, you need to somehow figure out how to craft the payload (hard if you're not on the system, and have access to source code, compile listings, and a debugging session), and get the payload on the system. Then, when you have arbitrary code execution, you need to know how the system is set up to do interesting stuff. Open sockets, create files, edit files, delete files, what have you.
I mentioned earlier that it would be interesting to introduce the arbitrary code that we want to execute in to the common storage area, which is accessible by every address space. Address spaces are limited in what they can do based on authorities granted to the user they are started with by RACF. Authorities are for example; what files they have access to, what commands they can execute.
If you find a vulnerable program, but fail to inject enough code, you can use another program to inject it in to common storage, get a pointer to it, and then use that pointer as a target address for your exploit. Since the code being run from common storage would run in the address space of the vulnerable program, it'd be running under the user that the vulnerable program was started with. This is potential privilege escalation, as you get to do more stuff than you were originally allowed to do. Note that you first need to be able to log on to the system in order to inject anything in to common storage, which under normal circumstances you shouldn't be allowed to.

Conclusion.

So is it possible to "stack smash", or "smash the daisy chain" (as I like to call it) of z/OS LE programs? Yes.
Will you be hard pressed to find a vulnerability? Yes.
Will you be even more hard pressed to somehow be allowed by business rules to inject something that is executable? Yes. My visa account number is sadly not executable machine code.
If you're reading this because one of your managers is flipping out, I'm sorry.

Final note.

I'm really new at this exploiting stuff, and probably missing a lot of easy things that could make the process of finding and abusing vulnerabilities a lot less complicated. If someone with more experience wants to get in to this, I'd be more than happy to help them get started, because I know I'll get my return on investment when that person surpasses me and can teach me in return.
submitted by Bedeone to mainframe [link] [comments]

Adafruit Space Invader pendant. Want to convert to using a bicolor 1.2 led matrix. How would the code change?

 // Trinket/Gemma + LED matrix backpack jewelry. Plays animated // sequence on LED matrix. Press reset button to display again, // or add optional momentary button between pin #1 and +V. // THERE IS NO ANIMATION DATA IN THIS SOURCE FILE, you should // rarely need to change anything here. EDIT anim.h INSTEAD. #define BRIGHTNESS 14 // 0=min, 15=max #define I2C_ADDR 0x70 // Edit if backpack A0/A1 jumpers set #include  #include  #include  #include "anim2.h" // Animation data is located here #include "anim3.h" // Animation data is located here #include "anim4.h" // Animation data is located here static const uint8_t PROGMEM reorder[] = { // Column-reordering table 0x00,0x40,0x20,0x60,0x10,0x50,0x30,0x70,0x08,0x48,0x28,0x68,0x18,0x58,0x38,0x78, 0x04,0x44,0x24,0x64,0x14,0x54,0x34,0x74,0x0c,0x4c,0x2c,0x6c,0x1c,0x5c,0x3c,0x7c, 0x02,0x42,0x22,0x62,0x12,0x52,0x32,0x72,0x0a,0x4a,0x2a,0x6a,0x1a,0x5a,0x3a,0x7a, 0x06,0x46,0x26,0x66,0x16,0x56,0x36,0x76,0x0e,0x4e,0x2e,0x6e,0x1e,0x5e,0x3e,0x7e, 0x01,0x41,0x21,0x61,0x11,0x51,0x31,0x71,0x09,0x49,0x29,0x69,0x19,0x59,0x39,0x79, 0x05,0x45,0x25,0x65,0x15,0x55,0x35,0x75,0x0d,0x4d,0x2d,0x6d,0x1d,0x5d,0x3d,0x7d, 0x03,0x43,0x23,0x63,0x13,0x53,0x33,0x73,0x0b,0x4b,0x2b,0x6b,0x1b,0x5b,0x3b,0x7b, 0x07,0x47,0x27,0x67,0x17,0x57,0x37,0x77,0x0f,0x4f,0x2f,0x6f,0x1f,0x5f,0x3f,0x7f, 0x80,0xc0,0xa0,0xe0,0x90,0xd0,0xb0,0xf0,0x88,0xc8,0xa8,0xe8,0x98,0xd8,0xb8,0xf8, 0x84,0xc4,0xa4,0xe4,0x94,0xd4,0xb4,0xf4,0x8c,0xcc,0xac,0xec,0x9c,0xdc,0xbc,0xfc, 0x82,0xc2,0xa2,0xe2,0x92,0xd2,0xb2,0xf2,0x8a,0xca,0xaa,0xea,0x9a,0xda,0xba,0xfa, 0x86,0xc6,0xa6,0xe6,0x96,0xd6,0xb6,0xf6,0x8e,0xce,0xae,0xee,0x9e,0xde,0xbe,0xfe, 0x81,0xc1,0xa1,0xe1,0x91,0xd1,0xb1,0xf1,0x89,0xc9,0xa9,0xe9,0x99,0xd9,0xb9,0xf9, 0x85,0xc5,0xa5,0xe5,0x95,0xd5,0xb5,0xf5,0x8d,0xcd,0xad,0xed,0x9d,0xdd,0xbd,0xfd, 0x83,0xc3,0xa3,0xe3,0x93,0xd3,0xb3,0xf3,0x8b,0xcb,0xab,0xeb,0x9b,0xdb,0xbb,0xfb, 0x87,0xc7,0xa7,0xe7,0x97,0xd7,0xb7,0xf7,0x8f,0xcf,0xaf,0xef,0x9f,0xdf,0xbf,0xff }; int animationSection = 0; void ledCmd(uint8_t x) { // Issue command to LED backback driver Wire.beginTransmission(I2C_ADDR); Wire.write(x); Wire.endTransmission(); } void clear(void) { // Clear display buffer Wire.beginTransmission(I2C_ADDR); for(uint8_t i=0; i<17; i++) Wire.write(0); Wire.endTransmission(); } void setup() { power_timer1_disable(); // Disable unused peripherals power_adc_disable(); // to save power PCMSK |= _BV(PCINT1); // Set change mask for pin 1 Wire.begin(); // I2C init clear(); // Blank display ledCmd(0x21); // Turn on oscillator ledCmd(0xE0 | BRIGHTNESS); // Set brightness ledCmd(0x81); // Display on, no blink } uint8_t rep = REPS; void loop() { switch (animationSection) { case 0: for(int i=0; i 10) { animationSection = 0; } if(!--rep) { // If last cycle... ledCmd(0x20); // LED matrix in standby mode // GIMSK = _BV(PCIE); // Enable pin change interrupt // power_all_disable(); // All peripherals off // set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep_enable(); // sei(); // Keep interrupts disabled // sleep_mode(); // Power down CPU (pin 1 will wake) // Execution resumes here on wake. // PLD - Simply Sleep for 2 minutes then start again... //delay(100000); //delay(100000); delay(120000); animationSection = 0; GIMSK = 0; // Disable pin change interrupt rep = REPS; // Reset animation counter power_timer0_enable(); // Re-enable timer power_usi_enable(); // Re-enable USI Wire.begin(); // Re-init I2C clear(); // Blank display ledCmd(0x21); // Re-enable matrix } } ISR(PCINT0_vect) {} // Button tap 
This is a section of the anim file. I want to be able to set the various colors in these "frames"
// Animation data for Trinket/Gemma + LED matrix backpack jewelry. // Edit this file to change the animation; it's unlikely you'll need // to edit the source code. #define REPS 10 // Number of times to repeat the animation loop (1-255) const int frameSpeed2 = 3; const uint8_t PROGMEM anim2[] = { // Animation bitmaps. Each frame of animation MUST contain // 8 lines of graphics data (there is no error checking for // length). Each line should be prefixed with the letter 'B', // followed by exactly 8 binary digits (0 or 1), no more, // no less (again, no error checking). '0' represents an // 'off' pixel, '1' an 'on' pixel. End line with a comma. B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, B00000000, frameSpeed2, // 0.10 seconds }; 
submitted by pldiguanaman to arduino [link] [comments]

Assembly language and machine code - Gary explains!

Category: Binary Exploitation x64 ASM Fundamentals 0x04 – PEMDAS and such Introduction This post is going to cover some basic mathematical operations within intel’s 64 bit Assembly language. Binary System. The binary numeral system uses the number 2 as its base (radix). As a base-2 numeral system, it consists of only two numbers: 0 and 1. While it has been applied in ancient Egypt, China and India for different purposes, the binary system has become the language of electronics and computers in the modern world. Even if this option is not set, indexes related to primary keys and unique constraints are generated if they are already defined on a published table.'), (0x20, 'Converts user-defined data types (UDT) to base data types at the Subscriber. This option cannot be used when there is a CHECK or DEFAULT constraint on a UDT column, if a UDT column is Hi! I wrote a couple of modules at Access 97. The default statement is Option Compare Database. The other options are Option Compare Binary and Option Compare Text. I understand these two. But I seem not quite clear about Option Compare Database. On Access 97 help, it says that Option Compare Database can only be used within Microsoft Access. Length field is used either the short form or the long form as a option depend on Data field. The short form, Length field is a single octet in which bit 8 is zero and bits 7 to 1 encode the number of bytes in Data field, as an unsigned binary integer with bit 7 as the most significant bit.

[index] [8757] [2338] [11898] [21183] [20208] [3958] [19473] [19652] [22837] [19490]

Assembly language and machine code - Gary explains!

Read more: http://goo.gl/tgJqpw -- Ask Gary your questions on the AA forums: http://goo.gl/V3L5ZA You might have heard the terms "assembly language" and "ma...

Flag Counter